@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 +0 -344
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -7
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();
|