FigTree compatibility
FigTree compatibility is load-bearing: phylo can open a FigTree/BEAST NEXUS
file, apply its FIGTREE styling block, and write it back so that FigTree itself
re-opens the result without complaint. The trickiest part is preserving
int-vs-double in numeric annotations — FigTree casts settings to specific Java
types in its Swing controllers, and a 5.0 where a 5 is expected raises a
ClassCastException at paint time, which hangs FigTree with no error dialog.
Three modules form the seam: figtree-apply.ts (settings → style),
figtree-write.ts (style → settings), and figtree-text-style.ts (settings →
label text styling).
Recognizing a FIGTREE block
Section titled “Recognizing a FIGTREE block”A NEXUS document’s figtreeSettings (from parseNexus)
is a raw Map<string, string>. recognizeFigtreeSettings turns a typed
settings bag into a structured RecognizedFigtreeSettings — the shared
intermediate both translators read.
import { recognizeFigtreeSettings } from "insomni-phylo";
const recognized = recognizeFigtreeSettings(settingsLike);recognized.branchColorAttribute; // string?recognized.branchLineWidth; // number?recognized.foregroundColour; // Color?The input shape, FigTreeSettingsLike, wraps values: ReadonlyMap<string, FigTreeValueLike> where each value is a tagged union that distinguishes
integers from doubles:
type FigTreeValueLike = | { kind: "boolean"; value: boolean } | { kind: "integer"; value: number } | { kind: "double"; value: number } | …; // string / colorCarrying "integer" vs "double" through recognition is what makes the
round-trip safe — the type tag survives so the writer can re-emit 5 as 5,
not 5.0.
Applying settings to a style
Section titled “Applying settings to a style”Two translators share that recognition layer:
import { applyFigtreeSettings, // → Partial<PhyloChartConfig> applyFigtreeSettingsLegacy, // → Partial<PhyloStyle>} from "insomni-phylo";
// Drive the legacy PhyloScene renderer:const patch = applyFigtreeSettingsLegacy(settingsLike, attributes);handle.setStyle(patch);| Function | Output | Use |
|---|---|---|
applyFigtreeSettings(settings, attributes, options?) | Partial<PhyloChartConfig> | The config shape; options may fold in buffers / layout / annotations to produce text(id) accessors and per-tip !font styling. |
applyFigtreeSettingsLegacy(settings, attributes) | Partial<PhyloStyle> | Patch for the PhyloScene renderer (what phylon uses). |
Both take the attribute catalog
(AttributeInfo[]) so a branchColorAttribute naming a non-numeric column is
silently dropped instead of producing a broken scheme. ApplyFigtreeSettingsOptions
fields: clades, buffers, layout, annotations.
Layout settings
Section titled “Layout settings”FigTree’s layout-affecting keys (ladderize order, branch transform, tip
alignment) are read separately by applyFigtreeLayoutSettings, which returns a
PhyloLayoutOptions (ladderize?, transform?, …):
import { applyFigtreeLayoutSettings } from "insomni-phylo";
const { ladderize, transform } = applyFigtreeLayoutSettings(settingsLike);Label text styling
Section titled “Label text styling”figtree-text-style.ts translates recognized settings into per-section
TextStyleOptions the labels consume — folding in font size, the master
appearance.foregroundColour, and per-tip !font annotation overrides.
| Function | Section |
|---|---|
figtreeTipLabelStyle(r, buffers, annotations) | Tip labels (incl. !font). |
figtreeBranchLabelStyle(r, buffers, annotations) | Branch labels. |
figtreeInternalLabelStyle(r, buffers, annotations) | Internal-node labels. |
figtreeLabelText(buffers, layout, annotations, displayAttribute, sigDigits) | A text(id) accessor resolving displayAttribute (wraps resolveLabelText). |
Writing settings back
Section titled “Writing settings back”phyloStyleToFigtreeSettings(style) is the inverse of
applyFigtreeSettingsLegacy: it produces a Map<string, string> of raw value
strings ready for set key=value; emission. Merge this into the parsed
document’s figtreeSettings map before calling
writeNexus.
import { phyloStyleToFigtreeSettings, writeNexus } from "insomni-phylo";
const patch = phyloStyleToFigtreeSettings(handle.style);for (const [k, v] of patch) doc.figtreeSettings.set(k, v);const nexus = writeNexus(doc);The int-vs-double discipline is enforced here. Numeric keys are emitted with the explicit formatters from the writer module:
| Helper | Emits |
|---|---|
formatJavaInteger(n) | Integer.toString — e.g. 12 (range-checked, throws on non-int). |
formatJavaDouble(n) | Double.toString — e.g. 5.0, 1.0E-7. |
encodeFigtreeColor(r, g, b, a?) | #rrggbb or #aarrggbb. |
phyloStyleToFigtreeSettings chooses formatJavaInteger for keys FigTree casts
to int (font sizes, significant digits, …) and formatJavaDouble for keys it
casts to double (line widths, …) — matching the cast each FigTree controller
performs.
Why raw values matter for annotations
Section titled “Why raw values matter for annotations”The same int-vs-double concern applies to per-node annotations, not just the
FIGTREE block. The AnnotationColumn.rawValues
channel keeps the verbatim source slice of every cell
("0.5455417729722768", "{8,8.88E-16,#-26966}"). When
writeNewick re-emits annotations it prefers the raw
slice, so scientific-notation casing, integer-typed positional list elements,
and #-prefixed vs bare-decimal color encodings all survive a round-trip
byte-for-byte. The color parser behind extractHilights accepts #rrggbb,
#aarrggbb, and Java’s signed-decimal ARGB form (-16776961) that BEAST and
FigTree emit for !color / !hilight.