@gen-epix/ui-phylogenetic-tree 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ import { i as e, n as t, r as n, t as r } from "../../_chunks/identity.js";
2
+ import { StringUtil as i } from "@gen-epix/ui-core/utils/StringUtil";
3
+ //#region ../../node_modules/.pnpm/lodash@4.18.1/node_modules/lodash/_baseSum.js
4
+ var a = /* @__PURE__ */ n(((e, t) => {
5
+ function n(e, t) {
6
+ for (var n, r = -1, i = e.length; ++r < i;) {
7
+ var a = t(e[r]);
8
+ a !== void 0 && (n = n === void 0 ? a : n + a);
9
+ }
10
+ return n;
11
+ }
12
+ t.exports = n;
13
+ })), o = /* @__PURE__ */ e((/* @__PURE__ */ n(((e, t) => {
14
+ var n = a(), i = r();
15
+ function o(e) {
16
+ return e && e.length ? n(e, i) : 0;
17
+ }
18
+ t.exports = o;
19
+ })))(), 1), s = class e {
20
+ static getSortedNames(t) {
21
+ return [...(t.children ?? []).map((t) => e.getSortedNames(t)).flat(), ...t.children ? [] : [t.name]];
22
+ }
23
+ static parse(e) {
24
+ let n = [], r = {}, a = e.split(/(;|\(|\)|,|:)/).map((e) => e.trim()).filter((e) => e);
25
+ for (let e = 0; e < a.length; e++) {
26
+ let s = a[e], c = {}, l = a[e - 1];
27
+ switch (s) {
28
+ case ",":
29
+ n[n.length - 1].children.push(c), r = c;
30
+ break;
31
+ case ";": break;
32
+ case ":": break;
33
+ case "(":
34
+ r.children = [c], n.push(r), r = c;
35
+ break;
36
+ case ")":
37
+ r = n.pop(), r.children.forEach((e) => {
38
+ e.children || (e.size = 1, e.maxBranchLength = new t(e.branchLength ?? 0), e.subTreeLeaveNames = [e.name], e.subTreeNames = e.subTreeNames ?? []);
39
+ }), r.size = (0, o.default)(r.children.map((e) => e.size)), r.subTreeNames = r.children.map((e) => e.subTreeNames).flat().filter((e) => e), r.subTreeLeaveNames = r.children.map((e) => e.subTreeLeaveNames).flat().filter((e) => e), r.maxBranchLength = t.max(...r.children.map((e) => e.maxBranchLength));
40
+ break;
41
+ default: l === ")" || l === "(" || l === "," ? r.name = s : l === ":" && (r.branchLength = new t(s), r.subTreeNames = (r.children ?? []).map((e) => [e.name, ...e.subTreeNames]).flat().filter((e) => e), r.subTreeLeaveNames = (r.children ?? []).map((e) => e.subTreeLeaveNames).flat().filter((e) => e), r.maxBranchLength = r.branchLength.add(r.children?.length ? t.max(...r.children.map((e) => e.maxBranchLength)) : 0), r.name = r.name || `Generated-${i.createHash(r.subTreeNames.join(","))}`);
42
+ }
43
+ }
44
+ return r.branchLength = r.branchLength ?? new t(0), r.maxBranchLength = r.children?.length ? t.max(...r.children.map((e) => e.maxBranchLength)) : new t(0), r.name = "Root", r;
45
+ }
46
+ };
47
+ //#endregion
48
+ export { s as NewickUtil };
49
+
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../../node_modules/.pnpm/lodash@4.18.1/node_modules/lodash/_baseSum.js","../../../../../node_modules/.pnpm/lodash@4.18.1/node_modules/lodash/sum.js","../../../src/utils/NewickUtil/NewickUtil.ts"],"sourcesContent":["/**\n * The base implementation of `_.sum` and `_.sumBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the sum.\n */\nfunction baseSum(array, iteratee) {\n var result,\n index = -1,\n length = array.length;\n\n while (++index < length) {\n var current = iteratee(array[index]);\n if (current !== undefined) {\n result = result === undefined ? current : (result + current);\n }\n }\n return result;\n}\n\nmodule.exports = baseSum;\n","var baseSum = require('./_baseSum'),\n identity = require('./identity');\n\n/**\n * Computes the sum of the values in `array`.\n *\n * @static\n * @memberOf _\n * @since 3.4.0\n * @category Math\n * @param {Array} array The array to iterate over.\n * @returns {number} Returns the sum.\n * @example\n *\n * _.sum([4, 2, 8, 6]);\n * // => 20\n */\nfunction sum(array) {\n return (array && array.length)\n ? baseSum(array, identity)\n : 0;\n}\n\nmodule.exports = sum;\n","import { Decimal } from 'decimal.js';\nimport sum from 'lodash/sum';\nimport { StringUtil } from '@gen-epix/ui-core/utils/StringUtil';\n\nimport type { TreeNode } from '../../models/tree';\n\nexport class NewickUtil {\n public static getSortedNames(node: TreeNode): string[] {\n return [\n ...(node.children ?? []).map(child => NewickUtil.getSortedNames(child)).flat(),\n ...(!node.children ? [node.name] : []),\n ];\n }\n\n public static parse(newick: string): TreeNode {\n const ancestors: Array<Partial<TreeNode>> = [];\n let tree: Partial<TreeNode> = {};\n const tokens = newick.split(/(;|\\(|\\)|,|:)/).map(x => x.trim()).filter(x => x);\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n const subtree: Partial<TreeNode> = {};\n const x = tokens[i - 1];\n switch (token) {\n case ',': // another branch\n ancestors[ancestors.length - 1].children.push(subtree);\n tree = subtree;\n break;\n case ';': // end of tree, nothing to do\n break;\n case ':': // optional length next\n break;\n case '(': // new children\n tree.children = [subtree];\n ancestors.push(tree);\n tree = subtree;\n break;\n case ')': // optional name next\n tree = ancestors.pop();\n tree.children.forEach(child => {\n if (!child.children) {\n child.size = 1;\n child.maxBranchLength = new Decimal(child.branchLength ?? 0);\n child.subTreeLeaveNames = [child.name];\n child.subTreeNames = child.subTreeNames ?? [];\n }\n });\n tree.size = sum(tree.children.map(child => child.size));\n tree.subTreeNames = tree.children.map(child => child.subTreeNames).flat().filter(n => n);\n tree.subTreeLeaveNames = tree.children.map(child => child.subTreeLeaveNames).flat().filter(n => n);\n tree.maxBranchLength = Decimal.max(...tree.children.map(child => child.maxBranchLength));\n break;\n default:\n if (x === ')' || x === '(' || x === ',') {\n tree.name = token;\n } else if (x === ':') {\n tree.branchLength = new Decimal(token);\n tree.subTreeNames = (tree.children ?? []).map(child => [child.name, ...child.subTreeNames]).flat().filter(n => n);\n tree.subTreeLeaveNames = (tree.children ?? []).map(child => child.subTreeLeaveNames).flat().filter(n => n);\n tree.maxBranchLength = tree.branchLength.add(tree.children?.length ? Decimal.max(...tree.children.map(child => child.maxBranchLength)) : 0);\n tree.name = tree.name || `Generated-${StringUtil.createHash(tree.subTreeNames.join(','))}`;\n }\n }\n }\n tree.branchLength = tree.branchLength ?? new Decimal(0);\n tree.maxBranchLength = tree.children?.length ? Decimal.max(...tree.children.map(child => child.maxBranchLength)) : new Decimal(0);\n tree.name = 'Root';\n return tree;\n }\n}\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
@@ -0,0 +1,496 @@
1
+ import { Decimal } from 'decimal.js';
2
+ import { TreeAssembly, TreeNode, TreePathProperties } from '../../models/tree';
3
+ type TickerMarkScale = [number, number, number];
4
+ export declare class TreeUtil {
5
+ /**
6
+ * Traverses the tree and produces a {@link TreeAssembly} containing all
7
+ * pre-computed `Path2D` shapes and metadata needed to render the tree.
8
+ *
9
+ * Leaf nodes are assembled in DFS order (incrementing `leafIndex`), which
10
+ * determines their vertical position. Ancestor nodes are positioned vertically
11
+ * at the midpoint of their first and last child.
12
+ *
13
+ * @param params.rootNode - The root of the tree to assemble.
14
+ * @param params.treeCanvasWidth - Width of the tree canvas in pixels, used for support lines.
15
+ * @param params.pixelToGeneticDistanceRatio - Pixels per unit of genetic distance.
16
+ * @param params.itemHeight - Height of each row/item in pixels.
17
+ * @param params.sortedLeafNames - Custom sorting order of leaf nodes by name, derived from ordering (e.g. table row order). Must contain all leaf node names.
18
+ * @returns A fully populated {@link TreeAssembly} ready for rendering.
19
+ */
20
+ static assembleTree(params: {
21
+ ancestorDotRadius: number;
22
+ itemHeight: number;
23
+ leafDotRadius: number;
24
+ minimumDistancePercentageToShowLabel: number;
25
+ pixelToGeneticDistanceRatio: number;
26
+ rootNode: TreeNode;
27
+ sortedLeafNames: string[];
28
+ treeCanvasWidth: number;
29
+ treePadding: number;
30
+ }): TreeAssembly;
31
+ /**
32
+ * Assigns a dot-notation address to every node in the tree, representing its
33
+ * position in the hierarchy (e.g. `"1.2.1"`). Zero-branch-length children are
34
+ * always placed at index `1`; other children are indexed starting at `1` or
35
+ * `2` depending on whether a zero-branch sibling is present.
36
+ *
37
+ * @param rootNode - The root of the tree to address.
38
+ * @returns A map from node name to its dot-notation address string.
39
+ */
40
+ static createTreeAddresses(rootNode: TreeNode): {
41
+ [key: string]: string;
42
+ };
43
+ /**
44
+ * Draws a thin horizontal divider line across the full canvas width.
45
+ *
46
+ * Used to visually separate the scale header from the tree body.
47
+ *
48
+ * @param params.canvas - The canvas element to draw onto.
49
+ * @param params.y - Vertical position of the divider in logical pixels.
50
+ * @param params.devicePixelRatio - Screen DPR for correct positioning.
51
+ */
52
+ static drawDivider(params: {
53
+ canvas: HTMLCanvasElement;
54
+ devicePixelRatio: number;
55
+ y: number;
56
+ }): void;
57
+ /**
58
+ * Draws vertical dashed guide lines on the canvas at each tick mark position.
59
+ *
60
+ * @param params.canvas - The canvas element to draw onto.
61
+ * @param params.tickerMarkScale - Scale tuple from {@link getTickMarkScale}.
62
+ * @param params.geneticTreeWidth - Total genetic width of the tree.
63
+ * @param params.pixelToGeneticDistanceRatio - Pixels per unit of genetic distance.
64
+ * @param params.devicePixelRatio - Screen DPR.
65
+ * @param params.startY - Top of the drawn range in logical pixels (default 0).
66
+ * @param params.endY - Bottom of the drawn range in logical pixels (default canvas height).
67
+ * @param params.zoomLevel - Current zoom level.
68
+ * @param params.horizontalScrollPosition - Current horizontal scroll offset in pixels.
69
+ * @param params.regularFillColorSupportLine - Color for the guide lines.
70
+ */
71
+ static drawGuides(params: {
72
+ canvas: HTMLCanvasElement;
73
+ devicePixelRatio: number;
74
+ endY?: number;
75
+ geneticTreeWidth: Decimal;
76
+ horizontalScrollPosition: number;
77
+ paddingBottom?: number;
78
+ paddingTop?: number;
79
+ pixelToGeneticDistanceRatio: number;
80
+ regularFillColorSupportLine: string;
81
+ startY?: number;
82
+ tickerMarkScale: TickerMarkScale;
83
+ treePadding: number;
84
+ zoomLevel: number;
85
+ }): void;
86
+ /**
87
+ * Draws the genetic distance scale labels onto the canvas header area.
88
+ *
89
+ * Labels are drawn in reverse order (right-to-left distance decreasing) so
90
+ * the leftmost label shows the largest genetic distance.
91
+ *
92
+ * @param params.canvas - The canvas element to draw onto.
93
+ * @param params.fontFamily - Font family for the scale labels.
94
+ * @param params.scaleColor - Fill colour for the label text.
95
+ * @param params.headerHeight - Height of the header area in logical pixels.
96
+ * @param params.tickerMarkScale - Scale tuple from {@link getTickMarkScale}.
97
+ * @param params.geneticTreeWidth - Total genetic width of the tree.
98
+ * @param params.pixelToGeneticDistanceRatio - Pixels per unit of genetic distance.
99
+ * @param params.zoomLevel - Current zoom level.
100
+ * @param params.devicePixelRatio - Screen DPR.
101
+ * @param params.horizontalScrollPosition - Current horizontal scroll offset in pixels.
102
+ */
103
+ static drawScale(params: {
104
+ canvas: HTMLCanvasElement;
105
+ devicePixelRatio: number;
106
+ fontFamily: string;
107
+ geneticTreeWidth: Decimal;
108
+ headerHeight: number;
109
+ horizontalScrollPosition: number;
110
+ pixelToGeneticDistanceRatio: number;
111
+ scaleColor: string;
112
+ tickerMarkScale: TickerMarkScale;
113
+ treePadding: number;
114
+ zoomLevel: number;
115
+ }): void;
116
+ /**
117
+ * Renders the pre-assembled tree shapes onto a canvas using the 2D context.
118
+ *
119
+ * Applies a transform for zoom and scroll, then draws in order:
120
+ * vertical ancestor lines -> horizontal ancestor lines -> (linked) support lines
121
+ * -> (highlighted) distance labels -> ancestor dots -> leaf branch lines -> leaf dots.
122
+ *
123
+ * When nodes are highlighted, non-highlighted shapes are dimmed via `dimFn`.
124
+ * Leaf dot colours come from `nodeNameColors` when available.
125
+ *
126
+ * @param params.canvas - The canvas element to draw onto.
127
+ * @param params.treeAssembly - Pre-computed tree shapes from {@link assembleTree}.
128
+ * @param params.nodeNameColors - Colour mapping per node name for leaf dots.
129
+ * @param params.treeColor - Base colour for tree lines and dots.
130
+ * @param params.treeFont - CSS font string for distance labels.
131
+ * @param params.dimFn - Returns a dimmed version of a colour for non-highlighted shapes.
132
+ * @param params.supportLineColorLinked - Colour for support lines when linked.
133
+ * @param params.supportLineColorUnlinked - Colour for support lines when unlinked.
134
+ * @param params.zoomLevel - Current zoom level applied as a canvas transform.
135
+ * @param params.highlightedNodeNames - Node names to highlight; all others are dimmed.
136
+ * @param params.verticalScrollPosition - Vertical scroll offset in pixels.
137
+ * @param params.horizontalScrollPosition - Horizontal scroll offset in pixels.
138
+ * @param params.shouldShowDistances - Whether to render branch distance labels.
139
+ * @param params.devicePixelRatio - Screen DPR for crisp rendering on HiDPI displays.
140
+ * @param params.isLinked - Whether to draw the dashed support lines linking leaf nodes to the table.
141
+ * @param params.shouldShowSupportLinesWhenUnlinked - Whether to render support lines when unlinked.
142
+ */
143
+ static drawTree(params: {
144
+ canvas: HTMLCanvasElement;
145
+ devicePixelRatio: number;
146
+ dimFn: (color: string) => string;
147
+ headerHeight?: number;
148
+ highlightedNodeNames: string[];
149
+ horizontalScrollPosition: number;
150
+ isLinked: boolean;
151
+ itemHeight: number;
152
+ nodeNameColors: {
153
+ [key: string]: string;
154
+ } | null;
155
+ range: {
156
+ endIndex: number;
157
+ startIndex: number;
158
+ };
159
+ scrollPosition?: number;
160
+ shouldShowDistances: boolean;
161
+ shouldShowSupportLinesWhenUnlinked: boolean;
162
+ supportLineColorLinked: string;
163
+ supportLineColorUnlinked: string;
164
+ treeAssembly: TreeAssembly;
165
+ treeColor: string;
166
+ treeFont: string;
167
+ verticalScrollPosition: number;
168
+ zoomLevel: number;
169
+ }): void;
170
+ /**
171
+ * Fully redraws the tree canvas from scratch.
172
+ *
173
+ * Resets the canvas, resizes it to match its CSS layout size at the current
174
+ * DPR, then sequentially calls {@link drawBackground}, {@link drawGuides},
175
+ * {@link drawScale}, and {@link drawTree}.
176
+ *
177
+ * @param params.canvas - The canvas element to draw onto.
178
+ * @param params.backgroundColor - Fill colour for the canvas background.
179
+ * @param params.treeAssembly - Pre-computed tree shapes from {@link assembleTree}.
180
+ * @param params.nodeNameColors - Colour mapping per node name for leaf dots.
181
+ * @param params.treeColor - Base colour for tree lines and dots.
182
+ * @param params.treeFont - CSS font string for distance labels.
183
+ * @param params.dimFn - Returns a dimmed version of a colour for non-highlighted shapes.
184
+ * @param params.fontFamily - Font family for scale labels.
185
+ * @param params.scaleColor - Fill colour for scale label text.
186
+ * @param params.supportLineColorLinked - Colour for support lines when linked.
187
+ * @param params.supportLineColorUnlinked - Colour for support lines when unlinked.
188
+ * @param params.regularFillColorSupportLine - Colour for the dashed guide lines.
189
+ * @param params.zoomLevel - Current zoom level.
190
+ * @param params.isLinked - Whether to draw dashed support lines.
191
+ * @param params.highlightedNodeNames - Node names to highlight.
192
+ * @param params.verticalScrollPosition - Vertical scroll offset in pixels.
193
+ * @param params.horizontalScrollPosition - Horizontal scroll offset in pixels.
194
+ * @param params.treeCanvasWidth - Logical canvas width in pixels.
195
+ * @param params.treeCanvasHeight - Logical canvas height in pixels.
196
+ * @param params.headerHeight - Height of the scale header area in logical pixels.
197
+ * @param params.pixelToGeneticDistanceRatio - Pixels per unit of genetic distance.
198
+ * @param params.tickerMarkScale - Scale tuple from {@link getTickMarkScale}.
199
+ * @param params.shouldShowDistances - Whether to render branch labels.
200
+ * @param params.shouldShowSupportLinesWhenUnlinked - Whether to render support lines when unlinked.
201
+ * @param params.devicePixelRatio - Screen DPR for HiDPI rendering.
202
+ * @param params.geneticTreeWidth - Total genetic width of the tree.
203
+ */
204
+ static drawTreeCanvas(params: {
205
+ backgroundColor: string;
206
+ canvas: HTMLCanvasElement;
207
+ devicePixelRatio: number;
208
+ dimFn: (color: string) => string;
209
+ fontFamily: string;
210
+ geneticTreeWidth: Decimal;
211
+ headerHeight?: number;
212
+ highlightedNodeNames?: string[];
213
+ horizontalScrollPosition: number;
214
+ isLinked: boolean;
215
+ itemHeight: number;
216
+ nodeNameColors: {
217
+ [key: string]: string;
218
+ } | null;
219
+ pixelToGeneticDistanceRatio: number;
220
+ range: {
221
+ endIndex: number;
222
+ startIndex: number;
223
+ };
224
+ regularFillColorSupportLine: string;
225
+ scaleColor: string;
226
+ scrollPosition?: number;
227
+ shouldShowDistances: boolean;
228
+ shouldShowSupportLinesWhenUnlinked: boolean;
229
+ supportLineColorLinked: string;
230
+ supportLineColorUnlinked: string;
231
+ tickerMarkScale: TickerMarkScale;
232
+ treeAssembly: TreeAssembly;
233
+ treeCanvasHeight: number;
234
+ treeCanvasWidth: number;
235
+ treeColor: string;
236
+ treeFont: string;
237
+ treePadding: number;
238
+ verticalScrollPosition: number;
239
+ zoomLevel: number;
240
+ }): void;
241
+ /**
242
+ * Finds and returns a new root node for re-rooting the tree.
243
+ *
244
+ * When `selector` is `'node'`, the node with the given name becomes the new root.
245
+ * When `selector` is `'parent'`, the parent of the node with the given name is returned.
246
+ *
247
+ * The returned node has its `branchLength` reset to `0` and its `maxBranchLength`
248
+ * adjusted by subtracting the original branch length, so the scale remains consistent.
249
+ *
250
+ * @param rootNode - The current root of the tree.
251
+ * @param nodeName - Name of the node to search for.
252
+ * @param selector - Whether to return the matching node itself (`'node'`) or its parent (`'parent'`).
253
+ * @returns A shallow copy of the new root with adjusted branch length properties.
254
+ */
255
+ static findNewTreeRoot(rootNode: TreeNode, nodeName: string, selector: 'node' | 'parent'): TreeNode;
256
+ /**
257
+ * Finds the smallest non-zero branch length among all leaf nodes in the tree.
258
+ *
259
+ * This value is used as the minimum scale unit when computing tick mark
260
+ * intervals, ensuring the scale never subdivides below a single measurable step.
261
+ *
262
+ * @param tree - The root node of the tree to inspect.
263
+ * @returns The minimum positive branch length found on any leaf, or `Infinity`
264
+ * if the tree is empty or all leaves have zero/undefined branch lengths.
265
+ */
266
+ static getMinGeneticScaleUnit(tree: TreeNode): number;
267
+ /**
268
+ * Calculates the new scroll position after a zoom level change so that the
269
+ * point under the cursor stays in the same visual position.
270
+ *
271
+ * @param params.eventOffset - Pixel offset of the zoom event (e.g. mouse position) within the canvas.
272
+ * @param params.scrollPosition - Current scroll position before the zoom change.
273
+ * @param params.dimensionSize - Full logical size of the scrollable dimension in pixels.
274
+ * @param params.currentZoomLevel - The zoom level before the change.
275
+ * @param params.newZoomLevel - The zoom level after the change.
276
+ * @returns The adjusted scroll position that keeps the cursor-point visually stable.
277
+ */
278
+ static getNewScrollPositionForZoomLevel(params: {
279
+ currentZoomLevel: number;
280
+ dimensionSize: number;
281
+ eventOffset: number;
282
+ newZoomLevel: number;
283
+ scrollPosition: number;
284
+ }): number;
285
+ /**
286
+ * Performs a hit-test against the tree assembly's path maps to find the
287
+ * tree node or line segment under the mouse cursor.
288
+ *
289
+ * Checks in priority order: node dots (via `isPointInPath`) -> horizontal
290
+ * branch lines (with ±1 px vertical tolerance) -> vertical connector lines
291
+ * (with ±1 px horizontal tolerance).
292
+ *
293
+ * @param params.canvas - The canvas element that received the mouse event.
294
+ * @param params.event - The mouse event providing cursor coordinates.
295
+ * @param params.treeAssembly - The assembly containing path maps to hit-test against.
296
+ * @param params.devicePixelRatio - Screen DPR for correct coordinate scaling.
297
+ * @returns The {@link TreePathProperties} of the hit shape, or `undefined` if nothing was hit.
298
+ */
299
+ static getPathPropertiesFromCanvas(params: {
300
+ canvas: HTMLCanvasElement;
301
+ devicePixelRatio: number;
302
+ event: MouseEvent;
303
+ treeAssembly: TreeAssembly;
304
+ }): TreePathProperties;
305
+ /**
306
+ * Clamps X and Y scroll positions to their valid ranges for the current
307
+ * canvas dimensions, zoom level, and link state.
308
+ *
309
+ * When the tree is linked and at zoom level 1, y-scrolling is constrained
310
+ * tightly to the tree's pixel height. When unlinked or zoomed, the bounds
311
+ * include the full canvas extent to allow free panning.
312
+ *
313
+ * Also applies a device-pixel-ratio-dependent correction offset to prevent
314
+ * subtle over-scroll artefacts at non-integer DPR values (e.g. browser zoom).
315
+ *
316
+ * @param params.positionX - Requested horizontal scroll position.
317
+ * @param params.positionY - Requested vertical scroll position.
318
+ * @param params.treeCanvasWidth - Logical canvas width in pixels.
319
+ * @param params.treeCanvasHeight - Logical canvas height in pixels.
320
+ * @param params.treeHeight - Total rendered height of the full tree in pixels.
321
+ * @param params.devicePixelRatio - Current screen DPR.
322
+ * @param params.internalZoomLevel - Internal zoom level of the tree canvas.
323
+ * @param params.isLinked - Whether the tree is linked to the case list scroll position.
324
+ * @returns Clamped `{ newPositionX, newPositionY }` values.
325
+ */
326
+ static getSanitizedScrollPosition(params: {
327
+ devicePixelRatio: number;
328
+ headerHeight: number;
329
+ internalZoomLevel: number;
330
+ isLinked: boolean;
331
+ positionX: number;
332
+ positionY: number;
333
+ treeCanvasHeight: number;
334
+ treeCanvasWidth: number;
335
+ treeHeight: number;
336
+ treePadding: number;
337
+ }): {
338
+ newPositionX: number;
339
+ newPositionY: number;
340
+ };
341
+ /**
342
+ * Calculates the vertical scroll position for a tree canvas based on the
343
+ * currently visible rows in a linked table/list view.
344
+ *
345
+ * The goal is to keep the visible portion of the tree centered on the same
346
+ * items that are currently visible in the linked view, accounting for zoom.
347
+ *
348
+ * @param kwArgs.treeCanvasHeight - Height of the visible tree canvas area in pixels.
349
+ * @param kwArgs.treeHeight - Total rendered height of the full tree in pixels.
350
+ * @param kwArgs.treeSize - Total number of leaf items (rows) in the tree.
351
+ * @param kwArgs.verticalScrollPosition - Current vertical scroll position of the linked view in pixels.
352
+ * @param kwArgs.zoomLevel - Current zoom level (>1 means zoomed out, items appear smaller).
353
+ * @returns The new vertical scroll position for the tree canvas, clamped to [0, treeHeight - treeCanvasHeight].
354
+ */
355
+ static getScrollPositionFromTreeVisibility(kwArgs: {
356
+ itemHeight: number;
357
+ treeCanvasHeight: number;
358
+ treeHeight: number;
359
+ treeSize: number;
360
+ verticalScrollPosition: number;
361
+ zoomLevel: number;
362
+ }): number;
363
+ /**
364
+ * Determines the optimal tick mark scale for the genetic distance axis.
365
+ *
366
+ * Selects the number of scale lines and the genetic distance represented by
367
+ * each interval by searching for the combination with the least leftover
368
+ * (i.e. the product of `numLines × increment` closest to `geneticTreeWidth`).
369
+ * Increments are drawn from `SCALE_INCREMENTS` scaled by an order-of-magnitude
370
+ * multiplier derived from `geneticTreeWidth`, and those smaller than
371
+ * `minGeneticScaleUnit` are skipped.
372
+ *
373
+ * @param params.treeWidthMinusPadding - Width of the drawable canvas area in pixels (excluding padding).
374
+ * @param params.geneticTreeWidth - Total genetic distance represented by the full tree width.
375
+ * @param params.minGeneticScaleUnit - The smallest meaningful genetic distance unit (see {@link getMinGeneticScaleUnit}).
376
+ * @param params.zoomLevel - Current zoom level; divides the effective pixel width.
377
+ * @returns A {@link TickerMarkScale} tuple: `[numberOfLines, geneticDistancePerLine, minGeneticScaleUnit]`.
378
+ * Returns `[0, 0, 0]` when any required input is falsy.
379
+ */
380
+ static getTickMarkScale(params: {
381
+ geneticTreeWidth: Decimal;
382
+ maxScaleWidthPx: number;
383
+ minGeneticScaleUnit: number;
384
+ minScaleWidthPx: number;
385
+ scaleIncrements: number[];
386
+ treeWidthMinusPadding: number;
387
+ zoomLevel: number;
388
+ }): TickerMarkScale;
389
+ /**
390
+ * Sanitizes the tree by collapsing intermediate ancestor nodes that have a
391
+ * zero branch length, hoisting their children up to the parent level.
392
+ *
393
+ * This normalises Newick-parsed trees where polytomies or zero-distance
394
+ * internal nodes would otherwise produce visually redundant branching points.
395
+ *
396
+ * @param rootNode - The root of the tree to sanitize. Mutated in place.
397
+ * @returns The sanitized root node.
398
+ */
399
+ static sanitizeTree(rootNode: TreeNode, fallbackDistance: number): TreeNode;
400
+ /**
401
+ * Assembles the visual elements for an ancestor (internal) node and appends
402
+ * them to the tree assembly.
403
+ *
404
+ * Produces: vertical connector lines from each child toward the ancestor's
405
+ * Y midpoint (split into two sorted groups above and below center to allow
406
+ * per-segment highlighting), a horizontal branch line, an optional distance
407
+ * label, and a filled dot when all children have positive branch lengths.
408
+ *
409
+ * @param treeAssemblyContext - Shared context holding the assembly target and canvas dimensions.
410
+ * @param node - The ancestor tree node to assemble.
411
+ * @param childRenderResults - Assembly results returned by all direct children.
412
+ * @returns The pixel coordinates of the branch start and aggregated case IDs, for use by the parent.
413
+ */
414
+ private static assembleAncestorNode;
415
+ /**
416
+ * Assembles the visual elements for a single leaf node and appends them to
417
+ * the tree assembly.
418
+ *
419
+ * Produces: a horizontal branch line, an optional distance label, a dashed
420
+ * support line extending to the canvas edge, and a filled dot.
421
+ *
422
+ * @param treeAssemblyContext - Shared context holding the assembly target and canvas dimensions.
423
+ * @param node - The leaf tree node to assemble.
424
+ * @param distance - Accumulated genetic distance from the root to the start of this node's branch.
425
+ * @param leafIndex - Zero-based vertical index of this leaf, determining its Y position.
426
+ * @param sortingIndex - Sorting index of this leaf derived from linked view order. Used to draw the support line.
427
+ * @returns The pixel coordinates of the branch start and the node name, for use by the parent.
428
+ */
429
+ private static assembleLeafNode;
430
+ /**
431
+ * Helper that wraps a canvas drawing callback with a DPR-aware scale/translate
432
+ * transform, then restores the context to its pre-call state.
433
+ *
434
+ * @param canvas - The canvas element whose 2D context to use.
435
+ * @param devicePixelRatio - Screen DPR to scale up for HiDPI rendering.
436
+ * @param callback - Drawing operations to perform inside the transform.
437
+ */
438
+ private static draw;
439
+ /**
440
+ * Fills the tree canvas background with the theme's paper colour.
441
+ *
442
+ * @param params.canvas - The canvas element to draw onto.
443
+ * @param params.theme - MUI theme providing the background colour.
444
+ * @param params.treeCanvasWidth - Width of the area to fill in pixels.
445
+ * @param params.treeCanvasHeight - Height of the area to fill in pixels.
446
+ * @param params.devicePixelRatio - Screen DPR for HiDPI rendering.
447
+ */
448
+ private static drawBackground;
449
+ /**
450
+ * Iterates over each scale line position and invokes a callback with its
451
+ * canvas X coordinate.
452
+ *
453
+ * Computes the pixel offset for each tick so that the rightmost line aligns
454
+ * with the genetic tree width, accounting for the current scroll and zoom.
455
+ *
456
+ * @param params.tickerMarkScale - Scale tuple from {@link getTickMarkScale}.
457
+ * @param params.geneticTreeWidth - Total genetic width of the tree.
458
+ * @param params.pixelToGeneticDistanceRatio - Pixels per unit of genetic distance.
459
+ * @param params.zoomLevel - Current zoom level.
460
+ * @param params.devicePixelRatio - Screen DPR.
461
+ * @param params.horizontalScrollPosition - Current horizontal scroll offset in pixels (default 0).
462
+ * @param params.callback - Called for each line with `(x, index, numberOfLines)`.
463
+ */
464
+ private static forEachScaleLine;
465
+ private static getCanvasContext;
466
+ /**
467
+ * Returns a formatted distance label string for a branch, or `null` if the
468
+ * label should be suppressed.
469
+ *
470
+ * A label is suppressed when:
471
+ * - The tree has no maximum branch length, or it is zero.
472
+ * - The branch length is zero or undefined.
473
+ * - The branch is shorter than `MINIMUM_DISTANCE_PERCENTAGE_TO_SHOW_LABEL`
474
+ * percent of the maximum branch length in the tree.
475
+ *
476
+ * Precision is derived from the magnitude of `maxBranchLength` to avoid
477
+ * excessive decimal places for large values.
478
+ *
479
+ * @param treeAssemblyContext - Context containing the root node (for `maxBranchLength`).
480
+ * @param branchLength - The genetic distance of the branch to label.
481
+ * @returns A rounded numeric string, or `null` if the label should be hidden.
482
+ */
483
+ private static getDistanceLabel;
484
+ /**
485
+ * Returns the fill/stroke colour for a shape, dimming it when highlighted
486
+ * nodes are active and the shape is not among them.
487
+ *
488
+ * @param color - The full-brightness colour to use when highlighted or no highlight is active.
489
+ * @param dimFn - A function that returns a dimmed version of the colour.
490
+ * @param highlightedNodeNames - The currently highlighted node names.
491
+ * @param nodeNames - The node name(s) associated with the shape being drawn.
492
+ * @returns The resolved colour string.
493
+ */
494
+ private static getFillStyle;
495
+ }
496
+ export {};
@@ -0,0 +1 @@
1
+ export * from './TreeUtil';