@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.
@@ -475,41 +475,63 @@ function getChildArcsFan(pd) {
475
475
 
476
476
  // Circular midpoint that travels CCW from a -> b by half the CCW span
477
477
  function midCCW(a, b) {
478
- const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
478
+ const d = (b - a + TAU) % TAU; // CCW delta in [0, 2π)
479
479
  return norm(a + d / 2);
480
480
  }
481
481
 
482
- const byId = new Map(pd.map(d => [d.thisId, d]));
483
- const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
482
+ const key = (x) => (typeof x === "string" ? +x : x);
483
+
484
+ const byId = new Map(pd.map(d => [key(d.thisId), d]));
485
+ const childrenByParent = new Map(
486
+ pd.map(d => [
487
+ key(d.thisId),
488
+ // normalize children to numeric IDs; drop anything we can't resolve
489
+ (d.children || [])
490
+ .map(ch => (typeof ch === "object" ? ch.thisId : ch))
491
+ .map(key)
492
+ .filter(id => byId.has(id))
493
+ ])
494
+ );
484
495
 
485
496
  const child_arcs = [];
486
497
 
487
- for (const parent of pd) {
488
- const kids = childrenByParent.get(parent.thisId) || [];
498
+ for (const parentRaw of pd) {
499
+ const pid = key(parentRaw.thisId);
500
+ const kids = childrenByParent.get(pid) || [];
489
501
  if (kids.length < 2) continue;
490
502
 
491
503
  // Sort children by angle (normalized) around the circle
492
504
  const A = kids
493
- .map(id => ({ id, a: norm(byId.get(id).angle) }))
505
+ .map(id => {
506
+ const node = byId.get(id);
507
+ return node ? { id, a: norm(node.angle) } : null;
508
+ })
509
+ .filter(Boolean)
494
510
  .sort((u, v) => u.a - v.a);
495
511
 
496
512
  const N = A.length;
513
+ if (N < 2) continue;
514
+
515
+ const parent = byId.get(pid);
516
+ const radius = parent?.r;
517
+ if (!(radius > 0)) continue;
518
+
497
519
  for (let i = 0; i < N; i++) {
498
520
  const prev = A[(i - 1 + N) % N];
499
521
  const cur = A[i];
500
522
  const next = A[(i + 1) % N];
501
523
 
502
- // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
524
+ // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next), CCW
503
525
  const start = midCCW(prev.a, cur.a);
504
526
  const end = midCCW(cur.a, next.a);
505
527
 
506
528
  child_arcs.push({
507
- parentId: parent.thisId,
508
- childId: cur.id,
509
- radius: parent.r, // IMPORTANT: draw at the PARENT circle
529
+ parentId: pid,
530
+ childId: cur.id,
531
+ radius,
510
532
  start,
511
533
  end,
512
- sweep: 1 // CCW (matches describeArcSweep usage)
534
+ sweep: 0 // CCW (consistent with describeArcSweep and your y-flip)
513
535
  });
514
536
  }
515
537
  }
@@ -1431,7 +1453,7 @@ function drawPhylogeny(
1431
1453
  let first = true;
1432
1454
  while (cur && cur.parentId != null) {
1433
1455
  // ----- spoke (parent → child) -----
1434
- const s = spokeByChild.get(cur.thisId);
1456
+ const s = spokeByChild.get(key(cur.thisId));
1435
1457
  if (s) {
1436
1458
  const px = s.x0,
1437
1459
  py = s.y0;
@@ -1455,23 +1477,21 @@ function drawPhylogeny(
1455
1477
  }
1456
1478
 
1457
1479
  // ----- half-arc at parent radius (parent.angle → child.angle) -----
1458
- const a = arcByChild.get(cur.thisId);
1459
- if (a) {
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
- }
1466
-
1480
+ const a = arcByChild.get(key(cur.thisId));
1481
+ function pathFromArcRecord(rec) {
1482
+ const R = radiusPx(rec.radius);
1483
+ return rec.sweep == null
1484
+ ? describeArc(centerX, centerY, R, rec.start, rec.end)
1485
+ : describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);
1486
+ }
1467
1487
 
1488
+ if (a) {
1468
1489
  arcLayer
1469
1490
  .append("path")
1470
1491
  .attr("d", pathFromArcRecord(a))
1471
1492
  .attr("fill", "none")
1472
1493
  .attr("stroke", stroke)
1473
1494
  .attr("stroke-width", width);
1474
-
1475
1495
  }
1476
1496
 
1477
1497
  first = false;