@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.js
CHANGED
|
@@ -561,6 +561,54 @@ function getChildArcs(pd) {
|
|
|
561
561
|
return arcs;
|
|
562
562
|
}
|
|
563
563
|
|
|
564
|
+
function getChildArcsFan(pd) {
|
|
565
|
+
const TAU = Math.PI * 2;
|
|
566
|
+
const norm = (t) => ((t % TAU) + TAU) % TAU;
|
|
567
|
+
|
|
568
|
+
// Circular midpoint that travels CCW from a -> b by half the CCW span
|
|
569
|
+
function midCCW(a, b) {
|
|
570
|
+
const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
|
|
571
|
+
return norm(a + d / 2);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const byId = new Map(pd.map(d => [d.thisId, d]));
|
|
575
|
+
const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
|
|
576
|
+
|
|
577
|
+
const child_arcs = [];
|
|
578
|
+
|
|
579
|
+
for (const parent of pd) {
|
|
580
|
+
const kids = childrenByParent.get(parent.thisId) || [];
|
|
581
|
+
if (kids.length < 2) continue;
|
|
582
|
+
|
|
583
|
+
// Sort children by angle (normalized) around the circle
|
|
584
|
+
const A = kids
|
|
585
|
+
.map(id => ({ id, a: norm(byId.get(id).angle) }))
|
|
586
|
+
.sort((u, v) => u.a - v.a);
|
|
587
|
+
|
|
588
|
+
const N = A.length;
|
|
589
|
+
for (let i = 0; i < N; i++) {
|
|
590
|
+
const prev = A[(i - 1 + N) % N];
|
|
591
|
+
const cur = A[i];
|
|
592
|
+
const next = A[(i + 1) % N];
|
|
593
|
+
|
|
594
|
+
// “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
|
|
595
|
+
const start = midCCW(prev.a, cur.a);
|
|
596
|
+
const end = midCCW(cur.a, next.a);
|
|
597
|
+
|
|
598
|
+
child_arcs.push({
|
|
599
|
+
parentId: parent.thisId,
|
|
600
|
+
childId: cur.id,
|
|
601
|
+
radius: parent.r, // IMPORTANT: draw at the PARENT circle
|
|
602
|
+
start,
|
|
603
|
+
end,
|
|
604
|
+
sweep: 0 // CCW (matches describeArcSweep usage)
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return child_arcs;
|
|
610
|
+
}
|
|
611
|
+
|
|
564
612
|
// src/radial/radialLayout.js
|
|
565
613
|
|
|
566
614
|
/**
|
|
@@ -607,7 +655,12 @@ function radialLayout(node, opts = {}) {
|
|
|
607
655
|
: getArcs(pd);
|
|
608
656
|
|
|
609
657
|
// per-child arcs for half-arc highlighting if you already use them
|
|
610
|
-
|
|
658
|
+
let child_arcs = [];
|
|
659
|
+
if (opts.arcsStyle === "fan") {
|
|
660
|
+
child_arcs = getChildArcsFan(pd);
|
|
661
|
+
} else {
|
|
662
|
+
child_arcs = getChildArcs(pd);
|
|
663
|
+
}
|
|
611
664
|
|
|
612
665
|
return { data: pd, radii, arcs, child_arcs };
|
|
613
666
|
}
|
|
@@ -1261,6 +1314,10 @@ function drawPhylogeny(
|
|
|
1261
1314
|
|
|
1262
1315
|
return svg.node();
|
|
1263
1316
|
} else if (layout === "radial") {
|
|
1317
|
+
// RADIAL LAYOUT
|
|
1318
|
+
if (width !== height) {
|
|
1319
|
+
throw new Error("width and height must be the same for radial layout");
|
|
1320
|
+
}
|
|
1264
1321
|
const parsedTree = readTree(treeText);
|
|
1265
1322
|
const rad = radialLayout(parsedTree, {
|
|
1266
1323
|
angleStrategy: "fan",
|
|
@@ -1270,6 +1327,10 @@ function drawPhylogeny(
|
|
|
1270
1327
|
// ===== MODE =====
|
|
1271
1328
|
const TIP_MODE = radialMode; // "phylo" (shorten to original tips) or "outer" (project to one circle)
|
|
1272
1329
|
const isOuter = TIP_MODE === "outer";
|
|
1330
|
+
if (TIP_MODE !== "phylo" && TIP_MODE !== "outer") {
|
|
1331
|
+
throw new Error("radialMode must be either 'phylo' or 'outer'");
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1273
1334
|
|
|
1274
1335
|
// visuals (0 = let spokes reach the dots)
|
|
1275
1336
|
const DOT_R = 3;
|
|
@@ -1362,7 +1423,6 @@ function drawPhylogeny(
|
|
|
1362
1423
|
d.sweep
|
|
1363
1424
|
)
|
|
1364
1425
|
)
|
|
1365
|
-
|
|
1366
1426
|
.attr("fill", "none")
|
|
1367
1427
|
.attr("stroke", "#777")
|
|
1368
1428
|
.attr("stroke-width", strokeWidth);
|
|
@@ -1435,7 +1495,7 @@ function drawPhylogeny(
|
|
|
1435
1495
|
|
|
1436
1496
|
// maps for fast lookup on hover (childId → spoke / arc)
|
|
1437
1497
|
const spokeByChild = new Map(rad.radii.map((s) => [childIdOf(s), s]));
|
|
1438
|
-
const arcByChild = new Map(rad.child_arcs.map((a) => [a.childId, a]));
|
|
1498
|
+
const arcByChild = new Map((rad.child_arcs ?? []).map((a) => [a.childId, a]));
|
|
1439
1499
|
|
|
1440
1500
|
// ===== LABELS =====
|
|
1441
1501
|
// Labels — make them follow the tip position used by the current mode
|
|
@@ -1541,29 +1601,17 @@ function drawPhylogeny(
|
|
|
1541
1601
|
// ----- half-arc at parent radius (parent.angle → child.angle) -----
|
|
1542
1602
|
const a = arcByChild.get(cur.thisId);
|
|
1543
1603
|
if (a) {
|
|
1604
|
+
const pathD = (a.sweep == null)
|
|
1605
|
+
? describeArc(centerX, centerY, radiusPx(a.radius), a.start, a.end)
|
|
1606
|
+
: describeArcSweep(centerX, centerY, radiusPx(a.radius), a.start, a.end, a.sweep);
|
|
1607
|
+
|
|
1544
1608
|
arcLayer
|
|
1545
1609
|
.append("path")
|
|
1546
|
-
.attr("d",
|
|
1547
|
-
d.sweep == null
|
|
1548
|
-
? describeArc(
|
|
1549
|
-
centerX,
|
|
1550
|
-
centerY,
|
|
1551
|
-
radiusPx(d.radius),
|
|
1552
|
-
d.start,
|
|
1553
|
-
d.end
|
|
1554
|
-
)
|
|
1555
|
-
: describeArcSweep(
|
|
1556
|
-
centerX,
|
|
1557
|
-
centerY,
|
|
1558
|
-
radiusPx(d.radius),
|
|
1559
|
-
d.start,
|
|
1560
|
-
d.end,
|
|
1561
|
-
d.sweep
|
|
1562
|
-
)
|
|
1563
|
-
)
|
|
1610
|
+
.attr("d", pathD)
|
|
1564
1611
|
.attr("fill", "none")
|
|
1565
1612
|
.attr("stroke", stroke)
|
|
1566
1613
|
.attr("stroke-width", width);
|
|
1614
|
+
|
|
1567
1615
|
}
|
|
1568
1616
|
|
|
1569
1617
|
first = false;
|