@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
|
@@ -448,6 +448,54 @@ function getChildArcs(pd) {
|
|
|
448
448
|
return arcs;
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
+
function getChildArcsFan(pd) {
|
|
452
|
+
const TAU = Math.PI * 2;
|
|
453
|
+
const norm = (t) => ((t % TAU) + TAU) % TAU;
|
|
454
|
+
|
|
455
|
+
// Circular midpoint that travels CCW from a -> b by half the CCW span
|
|
456
|
+
function midCCW(a, b) {
|
|
457
|
+
const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
|
|
458
|
+
return norm(a + d / 2);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const byId = new Map(pd.map(d => [d.thisId, d]));
|
|
462
|
+
const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
|
|
463
|
+
|
|
464
|
+
const child_arcs = [];
|
|
465
|
+
|
|
466
|
+
for (const parent of pd) {
|
|
467
|
+
const kids = childrenByParent.get(parent.thisId) || [];
|
|
468
|
+
if (kids.length < 2) continue;
|
|
469
|
+
|
|
470
|
+
// Sort children by angle (normalized) around the circle
|
|
471
|
+
const A = kids
|
|
472
|
+
.map(id => ({ id, a: norm(byId.get(id).angle) }))
|
|
473
|
+
.sort((u, v) => u.a - v.a);
|
|
474
|
+
|
|
475
|
+
const N = A.length;
|
|
476
|
+
for (let i = 0; i < N; i++) {
|
|
477
|
+
const prev = A[(i - 1 + N) % N];
|
|
478
|
+
const cur = A[i];
|
|
479
|
+
const next = A[(i + 1) % N];
|
|
480
|
+
|
|
481
|
+
// “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
|
|
482
|
+
const start = midCCW(prev.a, cur.a);
|
|
483
|
+
const end = midCCW(cur.a, next.a);
|
|
484
|
+
|
|
485
|
+
child_arcs.push({
|
|
486
|
+
parentId: parent.thisId,
|
|
487
|
+
childId: cur.id,
|
|
488
|
+
radius: parent.r, // IMPORTANT: draw at the PARENT circle
|
|
489
|
+
start,
|
|
490
|
+
end,
|
|
491
|
+
sweep: 0 // CCW (matches describeArcSweep usage)
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return child_arcs;
|
|
497
|
+
}
|
|
498
|
+
|
|
451
499
|
// src/radial/radialLayout.js
|
|
452
500
|
|
|
453
501
|
/**
|
|
@@ -494,7 +542,12 @@ function radialLayout(node, opts = {}) {
|
|
|
494
542
|
: getArcs(pd);
|
|
495
543
|
|
|
496
544
|
// per-child arcs for half-arc highlighting if you already use them
|
|
497
|
-
|
|
545
|
+
let child_arcs = [];
|
|
546
|
+
if (opts.arcsStyle === "fan") {
|
|
547
|
+
child_arcs = getChildArcsFan(pd);
|
|
548
|
+
} else {
|
|
549
|
+
child_arcs = getChildArcs(pd);
|
|
550
|
+
}
|
|
498
551
|
|
|
499
552
|
return { data: pd, radii, arcs, child_arcs };
|
|
500
553
|
}
|
|
@@ -1094,6 +1147,10 @@ function drawPhylogeny(
|
|
|
1094
1147
|
|
|
1095
1148
|
return svg.node();
|
|
1096
1149
|
} else if (layout === "radial") {
|
|
1150
|
+
// RADIAL LAYOUT
|
|
1151
|
+
if (width !== height) {
|
|
1152
|
+
throw new Error("width and height must be the same for radial layout");
|
|
1153
|
+
}
|
|
1097
1154
|
const parsedTree = readTree(treeText);
|
|
1098
1155
|
const rad = radialLayout(parsedTree, {
|
|
1099
1156
|
angleStrategy: "fan",
|
|
@@ -1103,6 +1160,10 @@ function drawPhylogeny(
|
|
|
1103
1160
|
// ===== MODE =====
|
|
1104
1161
|
const TIP_MODE = radialMode; // "phylo" (shorten to original tips) or "outer" (project to one circle)
|
|
1105
1162
|
const isOuter = TIP_MODE === "outer";
|
|
1163
|
+
if (TIP_MODE !== "phylo" && TIP_MODE !== "outer") {
|
|
1164
|
+
throw new Error("radialMode must be either 'phylo' or 'outer'");
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1106
1167
|
|
|
1107
1168
|
// visuals (0 = let spokes reach the dots)
|
|
1108
1169
|
const DOT_R = 3;
|
|
@@ -1195,7 +1256,6 @@ function drawPhylogeny(
|
|
|
1195
1256
|
d.sweep
|
|
1196
1257
|
)
|
|
1197
1258
|
)
|
|
1198
|
-
|
|
1199
1259
|
.attr("fill", "none")
|
|
1200
1260
|
.attr("stroke", "#777")
|
|
1201
1261
|
.attr("stroke-width", strokeWidth);
|
|
@@ -1268,7 +1328,7 @@ function drawPhylogeny(
|
|
|
1268
1328
|
|
|
1269
1329
|
// maps for fast lookup on hover (childId → spoke / arc)
|
|
1270
1330
|
const spokeByChild = new Map(rad.radii.map((s) => [childIdOf(s), s]));
|
|
1271
|
-
const arcByChild = new Map(rad.child_arcs.map((a) => [a.childId, a]));
|
|
1331
|
+
const arcByChild = new Map((rad.child_arcs ?? []).map((a) => [a.childId, a]));
|
|
1272
1332
|
|
|
1273
1333
|
// ===== LABELS =====
|
|
1274
1334
|
// Labels — make them follow the tip position used by the current mode
|
|
@@ -1374,29 +1434,17 @@ function drawPhylogeny(
|
|
|
1374
1434
|
// ----- half-arc at parent radius (parent.angle → child.angle) -----
|
|
1375
1435
|
const a = arcByChild.get(cur.thisId);
|
|
1376
1436
|
if (a) {
|
|
1437
|
+
const pathD = (a.sweep == null)
|
|
1438
|
+
? describeArc(centerX, centerY, radiusPx(a.radius), a.start, a.end)
|
|
1439
|
+
: describeArcSweep(centerX, centerY, radiusPx(a.radius), a.start, a.end, a.sweep);
|
|
1440
|
+
|
|
1377
1441
|
arcLayer
|
|
1378
1442
|
.append("path")
|
|
1379
|
-
.attr("d",
|
|
1380
|
-
d.sweep == null
|
|
1381
|
-
? describeArc(
|
|
1382
|
-
centerX,
|
|
1383
|
-
centerY,
|
|
1384
|
-
radiusPx(d.radius),
|
|
1385
|
-
d.start,
|
|
1386
|
-
d.end
|
|
1387
|
-
)
|
|
1388
|
-
: describeArcSweep(
|
|
1389
|
-
centerX,
|
|
1390
|
-
centerY,
|
|
1391
|
-
radiusPx(d.radius),
|
|
1392
|
-
d.start,
|
|
1393
|
-
d.end,
|
|
1394
|
-
d.sweep
|
|
1395
|
-
)
|
|
1396
|
-
)
|
|
1443
|
+
.attr("d", pathD)
|
|
1397
1444
|
.attr("fill", "none")
|
|
1398
1445
|
.attr("stroke", stroke)
|
|
1399
1446
|
.attr("stroke-width", width);
|
|
1447
|
+
|
|
1400
1448
|
}
|
|
1401
1449
|
|
|
1402
1450
|
first = false;
|