@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.js CHANGED
@@ -567,41 +567,63 @@ function getChildArcsFan(pd) {
567
567
 
568
568
  // Circular midpoint that travels CCW from a -> b by half the CCW span
569
569
  function midCCW(a, b) {
570
- const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
570
+ const d = (b - a + TAU) % TAU; // CCW delta in [0, 2π)
571
571
  return norm(a + d / 2);
572
572
  }
573
573
 
574
- const byId = new Map(pd.map(d => [d.thisId, d]));
575
- const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
574
+ const key = (x) => (typeof x === "string" ? +x : x);
575
+
576
+ const byId = new Map(pd.map(d => [key(d.thisId), d]));
577
+ const childrenByParent = new Map(
578
+ pd.map(d => [
579
+ key(d.thisId),
580
+ // normalize children to numeric IDs; drop anything we can't resolve
581
+ (d.children || [])
582
+ .map(ch => (typeof ch === "object" ? ch.thisId : ch))
583
+ .map(key)
584
+ .filter(id => byId.has(id))
585
+ ])
586
+ );
576
587
 
577
588
  const child_arcs = [];
578
589
 
579
- for (const parent of pd) {
580
- const kids = childrenByParent.get(parent.thisId) || [];
590
+ for (const parentRaw of pd) {
591
+ const pid = key(parentRaw.thisId);
592
+ const kids = childrenByParent.get(pid) || [];
581
593
  if (kids.length < 2) continue;
582
594
 
583
595
  // Sort children by angle (normalized) around the circle
584
596
  const A = kids
585
- .map(id => ({ id, a: norm(byId.get(id).angle) }))
597
+ .map(id => {
598
+ const node = byId.get(id);
599
+ return node ? { id, a: norm(node.angle) } : null;
600
+ })
601
+ .filter(Boolean)
586
602
  .sort((u, v) => u.a - v.a);
587
603
 
588
604
  const N = A.length;
605
+ if (N < 2) continue;
606
+
607
+ const parent = byId.get(pid);
608
+ const radius = parent?.r;
609
+ if (!(radius > 0)) continue;
610
+
589
611
  for (let i = 0; i < N; i++) {
590
612
  const prev = A[(i - 1 + N) % N];
591
613
  const cur = A[i];
592
614
  const next = A[(i + 1) % N];
593
615
 
594
- // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
616
+ // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next), CCW
595
617
  const start = midCCW(prev.a, cur.a);
596
618
  const end = midCCW(cur.a, next.a);
597
619
 
598
620
  child_arcs.push({
599
- parentId: parent.thisId,
600
- childId: cur.id,
601
- radius: parent.r, // IMPORTANT: draw at the PARENT circle
621
+ parentId: pid,
622
+ childId: cur.id,
623
+ radius,
602
624
  start,
603
625
  end,
604
- sweep: 1 // CCW (matches describeArcSweep usage)
626
+ sweep: 0 // CCW (consistent with describeArcSweep and your y-flip)
605
627
  });
606
628
  }
607
629
  }
@@ -1577,7 +1599,7 @@ function drawPhylogeny(
1577
1599
  let first = true;
1578
1600
  while (cur && cur.parentId != null) {
1579
1601
  // ----- spoke (parent → child) -----
1580
- const s = spokeByChild.get(cur.thisId);
1602
+ const s = spokeByChild.get(key(cur.thisId));
1581
1603
  if (s) {
1582
1604
  const px = s.x0,
1583
1605
  py = s.y0;
@@ -1601,23 +1623,21 @@ function drawPhylogeny(
1601
1623
  }
1602
1624
 
1603
1625
  // ----- half-arc at parent radius (parent.angle → child.angle) -----
1604
- const a = arcByChild.get(cur.thisId);
1605
- if (a) {
1606
- function pathFromArcRecord(rec) {
1607
- const R = radiusPx(rec.radius);
1608
- return rec.sweep == null
1609
- ? describeArc(centerX, centerY, R, rec.start, rec.end)
1610
- : describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);
1611
- }
1612
-
1626
+ const a = arcByChild.get(key(cur.thisId));
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
+ }
1613
1633
 
1634
+ if (a) {
1614
1635
  arcLayer
1615
1636
  .append("path")
1616
1637
  .attr("d", pathFromArcRecord(a))
1617
1638
  .attr("fill", "none")
1618
1639
  .attr("stroke", stroke)
1619
1640
  .attr("stroke-width", width);
1620
-
1621
1641
  }
1622
1642
 
1623
1643
  first = false;