@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.
@@ -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; // CCW delta a->b in [0, 2π)
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 byId = new Map(pd.map(d => [d.thisId, d]));
462
- const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
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 parent of pd) {
467
- const kids = childrenByParent.get(parent.thisId) || [];
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 => ({ id, a: norm(byId.get(id).angle) }))
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: parent.thisId,
487
- childId: cur.id,
488
- radius: parent.r, // IMPORTANT: draw at the PARENT circle
508
+ parentId: pid,
509
+ childId: cur.id,
510
+ radius,
489
511
  start,
490
512
  end,
491
- sweep: 1 // CCW (matches describeArcSweep usage)
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
- if (a) {
1439
- function pathFromArcRecord(rec) {
1440
- const R = radiusPx(rec.radius);
1441
- return rec.sweep == null
1442
- ? describeArc(centerX, centerY, R, rec.start, rec.end)
1443
- : describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);
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;