@euphrasiologist/lwphylo 1.2.18 → 1.2.19
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 +42 -22
- package/dist/drawPhylogeny.cjs.map +1 -1
- package/dist/drawPhylogeny.esm.js +42 -22
- 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 +42 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +42 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -454,41 +454,63 @@ function getChildArcsFan(pd) {
|
|
|
454
454
|
|
|
455
455
|
// Circular midpoint that travels CCW from a -> b by half the CCW span
|
|
456
456
|
function midCCW(a, b) {
|
|
457
|
-
const d = (b - a + TAU) % TAU;
|
|
457
|
+
const d = (b - a + TAU) % TAU; // CCW delta in [0, 2π)
|
|
458
458
|
return norm(a + d / 2);
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
const
|
|
462
|
-
|
|
461
|
+
const key = (x) => (typeof x === "string" ? +x : x);
|
|
462
|
+
|
|
463
|
+
const byId = new Map(pd.map(d => [key(d.thisId), d]));
|
|
464
|
+
const childrenByParent = new Map(
|
|
465
|
+
pd.map(d => [
|
|
466
|
+
key(d.thisId),
|
|
467
|
+
// normalize children to numeric IDs; drop anything we can't resolve
|
|
468
|
+
(d.children || [])
|
|
469
|
+
.map(ch => (typeof ch === "object" ? ch.thisId : ch))
|
|
470
|
+
.map(key)
|
|
471
|
+
.filter(id => byId.has(id))
|
|
472
|
+
])
|
|
473
|
+
);
|
|
463
474
|
|
|
464
475
|
const child_arcs = [];
|
|
465
476
|
|
|
466
|
-
for (const
|
|
467
|
-
const
|
|
477
|
+
for (const parentRaw of pd) {
|
|
478
|
+
const pid = key(parentRaw.thisId);
|
|
479
|
+
const kids = childrenByParent.get(pid) || [];
|
|
468
480
|
if (kids.length < 2) continue;
|
|
469
481
|
|
|
470
482
|
// Sort children by angle (normalized) around the circle
|
|
471
483
|
const A = kids
|
|
472
|
-
.map(id =>
|
|
484
|
+
.map(id => {
|
|
485
|
+
const node = byId.get(id);
|
|
486
|
+
return node ? { id, a: norm(node.angle) } : null;
|
|
487
|
+
})
|
|
488
|
+
.filter(Boolean)
|
|
473
489
|
.sort((u, v) => u.a - v.a);
|
|
474
490
|
|
|
475
491
|
const N = A.length;
|
|
492
|
+
if (N < 2) continue;
|
|
493
|
+
|
|
494
|
+
const parent = byId.get(pid);
|
|
495
|
+
const radius = parent?.r;
|
|
496
|
+
if (!(radius > 0)) continue;
|
|
497
|
+
|
|
476
498
|
for (let i = 0; i < N; i++) {
|
|
477
499
|
const prev = A[(i - 1 + N) % N];
|
|
478
500
|
const cur = A[i];
|
|
479
501
|
const next = A[(i + 1) % N];
|
|
480
502
|
|
|
481
|
-
// “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
|
|
503
|
+
// “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next), CCW
|
|
482
504
|
const start = midCCW(prev.a, cur.a);
|
|
483
505
|
const end = midCCW(cur.a, next.a);
|
|
484
506
|
|
|
485
507
|
child_arcs.push({
|
|
486
|
-
parentId:
|
|
487
|
-
childId:
|
|
488
|
-
radius
|
|
508
|
+
parentId: pid,
|
|
509
|
+
childId: cur.id,
|
|
510
|
+
radius,
|
|
489
511
|
start,
|
|
490
512
|
end,
|
|
491
|
-
sweep:
|
|
513
|
+
sweep: 0 // CCW (consistent with describeArcSweep and your y-flip)
|
|
492
514
|
});
|
|
493
515
|
}
|
|
494
516
|
}
|
|
@@ -1410,7 +1432,7 @@ function drawPhylogeny(
|
|
|
1410
1432
|
let first = true;
|
|
1411
1433
|
while (cur && cur.parentId != null) {
|
|
1412
1434
|
// ----- spoke (parent → child) -----
|
|
1413
|
-
const s = spokeByChild.get(cur.thisId);
|
|
1435
|
+
const s = spokeByChild.get(key(cur.thisId));
|
|
1414
1436
|
if (s) {
|
|
1415
1437
|
const px = s.x0,
|
|
1416
1438
|
py = s.y0;
|
|
@@ -1434,23 +1456,21 @@ function drawPhylogeny(
|
|
|
1434
1456
|
}
|
|
1435
1457
|
|
|
1436
1458
|
// ----- half-arc at parent radius (parent.angle → child.angle) -----
|
|
1437
|
-
const a = arcByChild.get(cur.thisId);
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1459
|
+
const a = arcByChild.get(key(cur.thisId));
|
|
1460
|
+
function pathFromArcRecord(rec) {
|
|
1461
|
+
const R = radiusPx(rec.radius);
|
|
1462
|
+
return rec.sweep == null
|
|
1463
|
+
? describeArc(centerX, centerY, R, rec.start, rec.end)
|
|
1464
|
+
: describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);
|
|
1465
|
+
}
|
|
1446
1466
|
|
|
1467
|
+
if (a) {
|
|
1447
1468
|
arcLayer
|
|
1448
1469
|
.append("path")
|
|
1449
1470
|
.attr("d", pathFromArcRecord(a))
|
|
1450
1471
|
.attr("fill", "none")
|
|
1451
1472
|
.attr("stroke", stroke)
|
|
1452
1473
|
.attr("stroke-width", width);
|
|
1453
|
-
|
|
1454
1474
|
}
|
|
1455
1475
|
|
|
1456
1476
|
first = false;
|