@clarify/edge-router 0.1.0 → 0.2.0

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.mjs CHANGED
@@ -883,11 +883,6 @@ function stampRoutePoint(v) {
883
883
  if (!v.isConnPoint) pt.vn = v.vn;
884
884
  return pt;
885
885
  }
886
- //! @brief The ConnRef class represents a connector object.
887
- //!
888
- //! Connectors are a (possible multi-segment) line between two points.
889
- //! They are routed intelligently so as not to overlap any of the shape
890
- //! objects in the Router scene.
891
886
  var Edge = class {
892
887
  router;
893
888
  /** @internal */ _id;
@@ -927,33 +922,22 @@ var Edge = class {
927
922
  this.updateEndPoint("src", src);
928
923
  this.updateEndPoint("tar", dst);
929
924
  }
930
- //! @brief Returns a reference to the current raw "debug" route for the connector.
931
925
  route() {
932
926
  return this._route;
933
927
  }
934
- //! @brief Returns a reference to the current display version of the route.
935
928
  displayRoute() {
936
929
  if (this._displayRoute.empty()) this._displayRoute = this._route.simplify();
937
930
  return this._displayRoute;
938
931
  }
939
- //! @brief Returns ConnEnds specifying what this connector is attached to.
940
932
  endpointConnEnds() {
941
933
  let source = this.getConnEndForEndpointVertex(this._srcVert);
942
934
  let destination = this.getConnEndForEndpointVertex(this._dstVert);
943
935
  if (source && destination) return [source, destination];
944
936
  return null;
945
937
  }
946
- //! @brief Returns the obstacle anchors (shape or junction) at each end of
947
- //! this connector. Either element is null when that endpoint is a
948
- //! free-floating point rather than attached to an anchor.
949
- //!
950
- //! Mirrors the C++ `ConnRef::endpointAnchors()` (which returns a pair of
951
- //! Obstacle pointers). Used by HyperedgeRerouter to walk hyperedge topology.
952
938
  endpointAnchors() {
953
939
  return [this._srcConnend ? this._srcConnend._anchorObj : null, this._dstConnend ? this._dstConnend._anchorObj : null];
954
940
  }
955
- //! @brief Allows the user to specify a set of checkpoints that this
956
- //! connector will route via.
957
941
  setRoutingCheckpoints(checkpoints) {
958
942
  this._checkpoints = checkpoints;
959
943
  for (const vert of this._checkpointVertices) {
@@ -971,32 +955,22 @@ var Edge = class {
971
955
  this._checkpointVertices.push(vertex);
972
956
  }
973
957
  }
974
- //! @brief Returns the current set of routing checkpoints for this connector.
975
958
  routingCheckpoints() {
976
959
  return this._checkpoints;
977
960
  }
978
- //! @brief Returns candidate destination endpoint positions used by the
979
- //! orthogonal-routing "only-turn-when-aligned" optimisation.
980
- //!
981
- //! Since shape-pin endpoints are not supported, this is just the junction
982
- //! position (or empty for free-floating endpoints).
983
961
  possibleDstPinPoints() {
984
962
  if (this._dstConnend && this._dstConnend.junction()) return [this._dstConnend.position()];
985
963
  return [];
986
964
  }
987
- //! @brief Sets the hat-crossings flag.
988
965
  setHateCrossings(val) {
989
966
  this._hateCrossings = val;
990
967
  }
991
- //! @brief Returns the hat-crossings flag.
992
968
  doesHateCrossings() {
993
969
  return this._hateCrossings;
994
970
  }
995
- //! @brief Sets the route directly (internal use).
996
971
  set_route(route) {
997
972
  this._displayRoute.ps = route.ps.slice();
998
973
  }
999
- //! @brief Generates the path for this connector.
1000
974
  generatePath() {
1001
975
  if (!this._needsRerouteFlag) return false;
1002
976
  if (!this._dstVert || !this._srcVert) return false;
@@ -1222,9 +1196,6 @@ function midVertexNumber(p0, p1, c) {
1222
1196
  }
1223
1197
  return 8;
1224
1198
  }
1225
- //! @brief Break up overlapping parallel segments that are not the same edge
1226
- //! in the visibility graph, i.e., where one segment is a subsegment
1227
- //! of another.
1228
1199
  function splitBranchingSegments(poly, polyIsConn, conn, tolerance = 0) {
1229
1200
  let i = 1;
1230
1201
  while (i < conn.ps.length) {
@@ -1623,8 +1594,6 @@ var Direction = class Direction extends OptionSet {
1623
1594
  static inline = new Direction([Direction.inlineStart, Direction.inlineEnd]);
1624
1595
  static all = new Direction([Direction.block, Direction.inline]);
1625
1596
  };
1626
- //! A point that a connector route must visit, with optional arrival/departure
1627
- //! direction constraints.
1628
1597
  var Checkpoint = class {
1629
1598
  point;
1630
1599
  arrivalDirections;
@@ -1635,8 +1604,6 @@ var Checkpoint = class {
1635
1604
  this.departureDirections = departureDirections;
1636
1605
  }
1637
1606
  };
1638
- //! Represents one endpoint of a connector — either a free-floating point or
1639
- //! a junction anchor. Shape-pin endpoints are not supported in this port.
1640
1607
  var Endpoint = class {
1641
1608
  /** @internal */ _type;
1642
1609
  /** @internal */ _point;
@@ -1761,9 +1728,6 @@ var VertInf = class {
1761
1728
  this.isTarEndpoint = roles.isTarEndpoint ?? false;
1762
1729
  this.isDimensionChangePartner = roles.isDimensionChangePartner ?? false;
1763
1730
  }
1764
- //! Replace identity (objID + vn), position, and optionally role flags.
1765
- //! Use `resetPoint` if only the position needs updating (e.g. when a
1766
- //! shape moves but keeps the same corner indices).
1767
1731
  resetIdAndPoint(objID, vn, point, roles) {
1768
1732
  this.objID = objID;
1769
1733
  this.vn = vn;
@@ -1771,7 +1735,6 @@ var VertInf = class {
1771
1735
  this.point.vn = vn;
1772
1736
  if (roles) this.applyRoles(roles);
1773
1737
  }
1774
- //! Update position only; identity (objID + vn) and roles are preserved.
1775
1738
  resetPoint(point) {
1776
1739
  this.point = new Point(point.x, point.y);
1777
1740
  this.point.vn = this.vn;
@@ -1965,10 +1928,6 @@ var Obstacle = class {
1965
1928
  this._lastVert.shNext = firstVertex;
1966
1929
  this._firstVert.shPrev = lastVertex;
1967
1930
  }
1968
- //! Returns the anchor's centre vertex if it has one — junctions do, shapes
1969
- //! do not. Used by HyperedgeRerouter to resolve a junction-anchored
1970
- //! endpoint into a routable vertex. Default implementation returns null
1971
- //! (suitable for ShapeRef); JunctionRef overrides.
1972
1931
  centreVertex() {
1973
1932
  return null;
1974
1933
  }
@@ -2026,7 +1985,6 @@ var Obstacle = class {
2026
1985
  };
2027
1986
  //#endregion
2028
1987
  //#region src/junction.ts
2029
- //! Represents a fixed or free-floating junction between connectors.
2030
1988
  var Junction = class Junction extends Obstacle {
2031
1989
  followingConns = /* @__PURE__ */ new Set();
2032
1990
  _position;
@@ -2309,14 +2267,6 @@ function processShiftEvent(scanline, e, dim, pass) {
2309
2267
  if (!scanline.erase(v)) throw new Error("processShiftEvent: failed to erase Node from scanline");
2310
2268
  }
2311
2269
  }
2312
- //! @brief Determines the spatial limits for each shift segment in the given
2313
- //! dimension. Mirrors the C++ `buildOrthogonalChannelInfo`.
2314
- //!
2315
- //! For each event scan line we run four passes:
2316
- //! 1) Close handlers (limits from current neighbours)
2317
- //! 2) Remove closing events from the scanline
2318
- //! 3) Add opening events to the scanline
2319
- //! 4) Open handlers (limits from new neighbours)
2320
2270
  function buildOrthogonalChannelInfo(router, dim, segmentList) {
2321
2271
  if (segmentList.length === 0) return;
2322
2272
  const altDim = 1 - dim;
@@ -2358,12 +2308,6 @@ function buildOrthogonalChannelInfo(router, dim, segmentList) {
2358
2308
  }
2359
2309
  if (scanline.size() !== 0) throw new Error("buildOrthogonalChannelInfo: scanline non-empty after sweep");
2360
2310
  }
2361
- //! @brief For each orthogonal connector, mark every (segment, point) pair
2362
- //! where a routing checkpoint lies on the route. Each match is
2363
- //! appended to displayRoute.checkpointsOnRoute as [segmentIndex, pt].
2364
- //!
2365
- //! The `segmentIndex` is `ind * 2` for a checkpoint that lands on a bendpoint
2366
- //! and `(ind * 2) - 1` for one that lies along a segment.
2367
2311
  function buildConnectorRouteCheckpointCache(router) {
2368
2312
  for (const conn of router.connRefs) {
2369
2313
  const displayRoute = conn.displayRoute();
@@ -2377,8 +2321,6 @@ function buildConnectorRouteCheckpointCache(router) {
2377
2321
  }
2378
2322
  }
2379
2323
  }
2380
- //! @brief Clear the per-route checkpoint cache built by
2381
- //! buildConnectorRouteCheckpointCache.
2382
2324
  function clearConnectorRouteCheckpointCache(router) {
2383
2325
  for (const conn of router.connRefs) conn.displayRoute().checkpointsOnRoute = [];
2384
2326
  }
@@ -2386,8 +2328,6 @@ function clearConnectorRouteCheckpointCache(router) {
2386
2328
  //#region src/vpsc.ts
2387
2329
  const ZERO_UPPERBOUND = -1e-10;
2388
2330
  const LAGRANGIAN_TOLERANCE = -1e-4;
2389
- //! Thrown when satisfy() cannot resolve the constraint set (e.g. an equality
2390
- //! cycle). `path` is the active path of constraints that caused the failure.
2391
2331
  var UnsatisfiableError = class extends Error {
2392
2332
  path;
2393
2333
  constructor(path = []) {
@@ -2410,8 +2350,6 @@ var PositionStats = class {
2410
2350
  this.A2 += wi * ai * ai;
2411
2351
  }
2412
2352
  };
2413
- //! A variable to be positioned, with a desired position and a weight that
2414
- //! controls how strongly it resists being moved away from that position.
2415
2353
  var Variable = class {
2416
2354
  id;
2417
2355
  desiredPosition;
@@ -2430,7 +2368,6 @@ var Variable = class {
2430
2368
  this.weight = weight;
2431
2369
  this.scale = scale;
2432
2370
  }
2433
- //! Derivative of the local cost wrt this variable's position.
2434
2371
  dfdv() {
2435
2372
  return 2 * this.weight * (this.position() - this.desiredPosition);
2436
2373
  }
@@ -2447,8 +2384,6 @@ var Variable = class {
2447
2384
  return b.posn + this.offset;
2448
2385
  }
2449
2386
  };
2450
- //! A separation (or equality) constraint between two Variables of the form
2451
- //! `right.position - left.position >= gap` (or `==` when equality is true).
2452
2387
  var Constraint = class {
2453
2388
  left;
2454
2389
  right;
@@ -2465,9 +2400,6 @@ var Constraint = class {
2465
2400
  this.gap = gap;
2466
2401
  this.equality = equality;
2467
2402
  }
2468
- //! How much slack the constraint has, i.e. `right - left - gap`. Negative
2469
- //! values mean the constraint is violated. Returns Number.MAX_VALUE when
2470
- //! the constraint has been flagged unsatisfiable.
2471
2403
  slack() {
2472
2404
  if (this.unsatisfiable) return Number.MAX_VALUE;
2473
2405
  if (this.needsScaling) return this.right.scale * this.right.position() - this.gap - this.left.scale * this.left.position();
@@ -2536,9 +2468,6 @@ var ConstraintHeap = class {
2536
2468
  }
2537
2469
  }
2538
2470
  };
2539
- //! A connected component of variables linked by active constraints. Inside a
2540
- //! block all variables move together; the block's `posn` is the optimum of
2541
- //! the unconstrained sub-problem for those variables.
2542
2471
  var Block = class Block {
2543
2472
  vars = [];
2544
2473
  posn = 0;
@@ -2564,8 +2493,6 @@ var Block = class Block {
2564
2493
  this.posn = (this.ps.AD - this.ps.AB) / this.ps.A2;
2565
2494
  if (Number.isNaN(this.posn)) throw new Error("VPSC: Block.posn became NaN");
2566
2495
  }
2567
- //! Recompute posn from the current variable offsets (called after a merge
2568
- //! or moveBlocks pass).
2569
2496
  updateWeightedPosition() {
2570
2497
  this.ps.AB = 0;
2571
2498
  this.ps.AD = 0;
@@ -2591,9 +2518,6 @@ var Block = class Block {
2591
2518
  }
2592
2519
  return h;
2593
2520
  }
2594
- //! Merge two blocks across constraint c (selecting which side is `this`
2595
- //! and which is the merged-in block by size, to keep the operation
2596
- //! amortised cheap).
2597
2521
  mergeWith(b, c) {
2598
2522
  const dist = c.right.offset - c.left.offset - c.gap;
2599
2523
  const l = c.left.block;
@@ -2602,8 +2526,6 @@ var Block = class Block {
2602
2526
  else l.mergeInto(r, c, -dist);
2603
2527
  return b.deleted ? this : b;
2604
2528
  }
2605
- //! Merge b into this block across c. All variables of b are shifted by
2606
- //! `dist` so that c is satisfied as an equality.
2607
2529
  /** @internal */
2608
2530
  mergeInto(b, c, dist) {
2609
2531
  c.active = true;
@@ -2615,7 +2537,6 @@ var Block = class Block {
2615
2537
  if (Number.isNaN(this.posn)) throw new Error("VPSC: Block.posn became NaN");
2616
2538
  b.deleted = true;
2617
2539
  }
2618
- //! Move the contents of b's in-heap into this block's in-heap.
2619
2540
  mergeIn(b) {
2620
2541
  this.findMinInConstraint();
2621
2542
  b.findMinInConstraint();
@@ -2714,16 +2635,12 @@ var Block = class Block {
2714
2635
  this.reset_active_lm(c.left, v);
2715
2636
  }
2716
2637
  }
2717
- //! Find the constraint with the smallest Lagrange multiplier — the one
2718
- //! most "wanting" to split.
2719
2638
  findMinLM() {
2720
2639
  const holder = { c: null };
2721
2640
  this.reset_active_lm(this.vars[0], null);
2722
2641
  this.compute_dfdv(this.vars[0], null, holder);
2723
2642
  return holder.c;
2724
2643
  }
2725
- //! Like findMinLM but restricted to the active path between two specific
2726
- //! variables. Throws UnsatisfiableError when no such path exists.
2727
2644
  findMinLMBetween(lv, rv) {
2728
2645
  this.reset_active_lm(this.vars[0], null);
2729
2646
  this.compute_dfdv(this.vars[0], null, null);
@@ -2741,8 +2658,6 @@ var Block = class Block {
2741
2658
  for (const c of v.in) if (this.canFollowLeft(c, u)) this.populateSplitBlock(b, c.left, v);
2742
2659
  for (const c of v.out) if (this.canFollowRight(c, u)) this.populateSplitBlock(b, c.right, v);
2743
2660
  }
2744
- //! Active path between u and v in the block's active-constraint tree.
2745
- //! Result is pushed into `path` (in reverse order; matches C++).
2746
2661
  getActivePathBetween(path, u, v, w) {
2747
2662
  if (u === v) return true;
2748
2663
  for (const c of u.in) if (this.canFollowLeft(c, w)) {
@@ -2759,8 +2674,6 @@ var Block = class Block {
2759
2674
  }
2760
2675
  return false;
2761
2676
  }
2762
- //! Search the active tree from u for a directed path of `right` traversals
2763
- //! to v.
2764
2677
  isActiveDirectedPathBetween(u, v) {
2765
2678
  if (u === v) return true;
2766
2679
  for (const c of u.out) if (this.canFollowRight(c, null)) {
@@ -2778,8 +2691,6 @@ var Block = class Block {
2778
2691
  }
2779
2692
  return false;
2780
2693
  }
2781
- //! Split this block across the violated path between vl and vr. Sets `lb`
2782
- //! and `rb` (returned via a 2-tuple) and marks this block as deleted.
2783
2694
  splitBetween(vl, vr) {
2784
2695
  const c = this.findMinLMBetween(vl, vr);
2785
2696
  const { l, r } = this.split(c);
@@ -2790,7 +2701,6 @@ var Block = class Block {
2790
2701
  r
2791
2702
  };
2792
2703
  }
2793
- //! Split into two new blocks across c (which becomes inactive).
2794
2704
  split(c) {
2795
2705
  c.active = false;
2796
2706
  const l = new Block(this.blocks);
@@ -2802,7 +2712,6 @@ var Block = class Block {
2802
2712
  r
2803
2713
  };
2804
2714
  }
2805
- //! Sum of squared distances from desired positions, weighted.
2806
2715
  cost() {
2807
2716
  let c = 0;
2808
2717
  for (const v of this.vars) {
@@ -2812,8 +2721,6 @@ var Block = class Block {
2812
2721
  return c;
2813
2722
  }
2814
2723
  };
2815
- //! Container for all current Blocks. Owns the timestamp counter used by the
2816
- //! priority queues to detect out-of-date heap entries.
2817
2724
  var Blocks = class {
2818
2725
  blockTimeCtr = 0;
2819
2726
  m_blocks = [];
@@ -2833,7 +2740,6 @@ var Blocks = class {
2833
2740
  insert(block) {
2834
2741
  this.m_blocks.push(block);
2835
2742
  }
2836
- //! Topological order on variables derived from the constraint DAG.
2837
2743
  totalOrder() {
2838
2744
  const order = [];
2839
2745
  for (const v of this.vs) v.visited = false;
@@ -2845,8 +2751,6 @@ var Blocks = class {
2845
2751
  for (const c of v.out) if (!c.right.visited) this.dfsVisit(c.right, order);
2846
2752
  order.push(v);
2847
2753
  }
2848
- //! Process incoming constraints, most violated to least, merging with
2849
- //! left-neighbour blocks until no more violated in-constraints remain.
2850
2754
  mergeLeft(r) {
2851
2755
  r.timeStamp = ++this.blockTimeCtr;
2852
2756
  r.setUpInConstraints();
@@ -2893,7 +2797,6 @@ var Blocks = class {
2893
2797
  removeBlock(doomed) {
2894
2798
  doomed.deleted = true;
2895
2799
  }
2896
- //! Drop deleted blocks from the live list.
2897
2800
  cleanup() {
2898
2801
  let i = 0;
2899
2802
  for (let j = 0; j < this.m_blocks.length; j++) {
@@ -2903,9 +2806,6 @@ var Blocks = class {
2903
2806
  }
2904
2807
  this.m_blocks.length = i;
2905
2808
  }
2906
- //! Split block b across c, then run mergeLeft / mergeRight to consume any
2907
- //! violations created by the split. Returns the two surviving blocks (but
2908
- //! note: they may have been further merged during the rebalance).
2909
2809
  split(b, c) {
2910
2810
  const { l, r } = b.split(c);
2911
2811
  this.m_blocks.push(l);
@@ -2923,16 +2823,12 @@ var Blocks = class {
2923
2823
  r: rPrime
2924
2824
  };
2925
2825
  }
2926
- //! Total squared cost across all blocks.
2927
2826
  cost() {
2928
2827
  let c = 0;
2929
2828
  for (const b of this.m_blocks) c += b.cost();
2930
2829
  return c;
2931
2830
  }
2932
2831
  };
2933
- //! Incremental VPSC solver. Construct with the variable and constraint
2934
- //! lists, then call `satisfy()` to find a feasible solution or `solve()` to
2935
- //! drive cost to a local minimum.
2936
2832
  var IncSolver = class {
2937
2833
  splitCnt = 0;
2938
2834
  bs;
@@ -2962,11 +2858,9 @@ var IncSolver = class {
2962
2858
  this.inactive = cs.slice();
2963
2859
  for (const c of this.inactive) c.active = false;
2964
2860
  }
2965
- //! Returns the current Blocks container (mainly for tests / debugging).
2966
2861
  getBlocks() {
2967
2862
  return this.bs;
2968
2863
  }
2969
- //! Returns the variable list (read-only by convention).
2970
2864
  getVariables() {
2971
2865
  return this.vs;
2972
2866
  }
@@ -2984,8 +2878,6 @@ var IncSolver = class {
2984
2878
  if (Number.isNaN(v.finalPosition)) throw new Error("VPSC: variable finalPosition NaN");
2985
2879
  }
2986
2880
  }
2987
- //! Drive cost(): satisfy() + while-cost-decreases-loop. Returns true if
2988
- //! any blocks remain merged at the end (the C++ contract).
2989
2881
  solve() {
2990
2882
  this.satisfy();
2991
2883
  let lastCost = Number.MAX_VALUE;
@@ -3103,12 +2995,6 @@ const freeWeight$1 = 1e-5;
3103
2995
  const strongWeight = .001;
3104
2996
  const strongerWeight = 1;
3105
2997
  const fixedWeight$1 = 1e5;
3106
- //! The concrete ShiftSegment implementation used during orthogonal nudging.
3107
- //!
3108
- //! Each instance represents a segment of a connector's display route that
3109
- //! may be shifted along `dimension`. Construction takes the low and high
3110
- //! route-index of the segment endpoints; the actual coordinates are read
3111
- //! lazily from `connRef.displayRoute().ps`.
3112
2998
  var NudgingShiftSegment = class {
3113
2999
  connRef;
3114
3000
  /** @internal */ variable;
@@ -3169,10 +3055,6 @@ var NudgingShiftSegment = class {
3169
3055
  get zBend() {
3170
3056
  return this._zBend;
3171
3057
  }
3172
- //! Assign a VPSC Variable describing this segment's desired position and
3173
- //! how strongly it resists moving. The `justUnifying` flag distinguishes
3174
- //! the unifying preprocessing pass from the main nudging pass — see
3175
- //! ImproveOrthogonalRoutes.execute.
3176
3058
  createSolverVariable(justUnifying) {
3177
3059
  const nudgeFinalSegments = this.connRef.router.options.nudgeOrthogonalSegmentsConnectedToShapes;
3178
3060
  let varID = freeSegmentID$1;
@@ -3192,7 +3074,6 @@ var NudgingShiftSegment = class {
3192
3074
  } else if (!this.finalSegment) weight = strongWeight;
3193
3075
  this.variable = new Variable(varID, varPos, weight);
3194
3076
  }
3195
- //! Push the solver's chosen position back into the route.
3196
3077
  updatePositionsFromSolver(_justUnifying) {
3197
3078
  if (this.fixed) return;
3198
3079
  let newPos = this.variable.finalPosition;
@@ -3228,10 +3109,6 @@ var NudgingShiftSegment = class {
3228
3109
  if (this._highC()) return 1;
3229
3110
  return 0;
3230
3111
  }
3231
- //! Returns true if `rhs` shares an overlap with this segment such that the
3232
- //! two need to be constrained against each other in the nudger. Counts
3233
- //! collinear-sharing-endpoint pairs as overlapping so they can be nudged
3234
- //! apart where possible.
3235
3112
  overlapsWith(rhsSuper, dim) {
3236
3113
  const rhs = rhsSuper;
3237
3114
  const altDim = (dim + 1) % 2;
@@ -3309,9 +3186,6 @@ var NudgingShiftSegment = class {
3309
3186
  };
3310
3187
  //#endregion
3311
3188
  //#region src/utilities/pos-vert-inf.ts
3312
- //! A breakpoint along a candidate visibility segment — a position on the
3313
- //! perpendicular axis combined with the vertex (which may be a shape corner,
3314
- //! a connection point, or a dummy lattice vertex) at that position.
3315
3189
  function sameId(a, b) {
3316
3190
  return a.objID === b.objID && a.vn === b.vn;
3317
3191
  }
@@ -3446,11 +3320,6 @@ function getPosVertInfDirections(v, dim) {
3446
3320
  }
3447
3321
  return 0;
3448
3322
  }
3449
- //! A candidate horizontal/vertical visibility segment produced during sweep.
3450
- //! Stores its extent along the segment axis (`begin`..`finish`) and its
3451
- //! perpendicular position (`pos`), plus a sorted set of vertices that lie on
3452
- //! it (`vertInfs`) and intersection break-points discovered during the cross
3453
- //! sweep (`breakPoints`).
3454
3323
  var LineSegment = class {
3455
3324
  begin;
3456
3325
  finish;
@@ -3654,10 +3523,6 @@ var LineSegment = class {
3654
3523
  };
3655
3524
  //#endregion
3656
3525
  //#region src/utilities/segment-list-wrapper.ts
3657
- //! A list of LineSegments with overlap-merging insertion. The list keeps the
3658
- //! invariant that no two segments overlap; on insertion a new segment that
3659
- //! overlaps existing ones is merged into the first overlap, and subsequent
3660
- //! overlapping segments are merged into the first and removed.
3661
3526
  var SegmentListWrapper = class {
3662
3527
  /** @internal */ _list = [];
3663
3528
  insert(segment) {
@@ -3708,8 +3573,6 @@ const channelLeftID = 2;
3708
3573
  const channelRightID = 3;
3709
3574
  const freeWeight = 1e-5;
3710
3575
  const fixedWeight = 1e5;
3711
- //! A pair of unsigned values that can be compared and used as keys. The
3712
- //! smaller index is stored first so {a,b} and {b,a} compare equal.
3713
3576
  var UnsignedPair = class {
3714
3577
  _index1;
3715
3578
  _index2;
@@ -3744,8 +3607,6 @@ var UnsignedPairSet = class {
3744
3607
  return this._items.size;
3745
3608
  }
3746
3609
  };
3747
- //! Comparator used when merging NudgingShiftSegments — sorts route indexes by
3748
- //! point position in `dimension`.
3749
3610
  var CmpIndexes = class {
3750
3611
  connRef;
3751
3612
  dimension;
@@ -3759,12 +3620,6 @@ var CmpIndexes = class {
3759
3620
  return a < b ? -1 : a > b ? 1 : 0;
3760
3621
  }
3761
3622
  };
3762
- //! Given a router and a list of possible horizontal segments, plus a possible
3763
- //! vertical visibility segment, compute and add edges to the orthogonal
3764
- //! visibility graph for all visibility edges.
3765
- //!
3766
- //! Modifies `segments` in place — segments whose finish position is behind
3767
- //! the vertical line are committed and removed.
3768
3623
  function intersectSegments(router, segments, vertLine) {
3769
3624
  if (segments.length === 0) {
3770
3625
  vertLine.generateVisibilityEdgesFromBreakpointSet(router, 1);
@@ -3934,10 +3789,6 @@ function processEventHori(router, scanline, segments, e, pass) {
3934
3789
  if (!scanline.erase(v)) throw new Error("orthogonal: failed to erase Node from scanline");
3935
3790
  }
3936
3791
  }
3937
- //! Pins or connector endpoints sitting on the leading/trailing edge of the
3938
- //! visibility graph may only have visibility in the outward direction (away
3939
- //! from the graph). Add the supplied visibility flags so they can still be
3940
- //! reached.
3941
3792
  function fixConnectionPointVisibilityOnOutsideOfVisibilityGraph(events, addedVisibility) {
3942
3793
  const totalEvents = events.length;
3943
3794
  if (totalEvents === 0) return;
@@ -3955,12 +3806,6 @@ function fixConnectionPointVisibilityOnOutsideOfVisibilityGraph(events, addedVis
3955
3806
  if (events[revIndex].v.c) events[revIndex].v.c.visDirections = events[revIndex].v.c.visDirections.add(addedVisibility);
3956
3807
  }
3957
3808
  }
3958
- //! @brief Build the static orthogonal visibility graph for the router.
3959
- //!
3960
- //! Runs two sweepline passes (one per dimension) over every obstacle corner,
3961
- //! junction corner (when fixed), and connector endpoint, populating
3962
- //! `router.visOrthogGraph` with the lattice of horizontal/vertical visibility
3963
- //! edges and tagging every vertex with XL_*/XH_*/YL_*/YH_* property flags.
3964
3809
  function generateStaticOrthogonalVisGraph(router) {
3965
3810
  let events = [];
3966
3811
  for (const obstacle of router.m_obstacles) {
@@ -4150,9 +3995,6 @@ function buildOrthogonalNudgingSegments(router, dim, segmentList) {
4150
3995
  }
4151
3996
  }
4152
3997
  }
4153
- //! Comparator for the line-order sort used during nudging. Orders segments
4154
- //! that share a position by their PtOrder map ranking, falling back to
4155
- //! fixedOrder / c-bend order for fixed and c-bend segments.
4156
3998
  var CmpLineOrder = class {
4157
3999
  orders;
4158
4000
  dimension;
@@ -4160,8 +4002,6 @@ var CmpLineOrder = class {
4160
4002
  this.orders = orders;
4161
4003
  this.dimension = dimension;
4162
4004
  }
4163
- //! Returns `{ lessThan, comparable }`. `comparable` is false when the two
4164
- //! segments can't be ordered against each other.
4165
4005
  compare(lhsSuper, rhsSuper) {
4166
4006
  const lhs = lhsSuper;
4167
4007
  const rhs = rhsSuper;
@@ -4244,9 +4084,6 @@ function linesort(nudgeFinalSegments, origList, comparison) {
4244
4084
  }
4245
4085
  return resultList;
4246
4086
  }
4247
- //! A pair of variable indices marking a candidate "potential" equality
4248
- //! constraint between two free segments. Used in the unifying pass to
4249
- //! greedily glue free segments to the same position.
4250
4087
  var PotentialSegmentConstraint = class {
4251
4088
  index1;
4252
4089
  index2;
@@ -4490,25 +4327,13 @@ var ImproveOrthogonalRoutes = class {
4490
4327
  };
4491
4328
  //#endregion
4492
4329
  //#region src/hyperedgetree.ts
4493
- //! Map from a junction to the tree node that represents it.
4494
- //! Map from a VertInf to the tree node it became.
4495
- //! @brief A node in the hyperedge tree.
4496
- //!
4497
- //! Carries a position, optional junction, optional VertInf marker (used to
4498
- //! identify terminals), and the list of edges incident on this node.
4499
4330
  var HyperedgeTreeNode = class {
4500
4331
  edges;
4501
4332
  junction;
4502
4333
  point;
4503
- //! VertInf representing the original endpoint of a connector that landed
4504
- //! at this node. Used by buildHyperedgeTreeToRoot to remember terminals.
4505
4334
  finalVertex;
4506
- //! True if this terminal node is the source of its connector (vs target).
4507
4335
  isConnectorSource;
4508
- //! True if this node is the extra dummy endpoint used for connection-pin
4509
- //! routing — removed from the final route when present.
4510
4336
  isPinDummyEndpoint;
4511
- //! Used during recursive traversals to detect cycles.
4512
4337
  visited;
4513
4338
  constructor() {
4514
4339
  this.edges = [];
@@ -4519,20 +4344,9 @@ var HyperedgeTreeNode = class {
4519
4344
  this.isPinDummyEndpoint = false;
4520
4345
  this.visited = false;
4521
4346
  }
4522
- //! Traverses the tree and writes each branch's path back to its connector's
4523
- //! display route. `pass === 0` clears each connector's route; `pass === 1`
4524
- //! actually writes the points.
4525
4347
  writeEdgesToConns(ignored, pass, setRouteOps) {
4526
4348
  for (const curr of this.edges) if (curr !== ignored) curr.writeEdgesToConns(this, pass, setRouteOps);
4527
4349
  }
4528
- //! Traverses the tree from a junction outwards, creating fresh ConnRefs
4529
- //! for each branch. Each new connector's src endpoint is the junction
4530
- //! we started at; tar endpoint is set when we reach the next junction or
4531
- //! a terminal.
4532
- //!
4533
- //! The caller supplies a `connFactory` that creates a fresh connector
4534
- //! attached to the supplied junction; this keeps hyperedgetree.ts free of
4535
- //! a real ConnRef dependency.
4536
4350
  addConns(ignored, router, oldConns, conn, connFactory, setConnEndAtTerminal, setConnEndAtJunction) {
4537
4351
  if (!conn && !this.junction) throw new Error("HyperedgeTreeNode.addConns: conn or junction required");
4538
4352
  for (const curr of this.edges) if (curr !== ignored) {
@@ -4542,12 +4356,6 @@ var HyperedgeTreeNode = class {
4542
4356
  curr.addConns(this, router, oldConns, connFactory, setConnEndAtTerminal, setConnEndAtJunction);
4543
4357
  }
4544
4358
  }
4545
- //! Traverses the tree and rewrites connector ConnEnds whose junction
4546
- //! may have changed during hyperedge improvement. `forward` tracks the
4547
- //! traversal direction relative to a connector's src/tar orientation.
4548
- //!
4549
- //! The caller supplies `getEnds`, `getEndJunction`, and
4550
- //! `setEndToJunction` callbacks so we don't depend on ConnRef directly.
4551
4359
  updateConnEnds(ignored, forward, changedConns, getEnds, setEndToJunction) {
4552
4360
  for (const edge of this.edges) {
4553
4361
  if (edge === ignored) continue;
@@ -4563,42 +4371,27 @@ var HyperedgeTreeNode = class {
4563
4371
  edge.updateConnEnds(this, next, changedConns, getEnds, setEndToJunction);
4564
4372
  }
4565
4373
  }
4566
- //! Traverses the tree and accumulates the JunctionRefs and ConnRefs that
4567
- //! make up the hyperedge. Used by HyperedgeImprover to know what objects
4568
- //! are affected by a re-route.
4569
4374
  listJunctionsAndConnectors(ignored, junctions, connectors) {
4570
4375
  if (this.junction) junctions.push(this.junction);
4571
4376
  for (const curr of this.edges) if (curr !== ignored) curr.listJunctionsAndConnectors(this, junctions, connectors);
4572
4377
  }
4573
- //! Returns true if this node cannot be moved during hyperedge improvement.
4574
- //! Conditions: it has only one edge (a terminal), its junction is fixed.
4575
4378
  isImmovable() {
4576
4379
  if (this.edges.length === 1 || this.junction && this.junction.positionFixed()) return true;
4577
4380
  return false;
4578
4381
  }
4579
- //! Recursively deletes the subtree on the other side of every edge
4580
- //! except `ignored`. Used to tear down a hyperedge tree. TS doesn't
4581
- //! need to free anything; we simply detach edges so GC can collect them.
4582
4382
  deleteEdgesExcept(ignored) {
4583
4383
  for (const curr of this.edges) if (curr !== ignored) curr.deleteNodesExcept(this);
4584
4384
  this.edges = [];
4585
4385
  }
4586
- //! Removes a specific edge from this node's edge list (all occurrences).
4587
4386
  disconnectEdge(edge) {
4588
4387
  for (let i = this.edges.length - 1; i >= 0; i--) if (this.edges[i] === edge) this.edges.splice(i, 1);
4589
4388
  }
4590
- //! Moves every edge attached to `oldNode` to be attached to this node
4591
- //! instead. Used when collapsing two coincident nodes into one.
4592
4389
  spliceEdgesFrom(oldNode) {
4593
4390
  if (oldNode === this) throw new Error("HyperedgeTreeNode.spliceEdgesFrom: cannot splice from self");
4594
4391
  while (oldNode.edges.length > 0) oldNode.edges[0].replaceNode(oldNode, this);
4595
4392
  }
4596
4393
  };
4597
- //! @brief An edge in the hyperedge tree. Connects exactly two nodes
4598
- //! and tracks the connector this segment will become.
4599
4394
  var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4600
- //! Pair of endpoint nodes; conventionally `first` is the "from" side
4601
- //! when traversing. Either can be null briefly during disconnect.
4602
4395
  ends;
4603
4396
  conn;
4604
4397
  constructor(node1, node2, conn) {
@@ -4607,22 +4400,16 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4607
4400
  node1.edges.push(this);
4608
4401
  node2.edges.push(this);
4609
4402
  }
4610
- //! Returns the node at the other end from `from`.
4611
4403
  followFrom(from) {
4612
4404
  if (this.ends[0] === from) return this.ends[1];
4613
4405
  return this.ends[0];
4614
4406
  }
4615
- //! True when both endpoints share the same position.
4616
4407
  zeroLength() {
4617
4408
  return this.ends[0].point.eq(this.ends[1].point);
4618
4409
  }
4619
- //! True when both endpoints have the same coordinate along `dimension`
4620
- //! (i.e. the edge is axis-aligned in that dimension).
4621
4410
  hasOrientation(dimension) {
4622
4411
  return this.ends[0].point.at(dimension) === this.ends[1].point.at(dimension);
4623
4412
  }
4624
- //! Replaces `oldNode` with `newNode` on whichever side it appears.
4625
- //! `oldNode` is detached and `newNode` gains the edge in its list.
4626
4413
  replaceNode(oldNode, newNode) {
4627
4414
  if (this.ends[0] === oldNode) {
4628
4415
  oldNode.disconnectEdge(this);
@@ -4634,11 +4421,6 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4634
4421
  this.ends[1] = newNode;
4635
4422
  }
4636
4423
  }
4637
- //! Traverses outward from `ignored`, writing the segment between this
4638
- //! edge's two endpoints into the appropriate connector's display route.
4639
- //!
4640
- //! `pass === 0` clears each connector's route; `pass === 1` writes points.
4641
- //! `setRouteOps` provides connector-side primitives.
4642
4424
  writeEdgesToConns(ignored, pass, setRouteOps) {
4643
4425
  if (this.ends[0] === null || this.ends[1] === null) throw new Error("HyperedgeTreeEdge.writeEdgesToConns: edge has null endpoint");
4644
4426
  if (this.conn === null) throw new Error("HyperedgeTreeEdge.writeEdgesToConns: missing conn");
@@ -4668,9 +4450,6 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4668
4450
  }
4669
4451
  nextNode.writeEdgesToConns(this, pass, setRouteOps);
4670
4452
  }
4671
- //! Traverses outwards through this edge, creating connectors as the
4672
- //! traversal crosses junctions and terminals. See
4673
- //! HyperedgeTreeNode.addConns for the callback shape.
4674
4453
  addConns(ignored, router, oldConns, connFactory, setConnEndAtTerminal, setConnEndAtJunction) {
4675
4454
  if (this.conn === null) throw new Error("HyperedgeTreeEdge.addConns: missing conn");
4676
4455
  let endNode = null;
@@ -4685,9 +4464,6 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4685
4464
  if (endNode && endNode.finalVertex) setConnEndAtTerminal(this.conn, endNode.finalVertex, oldConns);
4686
4465
  else if (endNode && endNode.junction) setConnEndAtJunction(this.conn, endNode.junction);
4687
4466
  }
4688
- //! Traverses outwards, rewriting connector ConnEnds whose junction
4689
- //! has been adjusted by hyperedge improvement. See
4690
- //! HyperedgeTreeNode.updateConnEnds for callback semantics.
4691
4467
  updateConnEnds(ignored, forward, changedConns, getEnds, setEndToJunction) {
4692
4468
  let endNode = null;
4693
4469
  if (this.ends[0] && this.ends[0] !== ignored) {
@@ -4706,20 +4482,11 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4706
4482
  }
4707
4483
  }
4708
4484
  }
4709
- //! Traverses outwards, accumulating the JunctionRefs and ConnRefs in
4710
- //! the hyperedge. Records the connector once per traversal.
4711
4485
  listJunctionsAndConnectors(ignored, junctions, connectors) {
4712
4486
  if (this.conn && !connectors.includes(this.conn)) connectors.push(this.conn);
4713
4487
  if (this.ends[0] !== ignored && this.ends[0]) this.ends[0].listJunctionsAndConnectors(this, junctions, connectors);
4714
4488
  else if (this.ends[1] !== ignored && this.ends[1]) this.ends[1].listJunctionsAndConnectors(this, junctions, connectors);
4715
4489
  }
4716
- //! Splits this edge by inserting a fresh node at `point`. The existing
4717
- //! edge stays attached to `source` on one side and to the new node on
4718
- //! the other; a fresh edge connects the new node to the original target.
4719
- //!
4720
- //! Returns the newly created node so callers can position more edges off
4721
- //! it (the C++ creates the new HyperedgeTreeEdge purely for its side
4722
- //! effect — the constructor wires itself into both endpoint lists).
4723
4490
  splitFromNodeAtPoint(source, point) {
4724
4491
  if (this.ends[1] === source) {
4725
4492
  const tmp = this.ends[0];
@@ -4736,7 +4503,6 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4736
4503
  split.edges.push(this);
4737
4504
  return split;
4738
4505
  }
4739
- //! Detaches this edge from both endpoints and clears its `ends` array.
4740
4506
  disconnectEdge() {
4741
4507
  if (this.ends[0] === null || this.ends[1] === null) throw new Error("HyperedgeTreeEdge.disconnectEdge: edge already disconnected");
4742
4508
  this.ends[0].disconnectEdge(this);
@@ -4744,9 +4510,6 @@ var HyperedgeTreeEdge = class HyperedgeTreeEdge {
4744
4510
  this.ends[0] = null;
4745
4511
  this.ends[1] = null;
4746
4512
  }
4747
- //! Recursively detaches nodes on the other side of this edge (except
4748
- //! `ignored`). TS doesn't need to free anything; we null out the
4749
- //! pointers so GC reclaims the detached subtree.
4750
4513
  deleteNodesExcept(ignored) {
4751
4514
  if (this.ends[0] && this.ends[0] !== ignored) this.ends[0].deleteEdgesExcept(this);
4752
4515
  this.ends[0] = null;
@@ -4835,8 +4598,6 @@ function vertHeapLess(a, b) {
4835
4598
  function edgeHeapLess(a, b) {
4836
4599
  return a.mtstDist() < b.mtstDist();
4837
4600
  }
4838
- //! @brief Builds a minimum terminal spanning tree for a set of terminal
4839
- //! vertices.
4840
4601
  var MinimumTerminalSpanningTree = class {
4841
4602
  router;
4842
4603
  terminals;
@@ -4864,13 +4625,9 @@ var MinimumTerminalSpanningTree = class {
4864
4625
  this.vHeap = new BinaryHeap(vertHeapLess);
4865
4626
  this.beHeap = new BinaryHeap(edgeHeapLess);
4866
4627
  }
4867
- //! Returns the root junction node of the constructed tree (null until
4868
- //! construct{Sequential,Interleaved}() runs).
4869
4628
  rootJunction() {
4870
4629
  return this.m_rootJunction;
4871
4630
  }
4872
- //! Tears down the constructed hyperedge tree subtree (callers may instead
4873
- //! rely on GC; this exists to mirror the C++ destructor).
4874
4631
  dispose() {
4875
4632
  if (this.m_rootJunction) {
4876
4633
  this.m_rootJunction.deleteEdgesExcept(null);
@@ -4897,9 +4654,6 @@ var MinimumTerminalSpanningTree = class {
4897
4654
  this.allsets.splice(lo, 1);
4898
4655
  this.allsets.push(merged);
4899
4656
  }
4900
- //! Returns the HyperedgeTreeNode for a given vertex, creating it if needed.
4901
- //! When two vertices end up at the same node, that node becomes a junction.
4902
- //! Also wires a fresh HyperedgeTreeEdge to `prevNode` if supplied.
4903
4657
  addNode(vertex, prevNode) {
4904
4658
  let node;
4905
4659
  const existing = this.nodes.get(vertex);
@@ -4921,8 +4675,6 @@ var MinimumTerminalSpanningTree = class {
4921
4675
  if (prevNode) new HyperedgeTreeEdge(prevNode, node, null);
4922
4676
  return node;
4923
4677
  }
4924
- //! Walks backwards along pathNext links, materialising HyperedgeTreeNodes
4925
- //! and edges as it goes. Stops when it reaches a junction or terminal.
4926
4678
  buildHyperedgeTreeToRoot(currVert, prevNode, prevVert, markEdges = false) {
4927
4679
  if (prevNode && prevNode.junction) return;
4928
4680
  while (currVert) {
@@ -4943,9 +4695,6 @@ var MinimumTerminalSpanningTree = class {
4943
4695
  currVert = currVert.pathNext;
4944
4696
  }
4945
4697
  }
4946
- //! Resets sptfDist and treeRootPointer for every vertex on the pathNext
4947
- //! chain starting at `currVert`. Returns the old tree-root pointer for
4948
- //! the first vertex that already had sptfDist === 0.
4949
4698
  resetDistsForPath(currVert, newRootVertPtr) {
4950
4699
  if (!currVert) throw new Error("resetDistsForPath: currVert must not be null");
4951
4700
  let cv = currVert;
@@ -4962,8 +4711,6 @@ var MinimumTerminalSpanningTree = class {
4962
4711
  }
4963
4712
  throw new Error("resetDistsForPath: walked off the end");
4964
4713
  }
4965
- //! Recursively marks the rest of an existing hyperedge with the new root
4966
- //! pointer so all vertices on a freshly-committed branch share one root.
4967
4714
  rewriteRestOfHyperedge(vert, newTreeRootPtr) {
4968
4715
  vert.setTreeRootPointer(newTreeRootPtr);
4969
4716
  const edgeList = this.getOrthogonalEdgesFromVertex(vert, null);
@@ -4972,10 +4719,6 @@ var MinimumTerminalSpanningTree = class {
4972
4719
  if (v.sptfDist === 0) this.rewriteRestOfHyperedge(v, newTreeRootPtr);
4973
4720
  }
4974
4721
  }
4975
- //! Creates (if necessary) and returns the dummy orthogonal partner for a
4976
- //! given vertex. The partner sits at the same point but is on a different
4977
- //! "layer" — turning between the two layers costs `penalty` (default
4978
- //! bendPenalty). This models the rectilinear bend cost.
4979
4722
  orthogonalPartner(vert, penalty = 0) {
4980
4723
  if (penalty === 0) penalty = this.bendPenalty;
4981
4724
  if (vert.orthogonalPartner === null) {
@@ -4987,8 +4730,6 @@ var MinimumTerminalSpanningTree = class {
4987
4730
  }
4988
4731
  return vert.orthogonalPartner;
4989
4732
  }
4990
- //! Removes bridging edges from `beHeap` whose endpoints no longer connect
4991
- //! distinct trees or whose roots have been pruned.
4992
4733
  removeInvalidBridgingEdges() {
4993
4734
  const valid = [];
4994
4735
  for (const e of this.beHeap.arr) {
@@ -4999,10 +4740,6 @@ var MinimumTerminalSpanningTree = class {
4999
4740
  }
5000
4741
  this.beHeap.replaceWith(valid);
5001
4742
  }
5002
- //! Returns every orthogonal edge incident on `vert`, paired with the
5003
- //! "partner" vertex on the other side (which may be `vert`'s real
5004
- //! neighbour or its orthogonal partner depending on layer). Pre-computes
5005
- //! the orthogonal partner if not present.
5006
4743
  getOrthogonalEdgesFromVertex(vert, prev) {
5007
4744
  const edgeList = [];
5008
4745
  if (!vert) throw new Error("getOrthogonalEdgesFromVertex: vert required");
@@ -5027,8 +4764,6 @@ var MinimumTerminalSpanningTree = class {
5027
4764
  }
5028
4765
  return edgeList;
5029
4766
  }
5030
- //! Builds the MTST using sequential (Dijkstra-then-Kruskal) construction.
5031
- //! Slower / weaker than the interleaved variant, kept for parity.
5032
4767
  constructSequential() {
5033
4768
  const vHeap = new BinaryHeap(vertHeapLess);
5034
4769
  const beHeap = new BinaryHeap(edgeHeapLess);
@@ -5107,8 +4842,6 @@ var MinimumTerminalSpanningTree = class {
5107
4842
  this.nodes.clear();
5108
4843
  this.allsets.length = 0;
5109
4844
  }
5110
- //! Builds the MTST using interleaved Dijkstra/Kruskal construction —
5111
- //! preferred over the sequential variant.
5112
4845
  constructInterleaved() {
5113
4846
  this.origTerminals = new Set(this.terminals);
5114
4847
  const endVert = this.router.vertices.end();
@@ -5165,9 +4898,6 @@ var MinimumTerminalSpanningTree = class {
5165
4898
  for (const v of this.extraVertices) v.removeFromGraph(false);
5166
4899
  this.extraVertices.length = 0;
5167
4900
  }
5168
- //! Returns true if a new leaf can join an old leaf without introducing a
5169
- //! bend. The exact rules depend on whether the old leaf is itself a
5170
- //! terminal (sptfDist === 0) or a propagated vertex.
5171
4901
  connectsWithoutBend(oldLeaf, newLeaf) {
5172
4902
  if (oldLeaf.sptfDist === 0) {
5173
4903
  let hyperedgeConnection = false;
@@ -5186,8 +4916,6 @@ var MinimumTerminalSpanningTree = class {
5186
4916
  if (oldLeaf.pathNext) return colinear(oldLeaf.pathNext.point, oldLeaf.point, newLeaf.point);
5187
4917
  return true;
5188
4918
  }
5189
- //! Returns the real vertices of a bridging edge, swapping in orthogonal
5190
- //! partners when needed to make orientation comparisons meaningful.
5191
4919
  realVerticesCountingPartners(edge) {
5192
4920
  const v1 = edge.vert1();
5193
4921
  const v2 = edge.vert2();
@@ -5199,9 +4927,6 @@ var MinimumTerminalSpanningTree = class {
5199
4927
  }
5200
4928
  return [r1, r2];
5201
4929
  }
5202
- //! Commits to a particular bridging edge — merges the two terminal trees,
5203
- //! materialises the path on the HyperedgeTree, and rewrites root pointers
5204
- //! so all vertices on the committed branch share a single tree root.
5205
4930
  commitToBridgingEdge(e) {
5206
4931
  const ends = this.realVerticesCountingPartners(e);
5207
4932
  const r1 = ends[0].treeRoot();
@@ -5247,17 +4972,6 @@ var MinimumTerminalSpanningTree = class {
5247
4972
  };
5248
4973
  //#endregion
5249
4974
  //#region src/hyperedge.ts
5250
- //! Lists of junctions and connectors created, deleted, and changed during
5251
- //! a hyperedge rerouting or improvement pass.
5252
- //!
5253
- //! `changedConnectorList` is only populated by HyperedgeImprover — for the
5254
- //! rerouter it always stays empty.
5255
- //! A list of newly created junctions.
5256
- //! A list of newly created connectors.
5257
- //! A list of deleted junctions.
5258
- //! A list of deleted connectors.
5259
- //! A list of changed connectors (HyperedgeImprover only — always empty for
5260
- //! HyperedgeRerouter results).
5261
4975
  function getHyperedgeVertex(end, router) {
5262
4976
  if (end._anchorObj !== null) {
5263
4977
  const centre = end._anchorObj.centreVertex();
@@ -5279,16 +4993,6 @@ function getHyperedgeVertex(end, router) {
5279
4993
  vertex
5280
4994
  };
5281
4995
  }
5282
- //! @brief The HyperedgeRerouter class is a convenience object used to
5283
- //! register hyperedges for full rerouting.
5284
- //!
5285
- //! To work with this class, get a reference via `Router.hyperedgeRerouter()`.
5286
- //!
5287
- //! Registering a hyperedge stages it for rerouting — the rerouting itself
5288
- //! happens the next time `Router.route()` is called, just before connector
5289
- //! routes are generated. After `route()` returns, the caller can read
5290
- //! `newAndDeletedObjectLists(index)` to learn what junctions and connectors
5291
- //! were created and deleted for that hyperedge.
5292
4996
  var HyperedgeRerouter = class {
5293
4997
  router;
5294
4998
  hyperedges;
@@ -5298,11 +5002,6 @@ var HyperedgeRerouter = class {
5298
5002
  m_deleted_connectors_vector = [];
5299
5003
  m_terminal_vertices_vector = [];
5300
5004
  m_added_vertices = [];
5301
- //! How many fresh `VertInf`s `calcHyperedgeConnectors` injected into
5302
- //! `router.vertices` (free-floating terminals whose position+direction
5303
- //! didn't match any existing connector endpoint). Used by `Router.route()`
5304
- //! to decide whether the static vis graph needs rebuilding before MTST
5305
- //! runs: zero added → existing visibility is fine, MTST can start.
5306
5005
  /** @internal */
5307
5006
  addedVertexCount() {
5308
5007
  return this.m_added_vertices.length;
@@ -5311,12 +5010,9 @@ var HyperedgeRerouter = class {
5311
5010
  this.hyperedges = hyperedges;
5312
5011
  this.router = router;
5313
5012
  }
5314
- //! @brief The number of hyperedges that have been registered.
5315
5013
  count() {
5316
5014
  return this.hyperedges.length;
5317
5015
  }
5318
- //! @brief Returns the new/deleted objects for the hyperedge registered at
5319
- //! `index`. Call this after `Router.route()` has run.
5320
5016
  newAndDeletedObjectLists(index) {
5321
5017
  if (index > this.count()) throw new Error(`HyperedgeRerouter: index ${index} out of range (count=${this.count()})`);
5322
5018
  return {
@@ -5402,10 +5098,6 @@ var HyperedgeRerouter = class {
5402
5098
  }
5403
5099
  }
5404
5100
  /** @internal */
5405
- //! Perform the actual rerouting. Called from `Router.route()` before
5406
- //! individual connector routing. Builds an MTST per registered hyperedge,
5407
- //! writes the tree back to fresh ConnRef objects, then deletes the old
5408
- //! junctions and connectors.
5409
5101
  performRerouting() {
5410
5102
  this.m_new_junctions_vector = Array.from({ length: this.count() }, () => []);
5411
5103
  this.m_new_connectors_vector = Array.from({ length: this.count() }, () => []);
@@ -5476,8 +5168,6 @@ function makeSetRouteOps() {
5476
5168
  //#endregion
5477
5169
  //#region src/router.ts
5478
5170
  var Router = class {
5479
- //! Canonical list of every obstacle (shapes + junctions) known to the
5480
- //! router.
5481
5171
  m_obstacles;
5482
5172
  connRefs;
5483
5173
  visOrthogGraph;
@@ -5513,9 +5203,6 @@ var Router = class {
5513
5203
  this._nextObjectId = 1;
5514
5204
  this.m_in_crossing_rerouting_stage = false;
5515
5205
  }
5516
- //! @brief Returns the next unused internal object id and advances the
5517
- //! counter. Used by `Obstacle` and `ConnRef` to tag their owning
5518
- //! `VertInf.objID` and cache keys. Not part of the public API.
5519
5206
  /** @internal */
5520
5207
  _assignObjectId() {
5521
5208
  return this._nextObjectId++;
@@ -5525,49 +5212,30 @@ var Router = class {
5525
5212
  obstable.makeActive();
5526
5213
  return obstable;
5527
5214
  }
5528
- //! @brief Registers a generic obstacle in the router's obstacle list.
5529
- //! Called by Obstacle.makeActive — not part of the public API.
5530
5215
  /** @internal */
5531
5216
  addObstacle(obstacle) {
5532
5217
  this.m_obstacles.push(obstacle);
5533
5218
  }
5534
- //! @brief Removes a generic obstacle. Called by Obstacle.makeInactive.
5535
5219
  /** @internal */
5536
5220
  removeObstacle(obstacle) {
5537
5221
  const idx = this.m_obstacles.indexOf(obstacle);
5538
5222
  if (idx !== -1) this.m_obstacles.splice(idx, 1);
5539
5223
  }
5540
- //! @brief Adds a junction to the router scene. Called from the
5541
- //! JunctionRef constructor.
5542
5224
  addJunction(junction) {
5543
5225
  junction.makeActive();
5544
5226
  }
5545
- //! @brief Remove a connector from the router scene.
5546
- //!
5547
- //! Called from JunctionRef.removeJunctionAndMergeConnectors — applications
5548
- //! that want to remove a connector should use this method (instead of the
5549
- //! C++ deleteConnector → ActionInfo dance).
5550
5227
  deleteConnector(connector) {
5551
5228
  connector.makeInactive();
5552
5229
  }
5553
- //! @brief Remove a junction from the router scene.
5554
- //!
5555
- //! Called from JunctionRef.removeJunctionAndMergeConnectors — applications
5556
- //! can also call it directly.
5557
5230
  deleteJunction(junction) {
5558
5231
  if (junction.isActive()) {
5559
5232
  junction.removeFromGraph();
5560
5233
  junction.makeInactive();
5561
5234
  }
5562
5235
  }
5563
- //! @brief Returns whether the router is currently in the crossing-penalty
5564
- //! rerouting stage. Used by makepath to suppress some penalties
5565
- //! during the first routing pass.
5566
5236
  isInCrossingPenaltyReroutingStage() {
5567
5237
  return this.m_in_crossing_rerouting_stage;
5568
5238
  }
5569
- //! @brief Rebuild the static orthogonal visibility graph if it's been
5570
- //! invalidated. See orthogonal.ts.
5571
5239
  regenerateStaticBuiltGraph() {
5572
5240
  this.visOrthogGraph.clear();
5573
5241
  generateStaticOrthogonalVisGraph(this);
@@ -5586,18 +5254,6 @@ var Router = class {
5586
5254
  }
5587
5255
  return this.hyperedgesForRerouting.length - 1;
5588
5256
  }
5589
- //! @brief Run the orthogonal routing pipeline over every active connector.
5590
- //!
5591
- //! Replaces the C++ `processTransaction` minus the action-replay machinery.
5592
- //! Pipeline:
5593
- //! 1. Rebuild the static orthogonal visibility graph (stub — TODO(orthogonal)).
5594
- //! 2. Walk connRefs and call generatePath() on each. Connectors whose
5595
- //! `_needsRerouteFlag` / `_falsePath` is false are no-ops, so this is
5596
- //! safe to call after each individual mutation.
5597
- //! 3. Nudge orthogonal routes (stub — TODO(orthogonal)).
5598
- //! 4. Improve hyperedge routes (stub — TODO(hyperedgeimprover)).
5599
- //!
5600
- //! @return true if any connector had its route updated.
5601
5257
  route() {
5602
5258
  this.regenerateStaticBuiltGraph();
5603
5259
  let rerouted = /* @__PURE__ */ new Set();