Skip to content

Commit

Permalink
🚧 inline the display order calculation function
Browse files Browse the repository at this point in the history
  • Loading branch information
victorlin committed Oct 17, 2024
1 parent d66ee7b commit 39d8716
Showing 1 changed file with 33 additions and 53 deletions.
86 changes: 33 additions & 53 deletions src/components/tree/phyloTree/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,23 @@ export const applyToChildren = (phyloNode, func) => {
* of nodes in a rectangular tree.
* If `yCounter` is undefined then we wish to hide the node and all descendants of it
* @param {PhyloNode} node
* @param {function} getDisplayOrder
* @param {function} incrementer
* @param {int|undefined} yCounter
* @sideeffect modifies node.displayOrder and node.displayOrderRange
* @returns {int|undefined} current yCounter after assignment to the tree originating from `node`
*/
export const setDisplayOrderRecursively = (node, getDisplayOrder, yCounter) => {
export const setDisplayOrderRecursively = (node, incrementer, yCounter) => {
const children = node.n.children; // (redux) tree node
if (children && children.length) {
for (let i = children.length - 1; i >= 0; i--) {
yCounter = setDisplayOrderRecursively(children[i].shell, getDisplayOrder, yCounter);
yCounter = setDisplayOrderRecursively(children[i].shell, incrementer, yCounter);
}
} else {
node.displayOrder = (node.n.fullTipCount===0 || yCounter===undefined) ? yCounter : yCounter + getDisplayOrder(node);
node.displayOrderRange = [node.displayOrder, node.displayOrder];
if (node.n.fullTipCount !== 0 && yCounter !== undefined) {
yCounter += incrementer(node);
}
node.displayOrder = yCounter;
node.displayOrderRange = [yCounter, yCounter];
return yCounter;
}
/* if here, then all children have displayOrders, but we don't. */
Expand Down Expand Up @@ -88,17 +91,39 @@ function _getSpaceBetweenSubtrees(numSubtrees, numTips) {
export const setDisplayOrder = ({nodes, focus}) => {
timerStart("setDisplayOrder");

const getDisplayOrder = getDisplayOrderCallback(nodes, focus);
const numSubtrees = nodes[0].n.children.filter((n) => n.fullTipCount!==0).length;
const numTips = nodes[0].n.fullTipCount;
const spaceBetweenSubtrees = _getSpaceBetweenSubtrees(numSubtrees, numTips);

// No focus: 1 unit per node
let incrementer = (_node) => 1;

if (focus) {
const numVisible = nodes[0].n.tipCount;
const yProportionFocused = Math.max(0.8, numVisible / nodes.length);
const yPerFocused = (yProportionFocused * nodes.length) / numVisible;
const yPerUnfocused = ((1 - yProportionFocused) * nodes.length) / (nodes.length - numVisible);
incrementer = (() => {
let previousWasVisible = false;
return (node) => {
// Focus if the current node is visible or if the previous node was visible (for symmetric padding)
const y = (node.visibility === NODE_VISIBLE || previousWasVisible) ? yPerFocused : yPerUnfocused;

// Update for the next node
previousWasVisible = node.visibility === NODE_VISIBLE;

return y;
}
})();
}

let yCounter = 0;
/* iterate through each subtree, and add padding between each */
for (const subtree of nodes[0].n.children) {
if (subtree.fullTipCount===0) { // don't use screen space for this subtree
setDisplayOrderRecursively(nodes[subtree.arrayIdx], getDisplayOrder, undefined);
setDisplayOrderRecursively(nodes[subtree.arrayIdx], incrementer, undefined);
} else {
yCounter = setDisplayOrderRecursively(nodes[subtree.arrayIdx], getDisplayOrder, yCounter);
yCounter = setDisplayOrderRecursively(nodes[subtree.arrayIdx], incrementer, yCounter);
yCounter+=spaceBetweenSubtrees;
}
}
Expand All @@ -109,51 +134,6 @@ export const setDisplayOrder = ({nodes, focus}) => {
timerEnd("setDisplayOrder");
};

/**
* @param {Array<PhyloNode>} nodes
* @param {boolean} focus
* @returns fn to return a display order (y position) for a node
*/
function getDisplayOrderCallback(nodes, focus) {
/**
* Start at 0 and increase with each node.
* Note that this value is shared across invocations of the callback.
*/
let displayOrder = 0;

/**
* Keep track of whether the previous node was selected
*/
let previousWasVisible;

if (focus) {
const numVisible = nodes[0].n.tipCount;
const yProportionFocused = Math.max(0.8, numVisible / nodes.length);
const yProportionUnfocused = 1 - yProportionFocused;
const yPerFocused = (yProportionFocused * nodes.length) / numVisible;
const yPerUnfocused = (yProportionUnfocused * nodes.length) / (nodes.length - numVisible);

return (node) => {
// Focus if the current node is visible or if the previous node was visible (for symmetric padding)
if (node.visibility === NODE_VISIBLE || previousWasVisible) {
displayOrder += yPerFocused;
} else {
displayOrder += yPerUnfocused;
}

// Update for the next node
previousWasVisible = node.visibility === NODE_VISIBLE;

return displayOrder;
};
} else {
// No focus: 1 unit per node
return (_node) => {
displayOrder += 1;
return displayOrder;
};
}
}

export const formatDivergence = (divergence) => {
return divergence > 1 ?
Expand Down

0 comments on commit 39d8716

Please sign in to comment.