@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/index.cjs CHANGED
@@ -588,41 +588,63 @@ function getChildArcsFan(pd) {
588
588
 
589
589
  // Circular midpoint that travels CCW from a -> b by half the CCW span
590
590
  function midCCW(a, b) {
591
- const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
591
+ const d = (b - a + TAU) % TAU; // CCW delta in [0, 2π)
592
592
  return norm(a + d / 2);
593
593
  }
594
594
 
595
- const byId = new Map(pd.map(d => [d.thisId, d]));
596
- const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
595
+ const key = (x) => (typeof x === "string" ? +x : x);
596
+
597
+ const byId = new Map(pd.map(d => [key(d.thisId), d]));
598
+ const childrenByParent = new Map(
599
+ pd.map(d => [
600
+ key(d.thisId),
601
+ // normalize children to numeric IDs; drop anything we can't resolve
602
+ (d.children || [])
603
+ .map(ch => (typeof ch === "object" ? ch.thisId : ch))
604
+ .map(key)
605
+ .filter(id => byId.has(id))
606
+ ])
607
+ );
597
608
 
598
609
  const child_arcs = [];
599
610
 
600
- for (const parent of pd) {
601
- const kids = childrenByParent.get(parent.thisId) || [];
611
+ for (const parentRaw of pd) {
612
+ const pid = key(parentRaw.thisId);
613
+ const kids = childrenByParent.get(pid) || [];
602
614
  if (kids.length < 2) continue;
603
615
 
604
616
  // Sort children by angle (normalized) around the circle
605
617
  const A = kids
606
- .map(id => ({ id, a: norm(byId.get(id).angle) }))
618
+ .map(id => {
619
+ const node = byId.get(id);
620
+ return node ? { id, a: norm(node.angle) } : null;
621
+ })
622
+ .filter(Boolean)
607
623
  .sort((u, v) => u.a - v.a);
608
624
 
609
625
  const N = A.length;
626
+ if (N < 2) continue;
627
+
628
+ const parent = byId.get(pid);
629
+ const radius = parent?.r;
630
+ if (!(radius > 0)) continue;
631
+
610
632
  for (let i = 0; i < N; i++) {
611
633
  const prev = A[(i - 1 + N) % N];
612
634
  const cur = A[i];
613
635
  const next = A[(i + 1) % N];
614
636
 
615
- // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
637
+ // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next), CCW
616
638
  const start = midCCW(prev.a, cur.a);
617
639
  const end = midCCW(cur.a, next.a);
618
640
 
619
641
  child_arcs.push({
620
- parentId: parent.thisId,
621
- childId: cur.id,
622
- radius: parent.r, // IMPORTANT: draw at the PARENT circle
642
+ parentId: pid,
643
+ childId: cur.id,
644
+ radius,
623
645
  start,
624
646
  end,
625
- sweep: 1 // CCW (matches describeArcSweep usage)
647
+ sweep: 0 // CCW (consistent with describeArcSweep and your y-flip)
626
648
  });
627
649
  }
628
650
  }
@@ -1598,7 +1620,7 @@ function drawPhylogeny(
1598
1620
  let first = true;
1599
1621
  while (cur && cur.parentId != null) {
1600
1622
  // ----- spoke (parent → child) -----
1601
- const s = spokeByChild.get(cur.thisId);
1623
+ const s = spokeByChild.get(key(cur.thisId));
1602
1624
  if (s) {
1603
1625
  const px = s.x0,
1604
1626
  py = s.y0;
@@ -1622,23 +1644,21 @@ function drawPhylogeny(
1622
1644
  }
1623
1645
 
1624
1646
  // ----- half-arc at parent radius (parent.angle → child.angle) -----
1625
- const a = arcByChild.get(cur.thisId);
1626
- if (a) {
1627
- function pathFromArcRecord(rec) {
1628
- const R = radiusPx(rec.radius);
1629
- return rec.sweep == null
1630
- ? describeArc(centerX, centerY, R, rec.start, rec.end)
1631
- : describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);
1632
- }
1633
-
1647
+ const a = arcByChild.get(key(cur.thisId));
1648
+ function pathFromArcRecord(rec) {
1649
+ const R = radiusPx(rec.radius);
1650
+ return rec.sweep == null
1651
+ ? describeArc(centerX, centerY, R, rec.start, rec.end)
1652
+ : describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);
1653
+ }
1634
1654
 
1655
+ if (a) {
1635
1656
  arcLayer
1636
1657
  .append("path")
1637
1658
  .attr("d", pathFromArcRecord(a))
1638
1659
  .attr("fill", "none")
1639
1660
  .attr("stroke", stroke)
1640
1661
  .attr("stroke-width", width);
1641
-
1642
1662
  }
1643
1663
 
1644
1664
  first = false;