@euphrasiologist/lwphylo 1.2.13 → 1.2.16

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.
@@ -448,6 +448,54 @@ function getChildArcs(pd) {
448
448
  return arcs;
449
449
  }
450
450
 
451
+ function getChildArcsFan(pd) {
452
+ const TAU = Math.PI * 2;
453
+ const norm = (t) => ((t % TAU) + TAU) % TAU;
454
+
455
+ // Circular midpoint that travels CCW from a -> b by half the CCW span
456
+ function midCCW(a, b) {
457
+ const d = (b - a + TAU) % TAU; // CCW delta a->b in [0, 2π)
458
+ return norm(a + d / 2);
459
+ }
460
+
461
+ const byId = new Map(pd.map(d => [d.thisId, d]));
462
+ const childrenByParent = new Map(pd.map(d => [d.thisId, d.children || []]));
463
+
464
+ const child_arcs = [];
465
+
466
+ for (const parent of pd) {
467
+ const kids = childrenByParent.get(parent.thisId) || [];
468
+ if (kids.length < 2) continue;
469
+
470
+ // Sort children by angle (normalized) around the circle
471
+ const A = kids
472
+ .map(id => ({ id, a: norm(byId.get(id).angle) }))
473
+ .sort((u, v) => u.a - v.a);
474
+
475
+ const N = A.length;
476
+ for (let i = 0; i < N; i++) {
477
+ const prev = A[(i - 1 + N) % N];
478
+ const cur = A[i];
479
+ const next = A[(i + 1) % N];
480
+
481
+ // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next)
482
+ const start = midCCW(prev.a, cur.a);
483
+ const end = midCCW(cur.a, next.a);
484
+
485
+ child_arcs.push({
486
+ parentId: parent.thisId,
487
+ childId: cur.id,
488
+ radius: parent.r, // IMPORTANT: draw at the PARENT circle
489
+ start,
490
+ end,
491
+ sweep: 0 // CCW (matches describeArcSweep usage)
492
+ });
493
+ }
494
+ }
495
+
496
+ return child_arcs;
497
+ }
498
+
451
499
  // src/radial/radialLayout.js
452
500
 
453
501
  /**
@@ -494,7 +542,12 @@ function radialLayout(node, opts = {}) {
494
542
  : getArcs(pd);
495
543
 
496
544
  // per-child arcs for half-arc highlighting if you already use them
497
- const child_arcs = getChildArcs(pd);
545
+ let child_arcs = [];
546
+ if (opts.arcsStyle === "fan") {
547
+ child_arcs = getChildArcsFan(pd);
548
+ } else {
549
+ child_arcs = getChildArcs(pd);
550
+ }
498
551
 
499
552
  return { data: pd, radii, arcs, child_arcs };
500
553
  }
@@ -1162,6 +1215,29 @@ function drawPhylogeny(
1162
1215
  return { X0, Y0, X1s: X0 + dx * t, Y1s: Y0 + dy * t, len };
1163
1216
  }
1164
1217
 
1218
+ // put this next to your other radial helpers (top of file or in your lib)
1219
+ function arcPathCCW(cx, cy, R, a0, a1) {
1220
+ const TAU = Math.PI * 2;
1221
+ const norm = (t) => ((t % TAU) + TAU) % TAU;
1222
+
1223
+ a0 = norm(a0);
1224
+ a1 = norm(a1);
1225
+
1226
+ const delta = (a1 - a0 + TAU) % TAU; // CCW span a0 -> a1 in [0, 2π)
1227
+ if (delta < 1e-9) return ""; // degenerate; draw nothing
1228
+
1229
+ // With your polarToCartesian using y = cy - r*sin(t),
1230
+ // sweepFlag=0 gives a visually CCW arc segment.
1231
+ const largeArcFlag = delta > Math.PI ? 1 : 0;
1232
+ const sweepFlag = 0;
1233
+
1234
+ const p0 = polarToCartesian(cx, cy, R, a0);
1235
+ const p1 = polarToCartesian(cx, cy, R, a1);
1236
+
1237
+ return `M ${p0.x} ${p0.y} A ${R} ${R} 0 ${largeArcFlag} ${sweepFlag} ${p1.x} ${p1.y}`;
1238
+ }
1239
+
1240
+
1165
1241
  // ===== SVG ROOT =====
1166
1242
  const svg = d3
1167
1243
  .create("svg")
@@ -1203,7 +1279,6 @@ function drawPhylogeny(
1203
1279
  d.sweep
1204
1280
  )
1205
1281
  )
1206
-
1207
1282
  .attr("fill", "none")
1208
1283
  .attr("stroke", "#777")
1209
1284
  .attr("stroke-width", strokeWidth);
@@ -1382,9 +1457,7 @@ function drawPhylogeny(
1382
1457
  // ----- half-arc at parent radius (parent.angle → child.angle) -----
1383
1458
  const a = arcByChild.get(cur.thisId);
1384
1459
  if (a) {
1385
- const pathD = (a.sweep == null)
1386
- ? describeArc(centerX, centerY, radiusPx(a.radius), a.start, a.end)
1387
- : describeArcSweep(centerX, centerY, radiusPx(a.radius), a.start, a.end, a.sweep);
1460
+ const pathD = arcPathCCW(centerX, centerY, radiusPx(a.radius), a.start, a.end);
1388
1461
 
1389
1462
  arcLayer
1390
1463
  .append("path")