diff --git a/css/interaction/familyTree.css b/css/interaction/familyTree.css index a1e15e6e7a16db5075375bec799d2b83ecad7a12..021f42c4c310ab34ea5c11e2978b07de4d526925 100644 --- a/css/interaction/familyTree.css +++ b/css/interaction/familyTree.css @@ -1,73 +1,5 @@ - -#graph .linage { - fill: none; - stroke: white; -} -#graph .marriage { - fill: none; - stroke: white; -} -#graph .node { - background-color: lightblue; - border-style: solid; - border-width: 1px; -} -#graph .nodeText{ - font: 10px sans-serif; - margin: 0; - padding: 0; - color: black; -} -#graph { - font: 10px sans-serif; - margin: -20px 0; - padding: 0; - color: black; -} - -#graph div { - border-style: solid; - border-width: 1px; -} - -#graph .XY { - background-color: lightblue; -} - -#graph div.XX { - background-color: pink; -} - -#graph div.unknown { - background-color: gray; -} - -#graph div.unknownXY { - background-color: #808080; -} - -#graph div.unknownXX { - background-color: #808080; -} - -#graph .you { - background-color: red; -} - -#graph .emphasis { - font-style: italic; - font-weight: bold; - margin: 0; - background: #ffffff88; - border: 2px solid Gold; - white-space: nowrap; -} - #edit-family { display: flex; flex-wrap: wrap; align-items: center; } - -#edit-family #family-table { -} diff --git a/src/gui/multipleInspect.js b/src/gui/multipleInspect.js index 02d20945987c3a60f7f6ffcc619b486d33612ffb..5b83bc81e2557bf53b27b8d4f04011609cd3892f 100644 --- a/src/gui/multipleInspect.js +++ b/src/gui/multipleInspect.js @@ -18,13 +18,8 @@ App.UI.MultipleInspect = (function() { if (slaves.length > 1 && showFamilyTree) { const button = App.UI.tabBar.tabButton(`familyTreeTab`, "Family Tree"); - button.addEventListener('click', () => { - renderFamilyTree(slaves, slaves[0].ID); - }); tabBar.append(button); - const ftTarget = document.createElement("div"); - ftTarget.setAttribute("id", "family-tree"); - frag.append(App.UI.tabBar.makeTab(`familyTreeTab`, ftTarget)); + frag.append(App.UI.tabBar.makeTab(`familyTreeTab`, renderFamilyTree(slaves, slaves[0].ID))); } App.UI.tabBar.handlePreSelectedTab(`slave${slaves[0].ID}`); diff --git a/src/interaction/siFamily.js b/src/interaction/siFamily.js index 3f176773debfe05029be0c5df9252bba74f62b18..3c7c308160b92c8d1061c7a09abffe26a773a97b 100644 --- a/src/interaction/siFamily.js +++ b/src/interaction/siFamily.js @@ -1,18 +1,11 @@ /** + * @param {App.Entity.SlaveState} slave * @returns {HTMLParagraphElement} */ -App.UI.SlaveInteract.family = function() { - let element; +App.UI.SlaveInteract.family = function(slave) { const p = document.createElement("p"); p.id = "family"; - - element = document.createElement("div"); - element.id = "family-tree"; - p.append(element); - - element = document.createElement("span"); - element.id = "family-tree-link"; - p.append(element); + p.append(renderFamilyTree(V.slaves, slave.ID)); return p; }; diff --git a/src/interaction/slaveInteract.js b/src/interaction/slaveInteract.js index 2dedd1bb788bced00b535423ba63d5d699f941b4..5bd0ca517930c98c794935ca12681552a5b68d75 100644 --- a/src/interaction/slaveInteract.js +++ b/src/interaction/slaveInteract.js @@ -119,10 +119,7 @@ App.UI.SlaveInteract.mainPage = function(slave) { { title: "Family", id: "family-tab", - onClick: () => { - renderFamilyTree(V.slaves, slave.ID); - }, - get node() { return App.UI.SlaveInteract.family(); } + get node() { return App.UI.SlaveInteract.family(slave); } } ]; diff --git a/src/js/familyTreeJS.js b/src/js/familyTreeJS.js index c7c12b21b52bf191f3354e70ec581933c972f8ec..317f5a61bd07644c39c21932d15fb7a2673ba590 100644 --- a/src/js/familyTreeJS.js +++ b/src/js/familyTreeJS.js @@ -1,53 +1,30 @@ /* eslint-disable camelcase */ /* eslint-disable no-console */ -let lastActiveSlave, lastSlaves, lastPC; +/** + * @param {FC.HumanState[]} slaves + * @param {number} filterID + * @returns {Element} + */ globalThis.renderFamilyTree = function(slaves, filterID) { 'use strict'; - let ftreeWidth, ftreeHeight; - let chartWidth, chartHeight; - let margin; - d3.select('#ftree-canvas').remove(); - let svg = d3.select('#family-tree') - .append('svg') - .attr('id', 'ftree-canvas'); - let chartLayer = svg.append('g').classed('chartLayer', true); + const svgNode = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + const svg = d3.select(svgNode); - let data = buildFamilyTree(slaves, filterID); + const data = buildFamilyTree(slaves, filterID); - initFtreeSVG(data); - runFtreeSim(data); - - function initFtreeSVG(data) { - ftreeWidth = data.nodes.length * 45; - ftreeHeight = data.nodes.length * 35; - if (ftreeWidth < 600) { - ftreeWidth = 600; - } else if (ftreeWidth > 1920) { - ftreeWidth = 1920; - } - if (ftreeHeight < 480) { - ftreeHeight = 480; - } else if (ftreeHeight > 1200) { - ftreeHeight = 1200; - } - - margin = { - top: 0, - left: 0, - bottom: 0, - right: 0 - }; + const ftreeWidth = Math.clamp(data.nodes.length * 45, 600, 1920); + const ftreeHeight = Math.clamp(data.nodes.length * 35, 480, 1200); + initFtreeSVG(); + runFtreeSim(data); - chartWidth = ftreeWidth - (margin.left + margin.right); - chartHeight = ftreeHeight - (margin.top + margin.bottom); + return svgNode; + function initFtreeSVG() { svg.attr('width', ftreeWidth).attr('height', ftreeHeight); - svg.append('defs'); - svg.append('defs').append('marker') .attr('id', 'arrowhead') .attr('viewBox', '-0 -5 10 10') @@ -61,24 +38,19 @@ globalThis.renderFamilyTree = function(slaves, filterID) { .attr('d', 'M 0,-1 L 5,0 L 0,1') .attr('fill', '#a1a1a1') .style('stroke', 'none'); - - chartLayer - .attr('width', chartWidth) - .attr('height', chartHeight) - .attr('transform', `translate(${[margin.left, margin.top]})`); } function runFtreeSim(data) { let simulation = d3.forceSimulation() .force('link', d3.forceLink().id(function(d) { - return d.index; + return d.index.toString(); })) // eslint-disable-next-line no-unused-vars .force('collide', d3.forceCollide(function(d) { return 60; }).iterations(4)) .force('charge', d3.forceManyBody().strength(-200).distanceMin(100).distanceMax(1000)) - .force('center', d3.forceCenter(chartWidth / 2, chartHeight / 2)) + .force('center', d3.forceCenter(ftreeWidth / 2, ftreeHeight / 2)) .force('y', d3.forceY(100)) .force('x', d3.forceX(200)); @@ -194,6 +166,10 @@ globalThis.renderFamilyTree = function(slaves, filterID) { } }; +/** + * @param {FC.HumanState[]} slaves + * @param {number} filterID + */ globalThis.buildFamilyTree = function(slaves, filterID) { let family_graph = {nodes: [], links: []}; let node_lookup = {}; @@ -215,11 +191,10 @@ globalThis.buildFamilyTree = function(slaves, filterID) { father: V.PC.father, dick: V.PC.dick, vagina: V.PC.vagina, + /** @type {number} */ ID: V.PC.ID }; - let charList = [fake_pc]; - charList.push.apply(charList, slaves); - charList.push.apply(charList, V.incubator.tanks); + let charList = [fake_pc].concat(slaves).concat(V.incubator.tanks); let unborn = {}; for (const child of V.incubator.tanks) { @@ -229,9 +204,9 @@ globalThis.buildFamilyTree = function(slaves, filterID) { unborn[child.ID] = true; } - for (let i = 0; i < charList.length; i++) { - let mom = charList[i].mother; - let dad = charList[i].father; + for (const character of charList) { + let mom = character.mother; + let dad = character.father; if (mom) { if (!kids[mom]) { @@ -247,8 +222,7 @@ globalThis.buildFamilyTree = function(slaves, filterID) { } } - for (let i = 0; i < charList.length; i++) { - let character = charList[i]; + for (const character of charList) { if (character.mother === 0 && character.father === 0 && !kids[character.ID]) { continue; } @@ -323,9 +297,8 @@ globalThis.buildFamilyTree = function(slaves, filterID) { } } let mkeys = Object.keys(outmoms); - for (let i = 0; i < mkeys.length; i++) { + for (const key of mkeys) { let name; - let key = mkeys[i]; let names = outmoms[key]; if (names.length === 1) { name = names[0]; @@ -349,9 +322,8 @@ globalThis.buildFamilyTree = function(slaves, filterID) { } let dkeys = Object.keys(outdads); - for (let i = 0; i < dkeys.length; i++) { + for (const key of dkeys) { let name; - let key = dkeys[i]; let names = outdads[key]; if (names.length === 1) { name = names[0]; @@ -375,8 +347,8 @@ globalThis.buildFamilyTree = function(slaves, filterID) { } let charHash = {}; - for (let i = 0; i < charList.length; i++) { - charHash[charList[i].ID] = charList[i]; + for (const character of charList) { + charHash[character.ID] = character; } let related = {}; @@ -414,27 +386,24 @@ globalThis.buildFamilyTree = function(slaves, filterID) { for (let k in relIDs.tree) { related[k] = true; } - for (let i = 0; i < charList.length; i++) { - if (charHash[charList[i].ID]) { - let pRelIDs = relatedTo(charHash[charList[i].ID], filterID); - if (pRelIDs.related) { - for (let k in pRelIDs.tree) { - related[k] = true; - if (saveTree[k]) { - for (let k2 in saveTree[k].tree) { - related[k2] = true; - } + for (const character of charList) { + let pRelIDs = relatedTo(character, filterID); + if (pRelIDs.related) { + for (let k in pRelIDs.tree) { + related[k] = true; + if (saveTree[k]) { + for (let k2 in saveTree[k].tree) { + related[k2] = true; } } } - saveTree[charList[i].ID] = pRelIDs; } + saveTree[character.ID] = pRelIDs; } } } - for (let i = 0; i < charList.length; i++) { - let character = charList[i]; + for (const character of charList) { let char_id = character.ID; if (char_id !== filterID) { if (character.mother === 0 && character.father === 0 && !kids[char_id]) { @@ -462,8 +431,7 @@ globalThis.buildFamilyTree = function(slaves, filterID) { family_graph.nodes.push(char_obj); } - for (let i = 0; i < charList.length; i++) { - let character = charList[i]; + for (const character of charList) { let char_id = character.ID; if (character.mother === 0 && character.father === 0 && !kids[char_id]) { continue; @@ -478,12 +446,7 @@ globalThis.buildFamilyTree = function(slaves, filterID) { continue; } if (typeof node_lookup[character.mother] !== 'undefined') { - let ltype; - if (character.mother === character.father) { - ltype = 'homologous'; - } else { - ltype = 'maternal'; - } + const ltype = (character.mother === character.father) ? 'homologous' : 'maternal'; family_graph.links.push({ type: ltype, target: node_lookup[char_id] * 1, @@ -499,170 +462,3 @@ globalThis.buildFamilyTree = function(slaves, filterID) { } return family_graph; }; - -globalThis.updateFamilyTree = function(activeSlave = lastActiveSlave, slaves = lastSlaves, PC = lastPC) { - lastActiveSlave = activeSlave; - lastSlaves = slaves; - lastPC = PC; - let treeDepth = 0; - let numTreeNodes = 0; - - let graphElement = document.getElementById("graph"); - if (!graphElement) { - return; - } - graphElement.innerHTML = ""; - - /* The way this code works is that we start with the activeSlave then we call - slaveInfo() recursively to work our way up the tree finding their parents. - - */ - - function getSlave(id, expectedGenes) { - if (id === -1) { - return { - "slaveName": "YOU", - "ID": id, - "physicalAge": PC.physicalAge, - "genes": PC.genes, - "father": PC.father, - "mother": PC.mother - }; - } - if (id === 0) { - return {"slaveName": "-", "ID": id, "genes": expectedGenes}; - } - if (id === activeSlave.ID) { - return activeSlave; - } - for (let i = 0; i < slaves.length; ++i) { - if (slaves[i].ID === id) { - return slaves[i]; - } - } - return { - "slaveName": "-", - "ID": id, - "genes": expectedGenes - }; - } - - function slaveInfo(slave, activeSlaveId, recursionProtectSlaveId = {}) { - numTreeNodes = 0; - treeDepth = 0; - if (recursionProtectSlaveId[slave.ID]) { - console.log("Recursion protection"); - return slaveInfo_(slave, activeSlaveId); - } - recursionProtectSlaveId[slave.ID] = true; - - if (typeof slave.father === "undefined" || typeof slave.mother === "undefined") { - return slaveInfo_(slave, activeSlaveId); - } - - if (slave.father === -1 || slave.mother === -1) { - return slaveInfo(getSlave(-1), activeSlaveId, recursionProtectSlaveId); - } - if (slave.father !== 0) { - return slaveInfo(getSlave(slave.father, "unknownXY"), activeSlaveId, recursionProtectSlaveId); - } - - if (slave.mother !== 0) { - return slaveInfo(getSlave(slave.mother, "unknownXX"), activeSlaveId, recursionProtectSlaveId); - } - return slaveInfo_(slave, activeSlaveId); - } - - function slaveInfo_(slave, activeSlaveId, slavesAdded = {}, depth = 1) { - numTreeNodes += 1; - treeDepth = Math.max(treeDepth, depth); - let shouldAddChildren = false; - if (!slavesAdded[slave.ID]) { - shouldAddChildren = true; - slavesAdded[slave.ID] = true; - } - let data = { - "name": slave.slaveName + (slave.physicalAge ? (` (${slave.physicalAge})`) : ""), - "class": slave.genes, - "textClass": (activeSlaveId === slave.ID) ? "emphasis" : "", - "marriages": [], - }; - - let spouseToChild = {}; - - function maybeAddSpouseToChild(child) { - if (child.ID === slave.ID) { - return; - } - if (child.father === slave.ID) { - if (!spouseToChild[child.mother]) { - spouseToChild[child.mother] = []; - } - spouseToChild[child.mother].push(child); - } else if (child.mother === slave.ID) { - if (!spouseToChild[child.father]) { - spouseToChild[child.father] = []; - } - spouseToChild[child.father].push(child); - } - } - - if (activeSlave.ID !== PC.ID) { - maybeAddSpouseToChild(activeSlave); - } - maybeAddSpouseToChild(getSlave(-1)); - - for (let i = 0; i < slaves.length; ++i) { - let child = slaves[i]; - if (child.ID !== activeSlave.ID) { - maybeAddSpouseToChild(child); - } - } - - for (let key in spouseToChild) { - if (spouseToChild.hasOwnProperty(key)) { - let children = shouldAddChildren ? spouseToChild[key] : []; - let spouse = getSlave(key, (slaves.genes === "XX") ? "unknownXY" : (slaves.genes === "XY") ? "unknownXX" : "unknown"); - let spouseName; - if (spouse.ID !== slave.ID) { - spouseName = spouse.slaveName + (spouse.physicalAge ? (` (${spouse.physicalAge})`) : ""); - } else { - spouseName = (spouse.ID === -1) ? "(yourself)" : "(themselves)"; - } - let marriage = { - "spouse": {"name": spouseName, "class": spouse.genes}, - "children": children.map(function(x) { return slaveInfo_(x, activeSlaveId, slavesAdded, depth + 1); }) - }; - data.marriages.push(marriage); - } - } - return data; - } - - if (activeSlave === PC || activeSlave === null) { - activeSlave = getSlave(-1); - } - const treeData = [slaveInfo(activeSlave, activeSlave.ID)]; - console.log("Family tree is", treeData, 'and has:', numTreeNodes); - - let parentWidth = document.getElementById('edit-family').offsetWidth; - - console.log(parentWidth, document.getElementById('passages').offsetWidth); - if (!parentWidth) { - parentWidth = document.body.offsetWidth - 483; - } - - console.log(parentWidth, Math.min(200 + 40 * numTreeNodes, parentWidth - 200) + 200); - - dTree.init(treeData, { - target: "#graph", - debug: true, - height: 50 + 50 * treeDepth, - /* very rough heuristics */ - width: Math.min(200 + 40 * numTreeNodes, - parentWidth - 200) + 200, - callbacks: { - nodeClick: function( /* name, extra*/ ) {} - } - }); -}; diff --git a/src/npc/children/childInteract.tw b/src/npc/children/childInteract.tw index 42fe6fada60f492f28901c28f8e49f300daa7af2..53e4446fead827533c97ed6544c7f1dd7c9c3049 100644 --- a/src/npc/children/childInteract.tw +++ b/src/npc/children/childInteract.tw @@ -426,11 +426,11 @@ FIXME: <br><br> <span id="family"> - <div id="family-tree"></div> <span id="family-tree-link"> <<link "Pull up the file on $his family tree.">> <<replace #family-tree-link>> - /* TODO: this may need to be updated */<<run renderFamilyTree($slaves, $activeChild.ID)>><</replace>> + /* TODO: this may need to be updated */<<includeDOM renderFamilyTree($slaves, $activeChild.ID)>> + <</replace>> <</link>> </span> </span> diff --git a/src/npc/startingGirls/editFamily.js b/src/npc/startingGirls/editFamily.js index 586423a228d30f2a8590c5a71f86c2aa7888033f..540c4185d1492899a4828a7ad900dd42dc1beead 100644 --- a/src/npc/startingGirls/editFamily.js +++ b/src/npc/startingGirls/editFamily.js @@ -5,7 +5,7 @@ */ App.Intro.editFamily = function(slave, cheat) { const el = new DocumentFragment(); - const _allowPCFamily = (V.freshPC === 1 || V.saveImported === 0); + const allowPCFamily = (V.freshPC === 1 || V.saveImported === 0); const {His, his} = getPronouns(slave); // Checks to make sure a slave is not the active slave's parent. @@ -14,9 +14,9 @@ App.Intro.editFamily = function(slave, cheat) { const editFamily = makeElWithID("edit-family"); editFamily.append(makeFamilyTable()); - editFamily.append(makeElWithID("family-tree")); + const familyTree = App.UI.DOM.makeElement("div", App.StartingGirls.uncommittedFamilyTree(slave)); + editFamily.append(familyTree); el.append(editFamily); - setTimeout(() => App.StartingGirls.uncommittedFamilyTree(slave), 0); return el; @@ -37,7 +37,7 @@ App.Intro.editFamily = function(slave, cheat) { familyTable.append(sameFatherAs()); familyTable.append(motherOfTheChildren()); familyTable.append(fatherOfTheChildren()); - if (_allowPCFamily) { + if (allowPCFamily) { familyTable.append(resetAllRelativesOfPC()); } return familyTable; @@ -193,7 +193,7 @@ App.Intro.editFamily = function(slave, cheat) { ) ); - if (V.PC.vagina > 0 && isNotMom(V.PC) && ((V.PC.actualAge - slave.actualAge) >= V.fertilityAge) && _allowPCFamily) { + if (V.PC.vagina > 0 && isNotMom(V.PC) && ((V.PC.actualAge - slave.actualAge) >= V.fertilityAge) && allowPCFamily) { linkArray.push( App.UI.DOM.link( "You", @@ -241,7 +241,7 @@ App.Intro.editFamily = function(slave, cheat) { ) ); - if (V.PC.dick > 0 && isNotDad(V.PC) && ((V.PC.actualAge - slave.actualAge) >= V.potencyAge) && _allowPCFamily) { + if (V.PC.dick > 0 && isNotDad(V.PC) && ((V.PC.actualAge - slave.actualAge) >= V.potencyAge) && allowPCFamily) { linkArray.push( App.UI.DOM.link( "You", @@ -295,7 +295,7 @@ App.Intro.editFamily = function(slave, cheat) { ) ); - if ((slave.mother !== V.PC.ID) && (V.PC.mother !== slave.ID) && _allowPCFamily) { + if ((slave.mother !== V.PC.ID) && (V.PC.mother !== slave.ID) && allowPCFamily) { linkArray.push( App.UI.DOM.link( "You", @@ -363,7 +363,7 @@ App.Intro.editFamily = function(slave, cheat) { ) ); - if ((slave.father !== V.PC.ID) && (V.PC.father !== slave.ID) && _allowPCFamily) { + if ((slave.father !== V.PC.ID) && (V.PC.father !== slave.ID) && allowPCFamily) { linkArray.push( App.UI.DOM.link( "You", @@ -423,7 +423,7 @@ App.Intro.editFamily = function(slave, cheat) { s.mother = 0; } } - if (V.PC.mother === slave.ID && _allowPCFamily) { + if (V.PC.mother === slave.ID && allowPCFamily) { V.PC.mother = 0; } refresh(); @@ -432,7 +432,7 @@ App.Intro.editFamily = function(slave, cheat) { ); if (slave.vagina >= 0) { - if (isNotMom(V.PC) && (slave.actualAge - V.PC.actualAge) >= V.fertilityAge && _allowPCFamily) { + if (isNotMom(V.PC) && (slave.actualAge - V.PC.actualAge) >= V.fertilityAge && allowPCFamily) { linkArray.push( App.UI.DOM.link( "You", @@ -498,7 +498,7 @@ App.Intro.editFamily = function(slave, cheat) { s.father = 0; } } - if (V.PC.father === slave.ID && _allowPCFamily) { + if (V.PC.father === slave.ID && allowPCFamily) { V.PC.father = 0; } refresh(); @@ -507,7 +507,7 @@ App.Intro.editFamily = function(slave, cheat) { ); if (slave.dick > 0) { - if (isNotDad(V.PC) && (slave.actualAge - V.PC.actualAge) >= V.potencyAge && _allowPCFamily) { + if (isNotDad(V.PC) && (slave.actualAge - V.PC.actualAge) >= V.potencyAge && allowPCFamily) { linkArray.push( App.UI.DOM.link( "You", @@ -630,6 +630,6 @@ App.Intro.editFamily = function(slave, cheat) { function refresh() { jQuery('#family-table').replaceWith(makeFamilyTable); jQuery('#dont-be-dumb').empty().append(App.UI.DOM.makeElement("div", "You will break things by making impossible relations such as being your own father. If you do this, clearing all PC relations will fix it. Probably.", "note")); - App.StartingGirls.uncommittedFamilyTree(slave); + jQuery(familyTree).empty().append(App.StartingGirls.uncommittedFamilyTree(slave)); } }; diff --git a/src/npc/startingGirls/startingGirls.js b/src/npc/startingGirls/startingGirls.js index 48815f6af59a8c1f7a892c91375a4592a4c34235..7b3f016f140303c4b1faf95c5160623ac7235c52 100644 --- a/src/npc/startingGirls/startingGirls.js +++ b/src/npc/startingGirls/startingGirls.js @@ -289,10 +289,11 @@ App.StartingGirls.listOfSlavesWithParent = function(parent, id) { /** Render the family tree with an uncommitted slave * @param {App.Entity.SlaveState} slave + * @returns {Element} */ App.StartingGirls.uncommittedFamilyTree = function(slave) { let tSlaves = V.slaves.filter(s => s.ID !== slave.ID); // exclude the unedited copy of this slave, if it exists - renderFamilyTree(tSlaves.concat([slave]), slave.ID); + return renderFamilyTree(tSlaves.concat([slave]), slave.ID); }; /** Get the automatic origin that would be used for a slave, without actually changing the slave diff --git a/src/pregmod/managePersonalAffairs.tw b/src/pregmod/managePersonalAffairs.tw index fb27d0d6a64b42d2849a66064f2f1098474d4c3f..b3004435d355e7e6301b9fb533dbcda13cf883a7 100644 --- a/src/pregmod/managePersonalAffairs.tw +++ b/src/pregmod/managePersonalAffairs.tw @@ -392,12 +392,10 @@ <h3>Family</h3> <p> <span id="family"> - <div id="family-tree"> - </div> <span id="family-tree-link"> <<link "Pull up the file on your family tree.">> <<replace #family-tree-link>> - <<run renderFamilyTree($slaves, -1)>> + <<includeDOM renderFamilyTree($slaves, -1)>> <</replace>> <</link>> </span>