@euphrasiologist/lwphylo 1.2.12 → 1.2.15
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/dist/drawPhylogeny.cjs +69 -21
- package/dist/drawPhylogeny.cjs.map +1 -1
- package/dist/drawPhylogeny.esm.js +69 -21
- package/dist/drawPhylogeny.esm.js.map +1 -1
- package/dist/drawPhylogeny.umd.js +1 -1
- package/dist/drawPhylogeny.umd.js.map +1 -1
- package/dist/index.cjs +69 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +69 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -582,6 +582,54 @@ function getChildArcs(pd) {
|
|
|
582
582
|
return arcs;
|
|
583
583
|
}
|
|
584
584
|
|
|
585
|
+
function getChildArcsFan(pd) {
|
|
586
|
+
const TAU = Math.PI * 2;
|
|
587
|
+
const norm = (t) => ((t % TAU) + TAU) % TAU;
|
|
588
|
+
|
|
589
|
+
// Circular midpoint that travels CCW from a -> b by half the CCW span
|
|
590
|
+
function midCCW(a, b) {
|
|
591
|
+
const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
|
|
592
|
+
return norm(a + d / 2);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const byId = new Map(pd.map(d => [d.thisId, d]));
|
|
596
|
+
const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
|
|
597
|
+
|
|
598
|
+
const child_arcs = [];
|
|
599
|
+
|
|
600
|
+
for (const parent of pd) {
|
|
601
|
+
const kids = childrenByParent.get(parent.thisId) || [];
|
|
602
|
+
if (kids.length < 2) continue;
|
|
603
|
+
|
|
604
|
+
// Sort children by angle (normalized) around the circle
|
|
605
|
+
const A = kids
|
|
606
|
+
.map(id => ({ id, a: norm(byId.get(id).angle) }))
|
|
607
|
+
.sort((u, v) => u.a - v.a);
|
|
608
|
+
|
|
609
|
+
const N = A.length;
|
|
610
|
+
for (let i = 0; i < N; i++) {
|
|
611
|
+
const prev = A[(i - 1 + N) % N];
|
|
612
|
+
const cur = A[i];
|
|
613
|
+
const next = A[(i + 1) % N];
|
|
614
|
+
|
|
615
|
+
// “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
|
|
616
|
+
const start = midCCW(prev.a, cur.a);
|
|
617
|
+
const end = midCCW(cur.a, next.a);
|
|
618
|
+
|
|
619
|
+
child_arcs.push({
|
|
620
|
+
parentId: parent.thisId,
|
|
621
|
+
childId: cur.id,
|
|
622
|
+
radius: parent.r, // IMPORTANT: draw at the PARENT circle
|
|
623
|
+
start,
|
|
624
|
+
end,
|
|
625
|
+
sweep: 0 // CCW (matches describeArcSweep usage)
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return child_arcs;
|
|
631
|
+
}
|
|
632
|
+
|
|
585
633
|
// src/radial/radialLayout.js
|
|
586
634
|
|
|
587
635
|
/**
|
|
@@ -628,7 +676,12 @@ function radialLayout(node, opts = {}) {
|
|
|
628
676
|
: getArcs(pd);
|
|
629
677
|
|
|
630
678
|
// per-child arcs for half-arc highlighting if you already use them
|
|
631
|
-
|
|
679
|
+
let child_arcs = [];
|
|
680
|
+
if (opts.arcsStyle === "fan") {
|
|
681
|
+
child_arcs = getChildArcsFan(pd);
|
|
682
|
+
} else {
|
|
683
|
+
child_arcs = getChildArcs(pd);
|
|
684
|
+
}
|
|
632
685
|
|
|
633
686
|
return { data: pd, radii, arcs, child_arcs };
|
|
634
687
|
}
|
|
@@ -1282,6 +1335,10 @@ function drawPhylogeny(
|
|
|
1282
1335
|
|
|
1283
1336
|
return svg.node();
|
|
1284
1337
|
} else if (layout === "radial") {
|
|
1338
|
+
// RADIAL LAYOUT
|
|
1339
|
+
if (width !== height) {
|
|
1340
|
+
throw new Error("width and height must be the same for radial layout");
|
|
1341
|
+
}
|
|
1285
1342
|
const parsedTree = readTree(treeText);
|
|
1286
1343
|
const rad = radialLayout(parsedTree, {
|
|
1287
1344
|
angleStrategy: "fan",
|
|
@@ -1291,6 +1348,10 @@ function drawPhylogeny(
|
|
|
1291
1348
|
// ===== MODE =====
|
|
1292
1349
|
const TIP_MODE = radialMode; // "phylo" (shorten to original tips) or "outer" (project to one circle)
|
|
1293
1350
|
const isOuter = TIP_MODE === "outer";
|
|
1351
|
+
if (TIP_MODE !== "phylo" && TIP_MODE !== "outer") {
|
|
1352
|
+
throw new Error("radialMode must be either 'phylo' or 'outer'");
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1294
1355
|
|
|
1295
1356
|
// visuals (0 = let spokes reach the dots)
|
|
1296
1357
|
const DOT_R = 3;
|
|
@@ -1383,7 +1444,6 @@ function drawPhylogeny(
|
|
|
1383
1444
|
d.sweep
|
|
1384
1445
|
)
|
|
1385
1446
|
)
|
|
1386
|
-
|
|
1387
1447
|
.attr("fill", "none")
|
|
1388
1448
|
.attr("stroke", "#777")
|
|
1389
1449
|
.attr("stroke-width", strokeWidth);
|
|
@@ -1456,7 +1516,7 @@ function drawPhylogeny(
|
|
|
1456
1516
|
|
|
1457
1517
|
// maps for fast lookup on hover (childId → spoke / arc)
|
|
1458
1518
|
const spokeByChild = new Map(rad.radii.map((s) => [childIdOf(s), s]));
|
|
1459
|
-
const arcByChild = new Map(rad.child_arcs.map((a) => [a.childId, a]));
|
|
1519
|
+
const arcByChild = new Map((rad.child_arcs ?? []).map((a) => [a.childId, a]));
|
|
1460
1520
|
|
|
1461
1521
|
// ===== LABELS =====
|
|
1462
1522
|
// Labels — make them follow the tip position used by the current mode
|
|
@@ -1562,29 +1622,17 @@ function drawPhylogeny(
|
|
|
1562
1622
|
// ----- half-arc at parent radius (parent.angle → child.angle) -----
|
|
1563
1623
|
const a = arcByChild.get(cur.thisId);
|
|
1564
1624
|
if (a) {
|
|
1625
|
+
const pathD = (a.sweep == null)
|
|
1626
|
+
? describeArc(centerX, centerY, radiusPx(a.radius), a.start, a.end)
|
|
1627
|
+
: describeArcSweep(centerX, centerY, radiusPx(a.radius), a.start, a.end, a.sweep);
|
|
1628
|
+
|
|
1565
1629
|
arcLayer
|
|
1566
1630
|
.append("path")
|
|
1567
|
-
.attr("d",
|
|
1568
|
-
d.sweep == null
|
|
1569
|
-
? describeArc(
|
|
1570
|
-
centerX,
|
|
1571
|
-
centerY,
|
|
1572
|
-
radiusPx(d.radius),
|
|
1573
|
-
d.start,
|
|
1574
|
-
d.end
|
|
1575
|
-
)
|
|
1576
|
-
: describeArcSweep(
|
|
1577
|
-
centerX,
|
|
1578
|
-
centerY,
|
|
1579
|
-
radiusPx(d.radius),
|
|
1580
|
-
d.start,
|
|
1581
|
-
d.end,
|
|
1582
|
-
d.sweep
|
|
1583
|
-
)
|
|
1584
|
-
)
|
|
1631
|
+
.attr("d", pathD)
|
|
1585
1632
|
.attr("fill", "none")
|
|
1586
1633
|
.attr("stroke", stroke)
|
|
1587
1634
|
.attr("stroke-width", width);
|
|
1635
|
+
|
|
1588
1636
|
}
|
|
1589
1637
|
|
|
1590
1638
|
first = false;
|