archaeopteryx 3.14.0 → 3.16.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 +166 -18
- package/archaeopteryx.js +1248 -98
- package/forester.js +353 -3
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -256,9 +256,38 @@ the mouse wheel zooms too, and never rotates), **X− / X+ become rotate** (a
|
|
|
256
256
|
32nd of a turn per press), and the fit-width slot becomes the **node label
|
|
257
257
|
direction** flip — labels riding their spokes or standing upright — while
|
|
258
258
|
vertical expansion greys out. **Fit** centres and scales the fan; **Esc**
|
|
259
|
-
also resets rotation and label direction.
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
also resets rotation and label direction. **Each radial layout greys out the
|
|
260
|
+
display type it cannot show.** Circular always aligns — its external labels are
|
|
261
|
+
pulled out to a shared ring, with dashed connectors to match, whichever type is
|
|
262
|
+
chosen — so the circular phylogram is the aligned one, and the greyed button
|
|
263
|
+
there is the *unaligned* phylogram. Unrooted is the other way round: it has no
|
|
264
|
+
common edge to align to, so the aligned type is the one greyed. The button shown
|
|
265
|
+
as chosen describes the picture, and choosing the phylogram in circular leaves
|
|
266
|
+
your aligned-or-not preference for the other layouts untouched. Auto-hide Labels
|
|
267
|
+
stays live in every layout.
|
|
268
|
+
|
|
269
|
+
### The control-panel cheat sheet
|
|
270
|
+
|
|
271
|
+
The card button in the panel header opens **Control panel**: one row for every
|
|
272
|
+
control the panel is currently showing, in the order it shows them, each with
|
|
273
|
+
the control's own glyph or name and the sentence that explains it — the layout
|
|
274
|
+
and display-type buttons included, drawn with the very glyph they carry in the
|
|
275
|
+
panel. The About box has a row for it too.
|
|
276
|
+
|
|
277
|
+
The display types say what they do and when they are greyed, whether or not
|
|
278
|
+
they are greyed in the view you are looking at: *"phylogram: branch lengths
|
|
279
|
+
drawn to scale, so the tips end ragged. Greyed in the circular layout, which
|
|
280
|
+
always carries its labels to the outer ring, so there the aligned phylogram is
|
|
281
|
+
the one drawn."*
|
|
282
|
+
|
|
283
|
+
It is **modeless** and sits beside the panel, so a row can be read while the
|
|
284
|
+
control it names is used. It describes what is on the screen: fold a section
|
|
285
|
+
and its controls leave the sheet, open a tree that offers no alignment and the
|
|
286
|
+
alignment controls are not listed. Nothing on it is written twice — a row's
|
|
287
|
+
words are the tooltip the control already carries, so a control added later
|
|
288
|
+
needs no edit here, only a tooltip. And no row shows a live value, so a sheet
|
|
289
|
+
that is saved or printed does not go wrong for every reader but the one who
|
|
290
|
+
made it.
|
|
262
291
|
|
|
263
292
|
## Rooting
|
|
264
293
|
|
|
@@ -526,9 +555,23 @@ section holds the controls:
|
|
|
526
555
|
the tree), or `None`.
|
|
527
556
|
* **Glow** — a soft glow in each domain's own colour around its box.
|
|
528
557
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
558
|
+
**Hovering a domain** reads it out: the domain's name, its E-value (small
|
|
559
|
+
ones keep their exponent, 7.20e-117), the residues it spans with its own length and the protein's,
|
|
560
|
+
the tree tip it belongs to, and its accession where the file carries one.
|
|
561
|
+
**Clicking the box** looks the domain up: straight to the Pfam entry when
|
|
562
|
+
there is an accession, and an InterPro search by name when there is not —
|
|
563
|
+
phyloXML's domain `id` is optional, and a file with names alone (our own
|
|
564
|
+
`apaf.xml` among them) cannot address an entry, since InterPro resolves
|
|
565
|
+
accessions and not Pfam identifiers. Only the domain boxes take the mouse, so
|
|
566
|
+
the tree behind the track stays clickable.
|
|
567
|
+
|
|
568
|
+
In the circular and unrooted layouts the tracks ride each named tip's spoke
|
|
569
|
+
outward and carry no names of their own (the legend still works); they need
|
|
570
|
+
radial labels, which switching layouts turns on. An architecture goes with
|
|
571
|
+
its name: where the crowding rule has taken a tip's name away, its track is
|
|
572
|
+
left out too, since a track with no name beside it can only be identified by
|
|
573
|
+
which spoke it sits on. Switching the name fields off is not the same thing —
|
|
574
|
+
nothing is hidden by the rule then, and every track is still drawn. A malformed domain — a missing or
|
|
532
575
|
impossible position or E-value — is skipped and counted in a console
|
|
533
576
|
warning, never fatal. The tracks ride into the SVG, PDF and PNG exports.
|
|
534
577
|
This is the desktop's domain display, drawn to the same numbers
|
|
@@ -555,7 +598,7 @@ To find a motif, pick **Molecular Sequence** in a search box: it matches the
|
|
|
555
598
|
residues as written, gap characters included, as the desktop does.
|
|
556
599
|
|
|
557
600
|
**Sequence Logo** (the checkbox under **Alignment**) replaces the conservation
|
|
558
|
-
bar with a **logo**:
|
|
601
|
+
bar with a **logo**: a stack of letters per column, as tall as that column's
|
|
559
602
|
information content in bits and shared out by residue frequency, most frequent
|
|
560
603
|
on top — the display the MEME Suite and WebLogo draw. A conserved column is one
|
|
561
604
|
tall letter, a variable one a short pile, and the caption gives the scale
|
|
@@ -566,7 +609,8 @@ clade's motif rather than the file's, and the caption names how many tips that
|
|
|
566
609
|
is (`n = 12`). Two consequences worth knowing: gaps are not a letter —
|
|
567
610
|
frequencies are taken over the residues present, and the stack is then scaled
|
|
568
611
|
by the column's occupancy, so a column held up by two sequences out of fifty
|
|
569
|
-
draws short rather than perfectly conserved
|
|
612
|
+
draws short rather than perfectly conserved and an all-gap column draws
|
|
613
|
+
nothing; and there is **no small-sample
|
|
570
614
|
correction**, because entering a three-tip clade is a normal thing to do and
|
|
571
615
|
Schneider's correction would subtract more than the maximum and leave the
|
|
572
616
|
column blank. Read `n` and judge.
|
|
@@ -1040,11 +1084,13 @@ each with a checkbox under Display Data to turn it off.
|
|
|
1040
1084
|
Support and branch-length values draw **2 px smaller than the label font**
|
|
1041
1085
|
(never below 6 px), as on the desktop, so they annotate without competing.
|
|
1042
1086
|
And besides the numeric display there are **Support Dots**: a filled dot at
|
|
1043
|
-
the midpoint of
|
|
1087
|
+
the midpoint of a branch whose support is at least 95% (`supportDotMinimum`;
|
|
1044
1088
|
posterior- and bootstrap-scaled trees are told apart automatically). The dot
|
|
1045
1089
|
is always a fixed amount wider than the branch itself, so it tracks the
|
|
1046
1090
|
Branch Width slider instead of sitting at one fixed size. A branch drawn
|
|
1047
|
-
shorter than the dot itself stays clean
|
|
1091
|
+
shorter than the dot itself stays clean, and so does one whose dot would land
|
|
1092
|
+
on a dot already drawn — see the crowding rule below, which is what decides
|
|
1093
|
+
between them rather than either one being shrunk.
|
|
1048
1094
|
|
|
1049
1095
|
The **control panel itself** follows the same idea. It opens showing the
|
|
1050
1096
|
sections that describe the tree — what it can be coloured by, what it shows —
|
|
@@ -1090,6 +1136,85 @@ It describes what is on screen, so inside a subtree it describes the subtree
|
|
|
1090
1136
|
and says so, and it is recomputed each time it opens rather than cached (24 ms
|
|
1091
1137
|
on the 13,246-tip demo). There is no histogram.
|
|
1092
1138
|
|
|
1139
|
+
**Auto-hide Labels** also thins out **crowded branch data**. Support values,
|
|
1140
|
+
branch-length values and support symbols are drawn only where they fit: each
|
|
1141
|
+
mark claims the box it is about to occupy, and one that would overlap a box
|
|
1142
|
+
already claimed in that pass is left out. The claim order is the tree's own,
|
|
1143
|
+
root first, so the mark nearer the root keeps its place and the same tree at
|
|
1144
|
+
the same size always drops the same marks — on screen and in every export.
|
|
1145
|
+
|
|
1146
|
+
**The Auto-hide Labels toggle lights up while it is taking something away**,
|
|
1147
|
+
and its tooltip says what. The switch governs three rules and the light asks
|
|
1148
|
+
all three: *1 in k labels shown* in the rectangular layout, which thins by
|
|
1149
|
+
index; *n names that would overprint* in the two radial layouts, which thin by
|
|
1150
|
+
overlap; and *n branch values that would overlap*, in any layout. It stays dark
|
|
1151
|
+
on a tree with room for everything.
|
|
1152
|
+
|
|
1153
|
+
**In the circular and unrooted layouts, crowded tip labels are hidden by
|
|
1154
|
+
whether they actually overprint.** A name is drawn only where its own outline
|
|
1155
|
+
overlaps no name already drawn; the order is the tree's own, root first, so
|
|
1156
|
+
the same tree at the same size always keeps the same names. **A name found by
|
|
1157
|
+
a search is the exception**: it is drawn without being asked, so what you
|
|
1158
|
+
searched for is always on the screen — and since the names it would have
|
|
1159
|
+
yielded to are already down, a hit can be drawn across one. That is the rule
|
|
1160
|
+
both programs agreed on, the hit being the thing you are looking for, and it
|
|
1161
|
+
is the one case where the sentence above does not hold. The outlines are compared as they are drawn, turned, not as the
|
|
1162
|
+
upright boxes that enclose them — the bounds of a turned name are several
|
|
1163
|
+
times its own area, and comparing those would drop names that are plainly
|
|
1164
|
+
clear of each other.
|
|
1165
|
+
|
|
1166
|
+
The rectangular layout keeps the every-k-th thinning, where it belongs: its
|
|
1167
|
+
rows really are evenly spaced, and `k` is read from the font size against the
|
|
1168
|
+
row pitch. A fan has no rows, and until 2026-09-27 the circular layout
|
|
1169
|
+
borrowed that rule anyway — thinning a ring by the display's *height* over the
|
|
1170
|
+
node count, a quantity with nothing to do with a ring's circumference. It was
|
|
1171
|
+
wrong in both directions. Measured at 1100×850: on `Caliciviridae_100.xml` it
|
|
1172
|
+
kept 48 of 97 names where all 97 fit the ring with **not one** overlapping
|
|
1173
|
+
pair; on `flu_h5.xml` it kept 59 of 354 where 118 are readable. Both now draw
|
|
1174
|
+
what fits.
|
|
1175
|
+
|
|
1176
|
+
Both fans lose names where a fan is crowded — circular takes 236 of 354 on
|
|
1177
|
+
`flu_h5.xml`, and unrooted is the harsher of the two: on `Caliciviridae_100.xml`
|
|
1178
|
+
its 97 names had 172 overlapping pairs, the deepest printing 9.5 px through
|
|
1179
|
+
its neighbour, and 34 remain with none overlapping by more than the width of
|
|
1180
|
+
the measurement itself. Where there is room, nothing is dropped:
|
|
1181
|
+
`woese-tree-of-life.xml` keeps all 23 in every layout.
|
|
1182
|
+
|
|
1183
|
+
**Names go down first, and numbers yield to them.** Every node label that will
|
|
1184
|
+
be drawn — a tip name or a clade name — reserves its space before any mark is
|
|
1185
|
+
placed, and it is never asked: a label is drawn whatever else is there, so a
|
|
1186
|
+
number that would print through a name is the one left out. A value that has
|
|
1187
|
+
landed across a name is worse than a value not drawn at all, and it is the
|
|
1188
|
+
name that says what the tree is about. Measured at 1100×850 with both numbers
|
|
1189
|
+
on, before this: 44 of 93 numbers printed through a name in the rectangular
|
|
1190
|
+
view of `flu_h5.xml`, 30 of 55 on `confidences.xml`, 10 of 15 in the unrooted
|
|
1191
|
+
view. Now none, in any of the three layouts, and no number is refused unless
|
|
1192
|
+
something drawn is in its way.
|
|
1193
|
+
Numbers and symbols are kept in separate maps, since the symbol sits on the
|
|
1194
|
+
branch and the numbers just above and below it. Symbols are never shrunk to
|
|
1195
|
+
fit: where a symbol's size means something, a smaller one would report
|
|
1196
|
+
something else. Switch the toggle off and everything is drawn. Zero needs no
|
|
1197
|
+
special case — a lone zero-length branch overlaps nothing, so its number
|
|
1198
|
+
stays. Branch events take part too. This is the desktop Archaeopteryx's rule,
|
|
1199
|
+
adopted so the two programs thin the same tree the same way; the marks they
|
|
1200
|
+
drop are close but not identical, because the boxes come from each program's
|
|
1201
|
+
own font metrics.
|
|
1202
|
+
|
|
1203
|
+
It applies in **all three layouts**. In the circular and unrooted views a mark
|
|
1204
|
+
rides its branch: it is drawn at the branch's midpoint, turned to the branch's
|
|
1205
|
+
direction and set just off the line, so the box claimed for it is the
|
|
1206
|
+
axis-aligned bounds of that turned rectangle, centred where the text's own
|
|
1207
|
+
centre lands — the desktop's formula, which carries over because both programs
|
|
1208
|
+
measure it in real screen space rather than in the layout's own units. A
|
|
1209
|
+
diagonal label's bounds are wider than its ink, so a crowded number is dropped
|
|
1210
|
+
rather than overprinted. The circular view of `flu_h5.xml` goes from 785 drawn
|
|
1211
|
+
numbers, 743 of them overlapping another, to 87 with none.
|
|
1212
|
+
|
|
1213
|
+
One placement follows from that: in the two radial views every mark is drawn on
|
|
1214
|
+
the same point, the branch's midpoint, so a **branch event** sits a line clear
|
|
1215
|
+
of the branch-length value rather than on top of it. In the rectangular layout
|
|
1216
|
+
they are already apart along the branch and share the line above it.
|
|
1217
|
+
|
|
1093
1218
|
A host with little room to give can
|
|
1094
1219
|
also start the whole panel tighter and narrower with
|
|
1095
1220
|
[`panelDensity: 'compact'`](#configuration), or collapsed to its header bar
|
|
@@ -1128,7 +1253,7 @@ copy-pastable JSON.
|
|
|
1128
1253
|
| `layout` | `'rectangular'` | The starting layout: `'rectangular'`, `'circular'`, or `'unrooted'`. |
|
|
1129
1254
|
| `ladderizeTree` | `true` | Ladderize the tree on load: at each node, the larger clade first (any number of children, so a polytomy sorts too). |
|
|
1130
1255
|
| `showMsa` | tree-derived | Open with the alignment track shown. Default: on when the tree carries an aligned `mol_seq`, off otherwise — an explicit `true`/`false` overrides that. |
|
|
1131
|
-
| `showMsaLogo` | `false` | Open with the alignment summarised as a sequence logo instead of a conservation bar:
|
|
1256
|
+
| `showMsaLogo` | `false` | Open with the alignment summarised as a sequence logo instead of a conservation bar: a stack of letters per column, as tall as its information content, over the tips currently on screen. Only drawn while the alignment track is shown. |
|
|
1132
1257
|
| `showHeatmap` | `false` | Open with the heat map shown. Offered whenever the tree carries two or more numeric per-tip fields, but off unless asked for: almost any annotated tree has such fields, so turning it on by itself would be an opinion about the tree rather than a service. |
|
|
1133
1258
|
| `heatmapColumnOrder` | tree-derived | How the heat map's columns are ordered: `'document'` (as the file lists them), `'clustered'` (Euclidean), `'clustered-presence'` (Bray–Curtis), `'alphabetical'`, `'frequency'`. The clustered modes also draw the dendrogram. Default: a **clustered** order, with the distance chosen from the values — Bray–Curtis where the matrix has zeros to ignore and nothing negative, Euclidean otherwise. An explicit value always wins and is never re-derived. |
|
|
1134
1259
|
| `heatmapManualOrder` | `null` | The heat map's columns in your own order, as an array of property refs (`['meta:recA', 'meta:gyrA', …]`). Only read while `heatmapColumnOrder` is `'manual'`. A ref the tree has not got is ignored, and a column the list does not name follows the ones it does. |
|
|
@@ -1288,7 +1413,7 @@ All 118 of them, alphabetically:
|
|
|
1288
1413
|
| `showBranchVisualizations` | Node and branch visualizations are one switch now; use the Visualizations checkbox. |
|
|
1289
1414
|
| `showConfidenceValues` | Shown when the tree has confidences. |
|
|
1290
1415
|
| `showDistributions` | Off by default. |
|
|
1291
|
-
| `showDynahideButton` |
|
|
1416
|
+
| `showDynahideButton` | The Auto-hide Labels checkbox is always shown. |
|
|
1292
1417
|
| `showExternalLabels` | On by default; use the Ext. Labels checkbox. |
|
|
1293
1418
|
| `showExternalLabelsButton` | Always shown. |
|
|
1294
1419
|
| `showExternalNodes` | Node shapes now appear wherever a node visualization applies. |
|
|
@@ -1308,7 +1433,7 @@ All 118 of them, alphabetically:
|
|
|
1308
1433
|
| `showSequenceGeneSymbol` | Sequence labelling follows what the tree contains. |
|
|
1309
1434
|
| `showSequenceName` | Sequence labelling follows what the tree contains. |
|
|
1310
1435
|
| `showSequenceSymbol` | Sequence labelling follows what the tree contains. |
|
|
1311
|
-
| `showShortenNodeNamesButton` |
|
|
1436
|
+
| `showShortenNodeNamesButton` | The Short Names checkbox is always shown; it starts on when the tree has long node names. |
|
|
1312
1437
|
| `showTaxonomy` | Shown when the tree has taxonomies. |
|
|
1313
1438
|
| `showTaxonomyButton` | Shown automatically when the tree has taxonomies. |
|
|
1314
1439
|
| `showTaxonomyCode` | Taxonomy labelling follows what the tree contains. |
|
|
@@ -1686,6 +1811,22 @@ The 2026 additions beyond the visualization system, specified tightly enough
|
|
|
1686
1811
|
to rebuild. All pure logic lives in forester.js under `npm test`; the viewer
|
|
1687
1812
|
draws.
|
|
1688
1813
|
|
|
1814
|
+
The counts quoted above are measurements on one machine, and how many names
|
|
1815
|
+
fit a ring depends on how wide the system draws them: the same tree keeps 34
|
|
1816
|
+
in the unrooted view here and 33 on a Linux CI runner, with the crowding
|
|
1817
|
+
identical on both (97 names, 172 overlapping pairs with the rule off). The
|
|
1818
|
+
rule is the same; the font is not.
|
|
1819
|
+
|
|
1820
|
+
What the viewer draws is checked separately, by driving it in headless Chrome:
|
|
1821
|
+
`npm run test:browser` (or `test:browser:quick`, one case per harness, which is
|
|
1822
|
+
what CI runs). Those harnesses open a real tree, work the controls and measure
|
|
1823
|
+
the result — which marks are drawn, where they sit, whether a control lights.
|
|
1824
|
+
They exist because `npm test` cannot reach any of it: it covers the arithmetic,
|
|
1825
|
+
and the defects this code has actually had were wiring. A connector drawn out
|
|
1826
|
+
to the ring for a name that was hidden, an Auto-hide indicator dark over a tree
|
|
1827
|
+
it was thinning, a domain architecture left beside a tip whose name had gone —
|
|
1828
|
+
each of those passed every node test and every lint.
|
|
1829
|
+
|
|
1689
1830
|
### The unrooted layout
|
|
1690
1831
|
|
|
1691
1832
|
`forester.equalAngleLayout(root, startAngle, lengthOf)` — Meacham's
|
|
@@ -1708,8 +1849,12 @@ mathematics — `spokeAngle(d)` is `uangle` in unrooted and the cluster angle
|
|
|
1708
1849
|
minus π/2 in circular; `labelAngleDeg` rotates a label along its spoke and
|
|
1709
1850
|
`labelFlip` adds 180° on the left half (`spokeAngle mod 2π ∈ (π/2, 3π/2)`).
|
|
1710
1851
|
`layoutPointXY(d)` resolves a node's position in any layout for every
|
|
1711
|
-
consumer (overview dots, hit navigator, node transforms). Unrooted
|
|
1712
|
-
|
|
1852
|
+
consumer (overview dots, hit navigator, node transforms). Unrooted has no
|
|
1853
|
+
common edge to align labels to, so the aligned phylogram is the display type
|
|
1854
|
+
it greys out (circular greys the unaligned one instead, since it always
|
|
1855
|
+
aligns). Neither radial layout has even rows, so the every-k-th
|
|
1856
|
+
tip-label thinning runs in the rectangular layout alone — both fans hide their
|
|
1857
|
+
crowded names by overlap instead, as on the desktop.
|
|
1713
1858
|
|
|
1714
1859
|
### The domain tracks
|
|
1715
1860
|
|
|
@@ -1720,7 +1865,10 @@ drawable when `from` and `to` are integers with `to > from` and `confidence`
|
|
|
1720
1865
|
(its E-value) is a number; otherwise it is skipped and counted
|
|
1721
1866
|
(`forester.domainArchitectureDomains`). Gate: `showDomainArchitectures`
|
|
1722
1867
|
state (auto-on when `_basicTreeProperties.domainArchitectures`) AND external
|
|
1723
|
-
labels shown AND, in a radial layout, radial rather than upright labels
|
|
1868
|
+
labels shown AND, in a radial layout, radial rather than upright labels AND,
|
|
1869
|
+
per tip, a name the crowding rule has not taken (`_labelDropped`) — note
|
|
1870
|
+
"taken by the rule", not "absent": with the name fields switched off nothing
|
|
1871
|
+
is hidden by the rule and every track is still drawn.
|
|
1724
1872
|
|
|
1725
1873
|
Scale: one factor for the tree, `f = W_eff / Lmax × 0.9` px per residue.
|
|
1726
1874
|
`W` (the track width) starts at `0.25 × viewport width`; `d+` / `d−` scale it
|
|
@@ -1733,8 +1881,8 @@ threshold never rescales. The rectangular layout reserves `20 + W + 10` px
|
|
|
1733
1881
|
from `_w` past the label reservation (`_domainReserve`, counted wherever `_w`
|
|
1734
1882
|
is), so the tree compresses to make room; the radial fit adds
|
|
1735
1883
|
`4 + W_eff + 10` to the ring. Placement: rectangular `start = _w +
|
|
1736
|
-
nodeLabelGap + labelSpace + 20` for every tip
|
|
1737
|
-
height `clamp(round(tipPitch / 2), 6, 16)`; circular `r0 = maxRad +
|
|
1884
|
+
nodeLabelGap + labelSpace + 20` for every tip still showing its name (one
|
|
1885
|
+
aligned column) with box height `clamp(round(tipPitch / 2), 6, 16)`; circular `r0 = maxRad +
|
|
1738
1886
|
labelSpace + 4` under `rotate(spoke)`; unrooted `translate(tip)
|
|
1739
1887
|
rotate(spoke)` with `start = labelSpace + 4`. A domain `from..to` covers
|
|
1740
1888
|
`[start + (from − 1) f, start + to f]` — residue `r` is `[(r − 1) f, r f]`,
|