archaeopteryx 3.23.0 → 3.24.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.
- package/README.md +41 -14
- package/archaeopteryx.js +27 -10
- package/forester.js +131 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ config key live and shows the exact config JSON to copy into your own
|
|
|
25
25
|
`launch()` call.
|
|
26
26
|
|
|
27
27
|
* [Auspice / Nextstrain JSON (Time | Div, Clock plot)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=auspice)
|
|
28
|
-
* [Swine H1 HA1 + alignment (Nexus)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=swh1)
|
|
28
|
+
* [Swine H1 HA1 + alignment (Nexus; Clock plot from the years in the names)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=swh1)
|
|
29
29
|
* [BEAST annotations (Nexus)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=beast)
|
|
30
30
|
* [Flavivirus mature peptides (10 trees)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=flavivirus)
|
|
31
31
|
* [SARS-CoV-2 time tree (calendar)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=sarscov2)
|
|
@@ -933,11 +933,28 @@ ancestors' points, each at its own date and divergence; the panel opens
|
|
|
933
933
|
without them, since the line is the tips' and a deep root would crowd them
|
|
934
934
|
into a corner.
|
|
935
935
|
|
|
936
|
-
The button is offered on
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
936
|
+
The button is offered on two kinds of tree, whenever the line can be drawn
|
|
937
|
+
(three tips or more, not all on one date; a tree sampled at one moment has
|
|
938
|
+
no slope to estimate):
|
|
939
|
+
|
|
940
|
+
- a tree that has **Time | Div**: every node is dated and has a divergence,
|
|
941
|
+
so the ancestors can be shown too;
|
|
942
|
+
- a tree whose **branch lengths are its divergence and whose tips alone are
|
|
943
|
+
dated** — the tree a root-to-tip plot is run on *before* dating, TempEst's
|
|
944
|
+
case. A tip's divergence is the sum of the branch lengths from the root;
|
|
945
|
+
the plot has tips only (**Internal nodes** is not offered, since no
|
|
946
|
+
ancestor is dated); **Root date, in the tree** says `none`, since the tree
|
|
947
|
+
states no root date, and **Rate** is in the tree's own branch length unit,
|
|
948
|
+
where it states one. A tip's date is what the file states for it (a
|
|
949
|
+
phyloXML `<date>`, a `num_date` annotation) or, failing that, the date in
|
|
950
|
+
its **name**, as TempEst reads it: the rightmost date the label carries
|
|
951
|
+
(`A/swine/Iowa/A02524571/2020|1A.3.3.3` is 2020; a year alone is taken as
|
|
952
|
+
mid-year, 2020.5; a day/month-ambiguous `03/04/2020` is read day first),
|
|
953
|
+
and the panel's note says how many names it read and how many were
|
|
954
|
+
ambiguous. An ancestor is never dated by its name. Every tip must be
|
|
955
|
+
dated, by the file or by its name, and every branch must have a length: a
|
|
956
|
+
tree with a gap is not offered the plot, rather than plotted with tips
|
|
957
|
+
missing.
|
|
941
958
|
|
|
942
959
|
# forester.js
|
|
943
960
|
forester.js is a general suite for dealing with phylogenetic trees.
|
|
@@ -2599,15 +2616,25 @@ deliberate difference.)
|
|
|
2599
2616
|
### The clock plot
|
|
2600
2617
|
|
|
2601
2618
|
**Model** (`forester.js`, pure). `forester.clockPlotKind(phy)` answers
|
|
2602
|
-
`'divergence'` or `null`: the tree has
|
|
2603
|
-
(`forester.hasTimeAndDivergence`)
|
|
2604
|
-
|
|
2605
|
-
`
|
|
2606
|
-
|
|
2607
|
-
|
|
2619
|
+
`'divergence'`, `'root-to-tip'` or `null`: `'divergence'` where the tree has
|
|
2620
|
+
Time and Div (`forester.hasTimeAndDivergence`); `'root-to-tip'` where every
|
|
2621
|
+
tip is dated (by the file, or by the date in its name:
|
|
2622
|
+
`forester.parseTipLabelDate`), no ancestor is, every branch states a length
|
|
2623
|
+
and no branch states a clock rate (a clock-model tree's lengths are time) — and in
|
|
2624
|
+
both cases three tips or more, not all on one date. It is asked of the
|
|
2625
|
+
**tree**, never of the view. `forester.clockPlotData(phy, view)` returns
|
|
2626
|
+
`{kind, forward, unit, divUnit, fromRates, points, root, fit}`: a point
|
|
2608
2627
|
`{node, date, div, tip}` per node of the view (a tip also carries its
|
|
2609
|
-
`residual
|
|
2610
|
-
|
|
2628
|
+
`residual`; on a root-to-tip tree the tips only, their `div` the sum of the
|
|
2629
|
+
branch lengths from the tree's root), `forward` false where the dates are
|
|
2630
|
+
ages (on a root-to-tip tree: where their unit is geologic), `divUnit` the
|
|
2631
|
+
divergence's unit (`subs/site` on a Time | Div tree, a root-to-tip tree's
|
|
2632
|
+
stated branch length unit, or `null`), `fromRates` true where the divergence
|
|
2633
|
+
is time × clock rate, `calendar` true where the dates are calendar years
|
|
2634
|
+
(their unit says so, or they were read off the tips' names), `fromNames` and
|
|
2635
|
+
`ambiguousNames` how many tips a root-to-tip tree dates by their names and
|
|
2636
|
+
how many of those are day/month-ambiguous (read day first), `root.date`
|
|
2637
|
+
`null` on a root-to-tip tree, and `fit` =
|
|
2611
2638
|
`forester.clockRegression(dates, divergences)` over the tips — ordinary least
|
|
2612
2639
|
squares about the means, `{n, slope, intercept, r2}` — plus `rate` (the slope
|
|
2613
2640
|
in the direction time runs) and `rootDate` (where the line reaches the view
|
package/archaeopteryx.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
// v 3.
|
|
23
|
+
// v 3.24.0
|
|
24
24
|
// 2026-09-17
|
|
25
25
|
//
|
|
26
26
|
// Archaeopteryx.js is a software tool for the visualization and
|
|
@@ -103,7 +103,7 @@ function (root, d3, forester, phyloXml) {
|
|
|
103
103
|
// IIFE's own function name -- a plain object says what it is.)
|
|
104
104
|
let archaeopteryx = {};
|
|
105
105
|
|
|
106
|
-
const VERSION = '3.
|
|
106
|
+
const VERSION = '3.24.0';
|
|
107
107
|
const WEBSITE = 'https://cmzmasek.github.io/archaeopteryx-js/';
|
|
108
108
|
const DESKTOP_WEBSITE = 'https://cmzmasek.github.io/archaeopteryx/';
|
|
109
109
|
const SOURCE_WEBSITE = 'https://github.com/cmzmasek/archaeopteryx-js';
|
|
@@ -11171,10 +11171,12 @@ function (root, d3, forester, phyloXml) {
|
|
|
11171
11171
|
row.appendChild(input);
|
|
11172
11172
|
row.appendChild(span);
|
|
11173
11173
|
options.appendChild(row);
|
|
11174
|
-
return
|
|
11174
|
+
return row;
|
|
11175
11175
|
};
|
|
11176
11176
|
check('Regression line', 'line', 'to show/hide the least-squares line through the tips');
|
|
11177
|
-
|
|
11177
|
+
// on a root-to-tip tree no ancestor is dated: nothing to show, so the
|
|
11178
|
+
// choice is not offered (layClockPlot hides it by the data's kind)
|
|
11179
|
+
let internalRow = check('Internal nodes', 'internal', 'to show/hide the internal nodes: their dates were inferred,'
|
|
11178
11180
|
+ ' and they take no part in the line');
|
|
11179
11181
|
let deselect = document.createElement('button');
|
|
11180
11182
|
deselect.type = 'button';
|
|
@@ -11197,7 +11199,7 @@ function (root, d3, forester, phyloXml) {
|
|
|
11197
11199
|
body.appendChild(note);
|
|
11198
11200
|
|
|
11199
11201
|
_clockPlot = {dialog: shell.dialog, svg: svg, axes: axes, internal: internal, line: line, tips: tips,
|
|
11200
|
-
ring: ring, box: box, stats: stats, note: note, deselect: deselect,
|
|
11202
|
+
ring: ring, box: box, stats: stats, note: note, deselect: deselect, internalRow: internalRow,
|
|
11201
11203
|
data: null, marks: [], seen: null, hover: null, lays: 0, x: null, y: null};
|
|
11202
11204
|
// the shell's close control, and anything else that closes the dialog
|
|
11203
11205
|
let closed = function () {
|
|
@@ -11408,7 +11410,9 @@ function (root, d3, forester, phyloXml) {
|
|
|
11408
11410
|
}
|
|
11409
11411
|
|
|
11410
11412
|
function layClockPlot(cp, data) {
|
|
11411
|
-
let calendar = data.
|
|
11413
|
+
let calendar = data.calendar;
|
|
11414
|
+
let rootToTip = data.kind === 'root-to-tip';
|
|
11415
|
+
cp.internalRow.style.display = rootToTip ? 'none' : '';
|
|
11412
11416
|
let shown = data.points.filter(function (p) {
|
|
11413
11417
|
return p.tip || _clockPlotOptions.internal;
|
|
11414
11418
|
});
|
|
@@ -11464,7 +11468,7 @@ function (root, d3, forester, phyloXml) {
|
|
|
11464
11468
|
seg('aptx-clock-frame', left, top, left, bottom);
|
|
11465
11469
|
let unit = data.unit && !calendar ? ' (' + data.unit + ')' : '';
|
|
11466
11470
|
text('aptx-clock-title', (data.forward ? 'Date' : 'Age') + unit, (left + right) / 2, CLOCK_PLOT_H - 6, 'middle');
|
|
11467
|
-
let yt = text('aptx-clock-title', 'Divergence (
|
|
11471
|
+
let yt = text('aptx-clock-title', 'Divergence' + (data.divUnit ? ' (' + data.divUnit + ')' : ''), 0, 0, 'middle');
|
|
11468
11472
|
yt.setAttribute('transform', 'translate(12,' + ((top + bottom) / 2) + ') rotate(-90)');
|
|
11469
11473
|
|
|
11470
11474
|
// --- the points: ancestors under the line, tips over it
|
|
@@ -11506,12 +11510,13 @@ function (root, d3, forester, phyloXml) {
|
|
|
11506
11510
|
let tipCount = data.points.reduce(function (n, p) {
|
|
11507
11511
|
return n + (p.tip ? 1 : 0);
|
|
11508
11512
|
}, 0);
|
|
11509
|
-
let per = '
|
|
11513
|
+
let per = (data.divUnit ? ' ' + data.divUnit : '') + ' per ' + (calendar ? 'year' : (data.unit || 'unit of time'));
|
|
11510
11514
|
if (fit) {
|
|
11511
11515
|
treePropRow(cp.stats, 'Rate', clockNumber(fit.rate) + per);
|
|
11512
11516
|
treePropRow(cp.stats, 'Root date, by the line', fit.rootDate === null
|
|
11513
11517
|
? 'none: divergence does not rise with time' : clockDate(fit.rootDate, calendar));
|
|
11514
|
-
treePropRow(cp.stats, 'Root date, in the tree',
|
|
11518
|
+
treePropRow(cp.stats, 'Root date, in the tree', data.root.date === null
|
|
11519
|
+
? 'none: only the tips are dated' : clockDate(data.root.date, calendar));
|
|
11515
11520
|
treePropRow(cp.stats, 'R²', fit.r2 === null ? 'none: every tip has one divergence' : clockNumber(fit.r2));
|
|
11516
11521
|
} else {
|
|
11517
11522
|
treePropRow(cp.stats, 'Line', 'none: it takes three tips, not all on one date');
|
|
@@ -11520,6 +11525,15 @@ function (root, d3, forester, phyloXml) {
|
|
|
11520
11525
|
cp.note.textContent = (data.fromRates
|
|
11521
11526
|
? 'Divergence here is each branch’s time × its clock rate: the points show the model’s rates, not a measurement of their own. '
|
|
11522
11527
|
: '')
|
|
11528
|
+
+ (rootToTip
|
|
11529
|
+
? 'Divergence here is each tip’s distance from the root along the branch lengths the file states; only the tips are dated. '
|
|
11530
|
+
: '')
|
|
11531
|
+
+ (data.fromNames > 0
|
|
11532
|
+
? (data.fromNames === 1 ? 'One tip is dated by its name' : countNumber(data.fromNames) + ' tips are dated by their names')
|
|
11533
|
+
+ ' (a year alone is taken as mid-year'
|
|
11534
|
+
+ (data.ambiguousNames > 0 ? '; ' + countNumber(data.ambiguousNames) + ' with day and month ambiguous, read day first' : '')
|
|
11535
|
+
+ '). '
|
|
11536
|
+
: '')
|
|
11523
11537
|
+ 'The line is fitted to the tips only. Tips share ancestry, so R² describes the fit and is no test.'
|
|
11524
11538
|
+ ' Click a point to select its node; drag to select the tips in a box.';
|
|
11525
11539
|
// what was laid down, for the browser harness: read here, where it is
|
|
@@ -18225,9 +18239,12 @@ function (root, d3, forester, phyloXml) {
|
|
|
18225
18239
|
// live. Injecting twice is a no-op.
|
|
18226
18240
|
injectPanelStyles();
|
|
18227
18241
|
|
|
18242
|
+
// REMOVED (2026-10-07): the 2.x class "node_mouseover_tooltip" beside
|
|
18243
|
+
// "aptx-tip". A host's old stylesheet outranked .aptx-tip through it
|
|
18244
|
+
// (div.X beats .Y) and restyled the popup; see CHANGELOG, Unreleased.
|
|
18228
18245
|
if (!_node_mouseover_div) {
|
|
18229
18246
|
_node_mouseover_div = d3.select("body").append("div")
|
|
18230
|
-
.attr("class", "
|
|
18247
|
+
.attr("class", "aptx-tip")
|
|
18231
18248
|
.style("opacity", 1e-6);
|
|
18232
18249
|
}
|
|
18233
18250
|
|
package/forester.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
// v 3.
|
|
23
|
+
// v 3.24.0
|
|
24
24
|
// 2026-09-17
|
|
25
25
|
//
|
|
26
26
|
// forester.js is a general suite for dealing with phylogenetic trees.
|
|
@@ -7254,18 +7254,28 @@
|
|
|
7254
7254
|
// a panel linked to the tree, named "Clock plot"; JS only until the
|
|
7255
7255
|
// design is final.
|
|
7256
7256
|
//
|
|
7257
|
-
// THE RULE, designed for two kinds of tree
|
|
7257
|
+
// THE RULE, designed for two kinds of tree (both built; the second on
|
|
7258
|
+
// Christian's word, 2026-10-03):
|
|
7258
7259
|
//
|
|
7259
7260
|
// 1. 'divergence' -- a tree with Time and Div (hasTimeAndDivergence): each
|
|
7260
7261
|
// node states a date and a divergence from the root, recorded on it or
|
|
7261
7262
|
// summed from each branch's time x its clock rate. Every node is a
|
|
7262
7263
|
// point; which of the two layouts is on screen does not matter.
|
|
7263
|
-
// 2. 'root-to-tip'
|
|
7264
|
-
//
|
|
7265
|
-
//
|
|
7266
|
-
//
|
|
7267
|
-
//
|
|
7268
|
-
//
|
|
7264
|
+
// 2. 'root-to-tip' -- a tree whose branch lengths are its divergence and
|
|
7265
|
+
// whose tips are dated, with no date on any ancestor (it is not a time
|
|
7266
|
+
// tree): a tip's divergence is the sum of the stated lengths from the
|
|
7267
|
+
// root. Tips only. This is the tree a clock plot is wanted for most --
|
|
7268
|
+
// is there a signal worth dating? -- and the one whose answer depends
|
|
7269
|
+
// on its root. TempEst's plot. A tip is dated by what the file STATES
|
|
7270
|
+
// for it (a phyloXML <date>, a num_date annotation), or else by the
|
|
7271
|
+
// date its NAME carries, read by the label-date parser both programs
|
|
7272
|
+
// anchor BEAST heights with (Christian, 2026-10-03: names count, as
|
|
7273
|
+
// TempEst reads them; a year alone is mid-year). An ancestor is never
|
|
7274
|
+
// dated by its name. Every tip must be dated and every branch must
|
|
7275
|
+
// state a length: a tip with either missing has no point, and a plot
|
|
7276
|
+
// that drops tips silently answers a different question than it was
|
|
7277
|
+
// asked. A clock-model tree (a rate on every branch) is never this
|
|
7278
|
+
// kind: its lengths are time.
|
|
7269
7279
|
//
|
|
7270
7280
|
// In both, the plot is offered when its LINE can be drawn: three tips or
|
|
7271
7281
|
// more, and not all of them on one date. A tree sampled at one moment has
|
|
@@ -7277,19 +7287,76 @@
|
|
|
7277
7287
|
// an observation; an ancestor's was inferred, usually with a clock, and
|
|
7278
7288
|
// counting it would have the estimate confirm itself. The line is not
|
|
7279
7289
|
// forced through the root, so the date it reaches the root's divergence
|
|
7280
|
-
// at can be held against the date the tree states for the root
|
|
7290
|
+
// at can be held against the date the tree states for the root -- where
|
|
7291
|
+
// the tree states one; a root-to-tip tree does not, and there the line's
|
|
7292
|
+
// date is the only estimate.
|
|
7281
7293
|
const CLOCK_PLOT_MIN_TIPS = 3;
|
|
7282
7294
|
|
|
7295
|
+
// A tip's date on a root-to-tip tree: the one the file states, else the
|
|
7296
|
+
// one its name carries (forester.parseTipLabelDate: the rightmost date in
|
|
7297
|
+
// the label; a year alone is mid-year; an ambiguous numeric date is read
|
|
7298
|
+
// day first). {value, fromName, ambiguous}, or null for no date at all.
|
|
7299
|
+
function rootToTipDate(n) {
|
|
7300
|
+
let d = auspiceNodeDate(n);
|
|
7301
|
+
if (d !== null) {
|
|
7302
|
+
return {value: d, fromName: false, ambiguous: false};
|
|
7303
|
+
}
|
|
7304
|
+
let m = (typeof n.name === 'string' && n.name.length > 0) ? forester.parseTipLabelDate(n.name) : null;
|
|
7305
|
+
return m ? {value: m.decimalYear, fromName: true, ambiguous: m.ambiguous === true} : null;
|
|
7306
|
+
}
|
|
7307
|
+
|
|
7308
|
+
// Kind 2 of THE RULE, before the tips are counted: dated tips (by the
|
|
7309
|
+
// file or by their names), undated ancestors (by the file; a name dates
|
|
7310
|
+
// no ancestor), a length on every branch, no clock rates.
|
|
7311
|
+
function isRootToTipTree(root) {
|
|
7312
|
+
if (!root || root._divergenceFromRates === true || !everyLengthWasStated(root)) {
|
|
7313
|
+
return false;
|
|
7314
|
+
}
|
|
7315
|
+
let ok = true;
|
|
7316
|
+
forester.preOrderTraversalAll(root, function (n) {
|
|
7317
|
+
if (!ok) {
|
|
7318
|
+
return;
|
|
7319
|
+
}
|
|
7320
|
+
ok = (n.children && n.children.length > 0) ? auspiceNodeDate(n) === null : rootToTipDate(n) !== null;
|
|
7321
|
+
});
|
|
7322
|
+
return ok;
|
|
7323
|
+
}
|
|
7324
|
+
|
|
7325
|
+
// Each node's distance from the root along the stated branch lengths:
|
|
7326
|
+
// a root-to-tip tree's divergence.
|
|
7327
|
+
function rootToTipDistances(root) {
|
|
7328
|
+
let dist = new Map();
|
|
7329
|
+
(function walk(n, d) {
|
|
7330
|
+
dist.set(n, d);
|
|
7331
|
+
let children = n.children;
|
|
7332
|
+
if (children) {
|
|
7333
|
+
for (let i = 0; i < children.length; ++i) {
|
|
7334
|
+
let len = children[i].branch_length;
|
|
7335
|
+
walk(children[i], d + ((typeof len === 'number' && isFinite(len)) ? len : 0));
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
}(root, 0));
|
|
7339
|
+
return dist;
|
|
7340
|
+
}
|
|
7341
|
+
|
|
7283
7342
|
/**
|
|
7284
7343
|
* Which clock plot the tree has, or null for none (see THE RULE above).
|
|
7285
7344
|
*
|
|
7286
7345
|
* @param phy the tree
|
|
7287
|
-
* @returns {string|null} 'divergence', or null
|
|
7346
|
+
* @returns {string|null} 'divergence', 'root-to-tip', or null
|
|
7288
7347
|
*/
|
|
7289
7348
|
forester.clockPlotKind = function (phy) {
|
|
7290
|
-
if (!phy
|
|
7349
|
+
if (!phy) {
|
|
7291
7350
|
return null;
|
|
7292
7351
|
}
|
|
7352
|
+
let kind = forester.hasTimeAndDivergence(phy) ? 'divergence'
|
|
7353
|
+
: (isRootToTipTree(forester.getTreeRoot(phy)) ? 'root-to-tip' : null);
|
|
7354
|
+
if (kind === null) {
|
|
7355
|
+
return null;
|
|
7356
|
+
}
|
|
7357
|
+
let dateOf = kind === 'root-to-tip' ? function (n) {
|
|
7358
|
+
return rootToTipDate(n).value; // every tip has one: the kind says so
|
|
7359
|
+
} : auspiceNodeDate;
|
|
7293
7360
|
let tips = 0;
|
|
7294
7361
|
let first = null;
|
|
7295
7362
|
let differ = false;
|
|
@@ -7298,14 +7365,14 @@
|
|
|
7298
7365
|
return;
|
|
7299
7366
|
}
|
|
7300
7367
|
++tips;
|
|
7301
|
-
let d =
|
|
7368
|
+
let d = dateOf(n);
|
|
7302
7369
|
if (first === null) {
|
|
7303
7370
|
first = d;
|
|
7304
7371
|
} else if (d !== first) {
|
|
7305
7372
|
differ = true;
|
|
7306
7373
|
}
|
|
7307
7374
|
});
|
|
7308
|
-
return (tips >= CLOCK_PLOT_MIN_TIPS && differ) ?
|
|
7375
|
+
return (tips >= CLOCK_PLOT_MIN_TIPS && differ) ? kind : null;
|
|
7309
7376
|
};
|
|
7310
7377
|
|
|
7311
7378
|
/**
|
|
@@ -7363,14 +7430,25 @@
|
|
|
7363
7430
|
* @param view the tree or clade to plot (the tree, a node, or the
|
|
7364
7431
|
* viewer's subtree root); the whole tree where none is given
|
|
7365
7432
|
* @returns {object|null} null where the tree has no clock plot; else
|
|
7366
|
-
* kind 'divergence'
|
|
7433
|
+
* kind 'divergence' or 'root-to-tip'
|
|
7367
7434
|
* forward whether the dates increase toward the tips (calendar
|
|
7368
7435
|
* dates); false for ages
|
|
7369
7436
|
* unit the dates' unit as the tree states it, or null
|
|
7437
|
+
* divUnit the divergence's unit: 'subs/site' for a tree with Time
|
|
7438
|
+
* and Div, a root-to-tip tree's stated branch length unit,
|
|
7439
|
+
* or null
|
|
7370
7440
|
* fromRates whether the divergence is time x clock rate
|
|
7441
|
+
* calendar whether the dates are calendar years: their unit says so,
|
|
7442
|
+
* or they were read off the tips' names
|
|
7443
|
+
* fromNames how many tips are dated by their names (a root-to-tip
|
|
7444
|
+
* tree; 0 otherwise)
|
|
7445
|
+
* ambiguousNames how many of those names carry a day/month-ambiguous
|
|
7446
|
+
* numeric date, read day first
|
|
7371
7447
|
* points [{node, date, div, tip}] in preorder; the tips'
|
|
7372
|
-
* `residual` is their divergence less the line's
|
|
7373
|
-
*
|
|
7448
|
+
* `residual` is their divergence less the line's. On a
|
|
7449
|
+
* root-to-tip tree the tips only: no ancestor has a date
|
|
7450
|
+
* root {node, date, div}: the top of the view; `date` null on a
|
|
7451
|
+
* root-to-tip tree
|
|
7374
7452
|
* fit forester.clockRegression over the tips, with
|
|
7375
7453
|
* rate the slope in the direction time runs: divergence
|
|
7376
7454
|
* per unit of time (negative where the tips'
|
|
@@ -7391,24 +7469,49 @@
|
|
|
7391
7469
|
if (view) {
|
|
7392
7470
|
top = (!view.parent && view.children && view.children.length === 1) ? view.children[0] : view;
|
|
7393
7471
|
}
|
|
7472
|
+
// a root-to-tip tree's divergence is the path from the TREE's root,
|
|
7473
|
+
// in the view of a clade too: the clade's top then has one as well
|
|
7474
|
+
let rootToTip = kind === 'root-to-tip';
|
|
7475
|
+
let dist = rootToTip ? rootToTipDistances(treeRoot) : null;
|
|
7476
|
+
let divOf = rootToTip ? function (n) {
|
|
7477
|
+
return dist.get(n);
|
|
7478
|
+
} : auspiceNodeDiv;
|
|
7394
7479
|
let points = [];
|
|
7395
7480
|
let xs = [];
|
|
7396
7481
|
let ys = [];
|
|
7482
|
+
let fromNames = 0;
|
|
7483
|
+
let ambiguousNames = 0;
|
|
7397
7484
|
forester.preOrderTraversalAll(top, function (n) {
|
|
7485
|
+
let tip = !(n.children && n.children.length > 0);
|
|
7398
7486
|
let date = auspiceNodeDate(n);
|
|
7399
|
-
|
|
7487
|
+
if (rootToTip && tip && date === null) {
|
|
7488
|
+
let named = rootToTipDate(n); // every tip has one: the kind says so
|
|
7489
|
+
date = named ? named.value : null;
|
|
7490
|
+
if (named && named.fromName) {
|
|
7491
|
+
++fromNames;
|
|
7492
|
+
if (named.ambiguous) {
|
|
7493
|
+
++ambiguousNames;
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
let div = divOf(n);
|
|
7400
7498
|
if (date === null || div === null) {
|
|
7401
|
-
return; //
|
|
7499
|
+
return; // an undated ancestor (root-to-tip), or a guard, not a rule
|
|
7402
7500
|
}
|
|
7403
|
-
let tip = !(n.children && n.children.length > 0);
|
|
7404
7501
|
points.push({node: n, date: date, div: div, tip: tip});
|
|
7405
7502
|
if (tip) {
|
|
7406
7503
|
xs.push(date);
|
|
7407
7504
|
ys.push(div);
|
|
7408
7505
|
}
|
|
7409
7506
|
});
|
|
7410
|
-
let
|
|
7411
|
-
|
|
7507
|
+
let info = forester.timeAxisInfo(treeRoot);
|
|
7508
|
+
// a root-to-tip tree has no dated pair to read the direction off:
|
|
7509
|
+
// its dates are calendar dates unless their unit says ages
|
|
7510
|
+
let forward = rootToTip ? info.type !== 'geologic' : timeIncreasesTowardTips(treeRoot);
|
|
7511
|
+
// calendar years: the dates' unit says so, or they were read off the
|
|
7512
|
+
// tips' names, which carry no other kind of date
|
|
7513
|
+
let calendar = forward && (info.type === 'calendar' || (rootToTip && info.type === null && fromNames > 0));
|
|
7514
|
+
let root = {node: top, date: auspiceNodeDate(top), div: divOf(top)};
|
|
7412
7515
|
let fit = forester.clockRegression(xs, ys);
|
|
7413
7516
|
if (fit) {
|
|
7414
7517
|
fit.rate = forward ? fit.slope : -fit.slope;
|
|
@@ -7420,11 +7523,17 @@
|
|
|
7420
7523
|
}
|
|
7421
7524
|
}
|
|
7422
7525
|
}
|
|
7526
|
+
let lengthUnit = (phy !== treeRoot && typeof phy.branch_length_unit === 'string'
|
|
7527
|
+
&& phy.branch_length_unit.trim().length > 0) ? phy.branch_length_unit.trim() : null;
|
|
7423
7528
|
return {
|
|
7424
7529
|
kind: kind,
|
|
7425
7530
|
forward: forward,
|
|
7426
7531
|
unit: dateUnitOf(treeRoot),
|
|
7427
|
-
|
|
7532
|
+
divUnit: rootToTip ? lengthUnit : DIVERGENCE_LENGTH_UNIT,
|
|
7533
|
+
fromRates: !rootToTip && treeRoot._divergenceFromRates === true && !recordsDivergence(treeRoot),
|
|
7534
|
+
calendar: calendar,
|
|
7535
|
+
fromNames: fromNames,
|
|
7536
|
+
ambiguousNames: ambiguousNames,
|
|
7428
7537
|
points: points,
|
|
7429
7538
|
root: root,
|
|
7430
7539
|
fit: fit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archaeopteryx",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0",
|
|
4
4
|
"description": "Archaeopteryx.js is a software tool for the visualization and analysis of highly annotated phylogenetic trees.",
|
|
5
5
|
"main": "archaeopteryx.js",
|
|
6
6
|
"types": "archaeopteryx.d.ts",
|