@tscircuit/schematic-trace-solver 0.0.97 → 0.0.98
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.d.ts +9 -0
- package/dist/index.js +753 -285
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +45 -1
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +55 -13
- package/lib/solvers/TraceCleanupSolver/alignSameNetRails.ts +66 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts +124 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts +108 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts +121 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts +114 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts +27 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts +30 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/scoreRailAlignment.ts +67 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts +34 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +1 -60
- package/lib/utils/doesPathCoincideWithTraces.ts +61 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +1 -1
- package/tests/examples/__snapshots__/example02.snap.svg +1 -1
- package/tests/examples/__snapshots__/example14.snap.svg +1 -1
- package/tests/examples/__snapshots__/example18.snap.svg +1 -1
- package/tests/examples/__snapshots__/example46.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +1 -1
- package/tests/repros/repro-rp2040-gamepad-trace-alignment.test.ts +24 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-component-scope.test.ts +38 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-eligible-traces.test.ts +28 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-horizontal.test.ts +62 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-label-anchor.test.ts +36 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-label-junction.test.ts +42 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-obstacle.test.ts +28 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-pipeline-label-connector.test.ts +52 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts +25 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-visible-length.test.ts +40 -0
- package/tests/solvers/TraceCleanupSolver/fixtures/alignSameNetRails.ts +89 -0
package/dist/index.js
CHANGED
|
@@ -1440,12 +1440,12 @@ var applyJogToTerminalSegment = ({
|
|
|
1440
1440
|
if (si !== 0 && si !== pts.length - 2) return;
|
|
1441
1441
|
const start = pts[si];
|
|
1442
1442
|
const end = pts[si + 1];
|
|
1443
|
-
const
|
|
1444
|
-
const
|
|
1445
|
-
if (!
|
|
1446
|
-
const segDir =
|
|
1443
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS11;
|
|
1444
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS11;
|
|
1445
|
+
if (!isVertical5 && !isHorizontal4) return;
|
|
1446
|
+
const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1447
1447
|
if (pts.length === 2) {
|
|
1448
|
-
if (
|
|
1448
|
+
if (isVertical5) {
|
|
1449
1449
|
const jogYNearStart = start.y + segDir * JOG_SIZE;
|
|
1450
1450
|
const jogYNearEnd = end.y - segDir * JOG_SIZE;
|
|
1451
1451
|
pts.splice(
|
|
@@ -1471,7 +1471,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1471
1471
|
return;
|
|
1472
1472
|
}
|
|
1473
1473
|
if (si === 0) {
|
|
1474
|
-
if (
|
|
1474
|
+
if (isVertical5) {
|
|
1475
1475
|
const jogY = start.y + segDir * JOG_SIZE;
|
|
1476
1476
|
pts.splice(
|
|
1477
1477
|
1,
|
|
@@ -1491,7 +1491,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1491
1491
|
);
|
|
1492
1492
|
}
|
|
1493
1493
|
} else {
|
|
1494
|
-
if (
|
|
1494
|
+
if (isVertical5) {
|
|
1495
1495
|
const jogY = end.y - segDir * JOG_SIZE;
|
|
1496
1496
|
pts.splice(
|
|
1497
1497
|
si,
|
|
@@ -1587,10 +1587,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1587
1587
|
} else {
|
|
1588
1588
|
const start = pts[si];
|
|
1589
1589
|
const end = pts[si + 1];
|
|
1590
|
-
const
|
|
1591
|
-
const
|
|
1592
|
-
if (!
|
|
1593
|
-
if (
|
|
1590
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS4;
|
|
1591
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
|
|
1592
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
1593
|
+
if (isVertical5) {
|
|
1594
1594
|
start.x += offset;
|
|
1595
1595
|
end.x += offset;
|
|
1596
1596
|
} else {
|
|
@@ -1619,11 +1619,11 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1619
1619
|
}) {
|
|
1620
1620
|
const blockingCollisions = this.getBlockingCollisions({ group, offset });
|
|
1621
1621
|
if (blockingCollisions.length === 0) return offset;
|
|
1622
|
-
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical:
|
|
1622
|
+
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical: isVertical5, obstacle }) => {
|
|
1623
1623
|
let orig;
|
|
1624
1624
|
let min;
|
|
1625
1625
|
let max;
|
|
1626
|
-
if (
|
|
1626
|
+
if (isVertical5) {
|
|
1627
1627
|
orig = start.x;
|
|
1628
1628
|
min = obstacle.minX;
|
|
1629
1629
|
max = obstacle.maxX;
|
|
@@ -1635,8 +1635,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1635
1635
|
const next = orig + candidateOffset;
|
|
1636
1636
|
return orig <= min + EPS4 && next >= max - EPS4 || orig >= max - EPS4 && next <= min + EPS4;
|
|
1637
1637
|
});
|
|
1638
|
-
const edges = blockingCollisions.map(({ start, isVertical:
|
|
1639
|
-
if (
|
|
1638
|
+
const edges = blockingCollisions.map(({ start, isVertical: isVertical5, obstacle }) => {
|
|
1639
|
+
if (isVertical5) {
|
|
1640
1640
|
return {
|
|
1641
1641
|
coord: start.x,
|
|
1642
1642
|
lowEdge: obstacle.minX,
|
|
@@ -1670,10 +1670,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1670
1670
|
const start = trace.tracePath[traceSegmentIndex];
|
|
1671
1671
|
const end = trace.tracePath[traceSegmentIndex + 1];
|
|
1672
1672
|
if (!start || !end) continue;
|
|
1673
|
-
const
|
|
1674
|
-
const
|
|
1675
|
-
if (!
|
|
1676
|
-
const shiftedSegment =
|
|
1673
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS4;
|
|
1674
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
|
|
1675
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
1676
|
+
const shiftedSegment = isVertical5 ? [
|
|
1677
1677
|
{ ...start, x: start.x + offset },
|
|
1678
1678
|
{ ...end, x: end.x + offset }
|
|
1679
1679
|
] : [
|
|
@@ -1684,7 +1684,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1684
1684
|
if (collision) {
|
|
1685
1685
|
blockingCollisions.push({
|
|
1686
1686
|
start,
|
|
1687
|
-
isVertical:
|
|
1687
|
+
isVertical: isVertical5,
|
|
1688
1688
|
obstacle: collision.rect
|
|
1689
1689
|
});
|
|
1690
1690
|
}
|
|
@@ -1867,9 +1867,9 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1867
1867
|
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
1868
1868
|
const p1 = tracePath[i];
|
|
1869
1869
|
const p2 = tracePath[i + 1];
|
|
1870
|
-
const
|
|
1871
|
-
const
|
|
1872
|
-
if (!
|
|
1870
|
+
const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS11;
|
|
1871
|
+
const isVertical5 = Math.abs(p1.x - p2.x) < EPS11;
|
|
1872
|
+
if (!isHorizontal4 && !isVertical5) {
|
|
1873
1873
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
1874
1874
|
}
|
|
1875
1875
|
}
|
|
@@ -3478,19 +3478,18 @@ var generateRerouteCandidates = ({
|
|
|
3478
3478
|
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
3479
3479
|
};
|
|
3480
3480
|
|
|
3481
|
-
// lib/
|
|
3482
|
-
var MAX_TRIES = 5;
|
|
3481
|
+
// lib/utils/doesPathCoincideWithTraces.ts
|
|
3483
3482
|
var COINCIDENT_EPS = 2e-3;
|
|
3484
3483
|
var doesPathCoincideWithTraces = (path, traces) => {
|
|
3485
3484
|
const rangesOverlap1D = (a1, a2, b1, b2) => Math.min(Math.max(a1, a2), Math.max(b1, b2)) - Math.max(Math.min(a1, a2), Math.min(b1, b2)) > COINCIDENT_EPS;
|
|
3486
3485
|
for (let i = 0; i < path.length - 1; i++) {
|
|
3487
3486
|
const pathSegStart = path[i];
|
|
3488
3487
|
const pathSegEnd = path[i + 1];
|
|
3489
|
-
const
|
|
3490
|
-
const
|
|
3491
|
-
if (!
|
|
3492
|
-
const crossAxis =
|
|
3493
|
-
const alongAxis =
|
|
3488
|
+
const isVertical5 = Math.abs(pathSegStart.x - pathSegEnd.x) < COINCIDENT_EPS;
|
|
3489
|
+
const isHorizontal4 = Math.abs(pathSegStart.y - pathSegEnd.y) < COINCIDENT_EPS;
|
|
3490
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
3491
|
+
const crossAxis = isVertical5 ? "x" : "y";
|
|
3492
|
+
const alongAxis = isVertical5 ? "y" : "x";
|
|
3494
3493
|
for (const trace of traces) {
|
|
3495
3494
|
for (let j = 0; j < trace.tracePath.length - 1; j++) {
|
|
3496
3495
|
const traceSegStart = trace.tracePath[j];
|
|
@@ -3512,6 +3511,9 @@ var doesPathCoincideWithTraces = (path, traces) => {
|
|
|
3512
3511
|
}
|
|
3513
3512
|
return false;
|
|
3514
3513
|
};
|
|
3514
|
+
|
|
3515
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
3516
|
+
var MAX_TRIES = 5;
|
|
3515
3517
|
var SingleOverlapSolver = class extends BaseSolver {
|
|
3516
3518
|
queuedCandidatePaths;
|
|
3517
3519
|
solvedTracePath = null;
|
|
@@ -4691,6 +4693,590 @@ var balanceZShapes = ({
|
|
|
4691
4693
|
};
|
|
4692
4694
|
};
|
|
4693
4695
|
|
|
4696
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
|
|
4697
|
+
var RAIL_ALIGNMENT_EPSILON = 2e-3;
|
|
4698
|
+
var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
|
|
4699
|
+
var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
|
|
4700
|
+
var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
|
|
4701
|
+
var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
|
|
4702
|
+
var getRailOrientation = (a, b) => {
|
|
4703
|
+
if (!isPositiveLength(a, b)) return null;
|
|
4704
|
+
if (isVertical2(a, b)) return "vertical";
|
|
4705
|
+
if (isHorizontal2(a, b)) return "horizontal";
|
|
4706
|
+
return null;
|
|
4707
|
+
};
|
|
4708
|
+
var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
|
|
4709
|
+
var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
|
|
4710
|
+
var getDistinctCoordinates = (coordinates) => {
|
|
4711
|
+
const distinct = [];
|
|
4712
|
+
for (const coordinate of coordinates) {
|
|
4713
|
+
if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
|
|
4714
|
+
distinct.push(coordinate);
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
return distinct;
|
|
4718
|
+
};
|
|
4719
|
+
var getMergedIntervalLength = (intervals) => {
|
|
4720
|
+
const groups = [];
|
|
4721
|
+
for (const interval of intervals) {
|
|
4722
|
+
const group = groups.find(
|
|
4723
|
+
(items) => nearlyEqual(items[0].coordinate, interval.coordinate)
|
|
4724
|
+
);
|
|
4725
|
+
if (group) group.push(interval);
|
|
4726
|
+
else groups.push([interval]);
|
|
4727
|
+
}
|
|
4728
|
+
return groups.reduce((total, group) => {
|
|
4729
|
+
const sorted = [...group].sort((a, b) => a.min - b.min);
|
|
4730
|
+
let groupLength = 0;
|
|
4731
|
+
let currentMin = sorted[0].min;
|
|
4732
|
+
let currentMax = sorted[0].max;
|
|
4733
|
+
for (const interval of sorted.slice(1)) {
|
|
4734
|
+
if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
|
|
4735
|
+
currentMax = Math.max(currentMax, interval.max);
|
|
4736
|
+
} else {
|
|
4737
|
+
groupLength += currentMax - currentMin;
|
|
4738
|
+
currentMin = interval.min;
|
|
4739
|
+
currentMax = interval.max;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
return total + groupLength + currentMax - currentMin;
|
|
4743
|
+
}, 0);
|
|
4744
|
+
};
|
|
4745
|
+
var getVisibleTraceLength = (traces) => {
|
|
4746
|
+
const horizontal = [];
|
|
4747
|
+
const vertical = [];
|
|
4748
|
+
for (const trace of traces) {
|
|
4749
|
+
for (let index = 0; index < trace.tracePath.length - 1; index++) {
|
|
4750
|
+
const start = trace.tracePath[index];
|
|
4751
|
+
const end = trace.tracePath[index + 1];
|
|
4752
|
+
if (isHorizontal2(start, end)) {
|
|
4753
|
+
horizontal.push({
|
|
4754
|
+
coordinate: start.y,
|
|
4755
|
+
min: Math.min(start.x, end.x),
|
|
4756
|
+
max: Math.max(start.x, end.x)
|
|
4757
|
+
});
|
|
4758
|
+
} else if (isVertical2(start, end)) {
|
|
4759
|
+
vertical.push({
|
|
4760
|
+
coordinate: start.x,
|
|
4761
|
+
min: Math.min(start.y, end.y),
|
|
4762
|
+
max: Math.max(start.y, end.y)
|
|
4763
|
+
});
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
return getMergedIntervalLength(horizontal) + getMergedIntervalLength(vertical);
|
|
4768
|
+
};
|
|
4769
|
+
|
|
4770
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
|
|
4771
|
+
var moveRailSegments = (trace, segments, coordinate) => {
|
|
4772
|
+
const pointsToMove = /* @__PURE__ */ new Set();
|
|
4773
|
+
for (const segment of segments) {
|
|
4774
|
+
pointsToMove.add(segment.segmentIndex);
|
|
4775
|
+
pointsToMove.add(segment.segmentIndex + 1);
|
|
4776
|
+
}
|
|
4777
|
+
const orientation = segments[0].orientation;
|
|
4778
|
+
const tracePath = simplifyPath(
|
|
4779
|
+
trace.tracePath.map((point, index) => {
|
|
4780
|
+
if (!pointsToMove.has(index)) return point;
|
|
4781
|
+
return orientation === "vertical" ? { ...point, x: coordinate } : { ...point, y: coordinate };
|
|
4782
|
+
})
|
|
4783
|
+
);
|
|
4784
|
+
return { ...trace, tracePath };
|
|
4785
|
+
};
|
|
4786
|
+
|
|
4787
|
+
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
4788
|
+
var EPS5 = 1e-6;
|
|
4789
|
+
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
4790
|
+
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS5 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS5;
|
|
4791
|
+
var getTraceCorners = (path) => {
|
|
4792
|
+
const corners = [];
|
|
4793
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
4794
|
+
const previousPoint = path[i - 1];
|
|
4795
|
+
const cornerPoint = path[i];
|
|
4796
|
+
const nextPoint = path[i + 1];
|
|
4797
|
+
if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
|
|
4798
|
+
corners.push(cornerPoint);
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
return corners;
|
|
4802
|
+
};
|
|
4803
|
+
var tracePathContainsPoint = (path, point) => {
|
|
4804
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4805
|
+
if (isPointOnSegment(point, path[i], path[i + 1])) return true;
|
|
4806
|
+
}
|
|
4807
|
+
return false;
|
|
4808
|
+
};
|
|
4809
|
+
var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
4810
|
+
var getUniquePinCount = (pinIds) => new Set(pinIds).size;
|
|
4811
|
+
var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
|
|
4812
|
+
var isPointOnSegment = (point, start, end) => {
|
|
4813
|
+
if (getSegmentOrientation(start, end) === "horizontal") {
|
|
4814
|
+
return Math.abs(point.y - start.y) <= EPS5 && point.x >= Math.min(start.x, end.x) - EPS5 && point.x <= Math.max(start.x, end.x) + EPS5;
|
|
4815
|
+
}
|
|
4816
|
+
return Math.abs(point.x - start.x) <= EPS5 && point.y >= Math.min(start.y, end.y) - EPS5 && point.y <= Math.max(start.y, end.y) + EPS5;
|
|
4817
|
+
};
|
|
4818
|
+
var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS5 ? "horizontal" : "vertical";
|
|
4819
|
+
|
|
4820
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
|
|
4821
|
+
var getAnchoredTraceIds = (label, traces) => new Set(
|
|
4822
|
+
traces.filter(
|
|
4823
|
+
(trace) => trace.globalConnNetId === label.globalConnNetId && tracePathContainsPoint(trace.tracePath, label.anchorPoint)
|
|
4824
|
+
).map((trace) => trace.mspPairId)
|
|
4825
|
+
);
|
|
4826
|
+
var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
|
|
4827
|
+
const anchoredBefore = getAnchoredTraceIds(label, before);
|
|
4828
|
+
if (anchoredBefore.size === 0) return true;
|
|
4829
|
+
const anchoredAfter = getAnchoredTraceIds(label, after);
|
|
4830
|
+
return [...anchoredBefore].every((traceId) => anchoredAfter.has(traceId));
|
|
4831
|
+
});
|
|
4832
|
+
|
|
4833
|
+
// lib/solvers/Example28Solver/types.ts
|
|
4834
|
+
var EPS6 = 1e-9;
|
|
4835
|
+
|
|
4836
|
+
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
4837
|
+
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
4838
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS6;
|
|
4839
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS6;
|
|
4840
|
+
if (isVertical5) {
|
|
4841
|
+
return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
|
|
4842
|
+
}
|
|
4843
|
+
if (isHorizontal4) {
|
|
4844
|
+
return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
|
|
4845
|
+
}
|
|
4846
|
+
return false;
|
|
4847
|
+
};
|
|
4848
|
+
|
|
4849
|
+
// lib/solvers/Example28Solver/geometry.ts
|
|
4850
|
+
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
4851
|
+
var getPathLength = (path) => {
|
|
4852
|
+
let length = 0;
|
|
4853
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4854
|
+
length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
|
|
4855
|
+
}
|
|
4856
|
+
return length;
|
|
4857
|
+
};
|
|
4858
|
+
var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
4859
|
+
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS6 || Math.abs(start.y - end.y) < EPS6;
|
|
4860
|
+
var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS6 ? "horizontal" : "vertical";
|
|
4861
|
+
var projectPointToSegment = (point, start, end) => {
|
|
4862
|
+
if (Math.abs(start.x - end.x) < EPS6) {
|
|
4863
|
+
return {
|
|
4864
|
+
x: start.x,
|
|
4865
|
+
y: Math.min(
|
|
4866
|
+
Math.max(point.y, Math.min(start.y, end.y)),
|
|
4867
|
+
Math.max(start.y, end.y)
|
|
4868
|
+
)
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
if (Math.abs(start.y - end.y) < EPS6) {
|
|
4872
|
+
return {
|
|
4873
|
+
x: Math.min(
|
|
4874
|
+
Math.max(point.x, Math.min(start.x, end.x)),
|
|
4875
|
+
Math.max(start.x, end.x)
|
|
4876
|
+
),
|
|
4877
|
+
y: start.y
|
|
4878
|
+
};
|
|
4879
|
+
}
|
|
4880
|
+
return start;
|
|
4881
|
+
};
|
|
4882
|
+
var getPointToSegmentDistance = (point, start, end) => getDistance2(point, projectPointToSegment(point, start, end));
|
|
4883
|
+
var getSegments = (path) => {
|
|
4884
|
+
const segments = [];
|
|
4885
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4886
|
+
const start = path[i];
|
|
4887
|
+
const end = path[i + 1];
|
|
4888
|
+
if (!isAxisAlignedSegment(start, end)) continue;
|
|
4889
|
+
segments.push({
|
|
4890
|
+
start,
|
|
4891
|
+
end,
|
|
4892
|
+
orientation: getSegmentOrientation2(start, end)
|
|
4893
|
+
});
|
|
4894
|
+
}
|
|
4895
|
+
return segments;
|
|
4896
|
+
};
|
|
4897
|
+
var findSegmentContainingPoint = (path, point) => {
|
|
4898
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4899
|
+
const start = path[i];
|
|
4900
|
+
const end = path[i + 1];
|
|
4901
|
+
if (!isAxisAlignedSegment(start, end)) continue;
|
|
4902
|
+
if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
|
|
4903
|
+
return {
|
|
4904
|
+
index: i,
|
|
4905
|
+
start,
|
|
4906
|
+
end,
|
|
4907
|
+
orientation: getSegmentOrientation2(start, end)
|
|
4908
|
+
};
|
|
4909
|
+
}
|
|
4910
|
+
return null;
|
|
4911
|
+
};
|
|
4912
|
+
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
4913
|
+
if (segment.orientation === "horizontal") {
|
|
4914
|
+
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS6 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS6;
|
|
4915
|
+
}
|
|
4916
|
+
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
|
|
4917
|
+
};
|
|
4918
|
+
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
4919
|
+
const matchingSegments = getSegments(path).filter(
|
|
4920
|
+
(segment) => segment.orientation === orientation
|
|
4921
|
+
);
|
|
4922
|
+
if (matchingSegments.length === 0) return null;
|
|
4923
|
+
const containingSegments = matchingSegments.filter(
|
|
4924
|
+
(segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
|
|
4925
|
+
);
|
|
4926
|
+
const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
|
|
4927
|
+
const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
|
|
4928
|
+
return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
|
|
4929
|
+
};
|
|
4930
|
+
var projectPointToPath = (point, path) => {
|
|
4931
|
+
let bestPoint = null;
|
|
4932
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
4933
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4934
|
+
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
4935
|
+
const distance3 = getDistance2(point, projectedPoint);
|
|
4936
|
+
if (distance3 < bestDistance) {
|
|
4937
|
+
bestPoint = projectedPoint;
|
|
4938
|
+
bestDistance = distance3;
|
|
4939
|
+
}
|
|
4940
|
+
}
|
|
4941
|
+
return bestPoint;
|
|
4942
|
+
};
|
|
4943
|
+
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
4944
|
+
const aVertical = Math.abs(a1.x - a2.x) < EPS6;
|
|
4945
|
+
const bVertical = Math.abs(b1.x - b2.x) < EPS6;
|
|
4946
|
+
const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS6 && value <= Math.max(p1, p2) + EPS6;
|
|
4947
|
+
if (aVertical && bVertical) {
|
|
4948
|
+
if (Math.abs(a1.x - b1.x) > EPS6) return false;
|
|
4949
|
+
const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
|
|
4950
|
+
return overlap > EPS6;
|
|
4951
|
+
}
|
|
4952
|
+
if (!aVertical && !bVertical) {
|
|
4953
|
+
if (Math.abs(a1.y - b1.y) > EPS6) return false;
|
|
4954
|
+
const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
|
|
4955
|
+
return overlap > EPS6;
|
|
4956
|
+
}
|
|
4957
|
+
const verticalA = aVertical ? a1 : b1;
|
|
4958
|
+
const verticalB = aVertical ? a2 : b2;
|
|
4959
|
+
const horizontalA = aVertical ? b1 : a1;
|
|
4960
|
+
const horizontalB = aVertical ? b2 : a2;
|
|
4961
|
+
return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
|
|
4962
|
+
};
|
|
4963
|
+
var countPathIntersections = (pathA, pathB) => {
|
|
4964
|
+
let count = 0;
|
|
4965
|
+
for (let i = 0; i < pathA.length - 1; i++) {
|
|
4966
|
+
for (let j = 0; j < pathB.length - 1; j++) {
|
|
4967
|
+
if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
|
|
4968
|
+
count++;
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
return count;
|
|
4973
|
+
};
|
|
4974
|
+
var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
4975
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4976
|
+
const start = path[i];
|
|
4977
|
+
const end = path[i + 1];
|
|
4978
|
+
for (const obstacle of chipObstacles) {
|
|
4979
|
+
if (!segmentIntersectsRect(start, end, obstacle)) continue;
|
|
4980
|
+
if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
4981
|
+
return true;
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
return false;
|
|
4985
|
+
};
|
|
4986
|
+
|
|
4987
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/scoreRailAlignment.ts
|
|
4988
|
+
var countOtherNetCrossings = (traces, allTraces) => {
|
|
4989
|
+
let crossings = 0;
|
|
4990
|
+
for (const trace of traces) {
|
|
4991
|
+
for (const otherTrace of allTraces) {
|
|
4992
|
+
if (trace.globalConnNetId === otherTrace.globalConnNetId) continue;
|
|
4993
|
+
crossings += countPathIntersections(trace.tracePath, otherTrace.tracePath);
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
return crossings;
|
|
4997
|
+
};
|
|
4998
|
+
var getTraceGeometryMetrics = (traces, allTraces) => ({
|
|
4999
|
+
turnCount: traces.reduce(
|
|
5000
|
+
(total, trace) => total + countTurns(trace.tracePath),
|
|
5001
|
+
0
|
|
5002
|
+
),
|
|
5003
|
+
visibleLength: getVisibleTraceLength(traces),
|
|
5004
|
+
pathLength: traces.reduce(
|
|
5005
|
+
(sum, trace) => sum + getPathLength(trace.tracePath),
|
|
5006
|
+
0
|
|
5007
|
+
),
|
|
5008
|
+
otherNetCrossings: countOtherNetCrossings(traces, allTraces)
|
|
5009
|
+
});
|
|
5010
|
+
var scoreIsBetter = (candidate, best) => {
|
|
5011
|
+
if (candidate.turnCount !== best.turnCount)
|
|
5012
|
+
return candidate.turnCount < best.turnCount;
|
|
5013
|
+
if (!nearlyEqual(candidate.visibleLength, best.visibleLength))
|
|
5014
|
+
return candidate.visibleLength < best.visibleLength;
|
|
5015
|
+
if (!nearlyEqual(candidate.pathLength, best.pathLength))
|
|
5016
|
+
return candidate.pathLength < best.pathLength;
|
|
5017
|
+
if (candidate.otherNetCrossings !== best.otherNetCrossings)
|
|
5018
|
+
return candidate.otherNetCrossings < best.otherNetCrossings;
|
|
5019
|
+
if (!nearlyEqual(candidate.displacement, best.displacement))
|
|
5020
|
+
return candidate.displacement < best.displacement;
|
|
5021
|
+
return candidate.coordinate < best.coordinate;
|
|
5022
|
+
};
|
|
5023
|
+
var isReadabilityImprovement = (candidate, baseline) => candidate.turnCount <= baseline.turnCount && (candidate.turnCount < baseline.turnCount || candidate.visibleLength <= baseline.visibleLength + RAIL_ALIGNMENT_EPSILON);
|
|
5024
|
+
|
|
5025
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts
|
|
5026
|
+
var tracePathChanged = (original, candidate) => original.tracePath.length !== candidate.tracePath.length || candidate.tracePath.some(
|
|
5027
|
+
(point, index) => !pointsEqual(point, original.tracePath[index])
|
|
5028
|
+
);
|
|
5029
|
+
var evaluateRailGroup = ({
|
|
5030
|
+
group,
|
|
5031
|
+
traces,
|
|
5032
|
+
netLabelPlacements,
|
|
5033
|
+
obstacles,
|
|
5034
|
+
eligibleTraceIds
|
|
5035
|
+
}) => {
|
|
5036
|
+
const groupTraceIds = new Set(group.map((segment) => segment.traceId));
|
|
5037
|
+
const originalGroupTraces = traces.filter(
|
|
5038
|
+
(trace) => groupTraceIds.has(trace.mspPairId)
|
|
5039
|
+
);
|
|
5040
|
+
const baseline = getTraceGeometryMetrics(originalGroupTraces, traces);
|
|
5041
|
+
const coordinates = getDistinctCoordinates(
|
|
5042
|
+
group.map((segment) => segment.coordinate)
|
|
5043
|
+
);
|
|
5044
|
+
const otherNetTraces = traces.filter(
|
|
5045
|
+
(trace) => trace.globalConnNetId !== group[0].globalConnNetId
|
|
5046
|
+
);
|
|
5047
|
+
const immutableSameNetTraces = traces.filter(
|
|
5048
|
+
(trace) => trace.globalConnNetId === group[0].globalConnNetId && !eligibleTraceIds.has(trace.mspPairId)
|
|
5049
|
+
);
|
|
5050
|
+
let best = null;
|
|
5051
|
+
for (const coordinate of coordinates) {
|
|
5052
|
+
const candidateMap = /* @__PURE__ */ new Map();
|
|
5053
|
+
for (const trace of originalGroupTraces) {
|
|
5054
|
+
const candidateTrace = moveRailSegments(
|
|
5055
|
+
trace,
|
|
5056
|
+
group.filter((segment) => segment.traceId === trace.mspPairId),
|
|
5057
|
+
coordinate
|
|
5058
|
+
);
|
|
5059
|
+
candidateMap.set(trace.mspPairId, candidateTrace);
|
|
5060
|
+
}
|
|
5061
|
+
const candidateTraces = [...candidateMap.values()];
|
|
5062
|
+
const allCandidateTraces = traces.map(
|
|
5063
|
+
(trace) => candidateMap.get(trace.mspPairId) ?? trace
|
|
5064
|
+
);
|
|
5065
|
+
const candidatesAreClear = candidateTraces.every(
|
|
5066
|
+
(candidate2) => !isPathCollidingWithObstacles(candidate2.tracePath, obstacles) && detectTraceLabelOverlap({
|
|
5067
|
+
traces: [candidate2],
|
|
5068
|
+
netLabels: netLabelPlacements
|
|
5069
|
+
}).length === 0 && !doesPathCoincideWithTraces(candidate2.tracePath, otherNetTraces) && !doesPathCoincideWithTraces(
|
|
5070
|
+
candidate2.tracePath,
|
|
5071
|
+
immutableSameNetTraces.filter(
|
|
5072
|
+
(trace) => trace.mspPairId !== candidate2.mspPairId
|
|
5073
|
+
)
|
|
5074
|
+
)
|
|
5075
|
+
);
|
|
5076
|
+
if (!candidatesAreClear) continue;
|
|
5077
|
+
if (!preservesLabelAnchors(netLabelPlacements, traces, allCandidateTraces)) {
|
|
5078
|
+
continue;
|
|
5079
|
+
}
|
|
5080
|
+
const metrics = getTraceGeometryMetrics(candidateTraces, allCandidateTraces);
|
|
5081
|
+
if (metrics.otherNetCrossings > baseline.otherNetCrossings) continue;
|
|
5082
|
+
if (!isReadabilityImprovement(metrics, baseline)) continue;
|
|
5083
|
+
const score = {
|
|
5084
|
+
...metrics,
|
|
5085
|
+
displacement: group.reduce(
|
|
5086
|
+
(sum, segment) => sum + Math.abs(segment.coordinate - coordinate),
|
|
5087
|
+
0
|
|
5088
|
+
),
|
|
5089
|
+
coordinate
|
|
5090
|
+
};
|
|
5091
|
+
const changedTraceIds = candidateTraces.filter((candidate2) => {
|
|
5092
|
+
const original = traces.find(
|
|
5093
|
+
(trace) => trace.mspPairId === candidate2.mspPairId
|
|
5094
|
+
);
|
|
5095
|
+
return tracePathChanged(original, candidate2);
|
|
5096
|
+
}).map((trace) => trace.mspPairId);
|
|
5097
|
+
if (changedTraceIds.length === 0) continue;
|
|
5098
|
+
const candidate = { traces: allCandidateTraces, changedTraceIds, score };
|
|
5099
|
+
if (!best || scoreIsBetter(candidate.score, best.score)) best = candidate;
|
|
5100
|
+
}
|
|
5101
|
+
return best;
|
|
5102
|
+
};
|
|
5103
|
+
|
|
5104
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts
|
|
5105
|
+
var getMovableRailSegments = (trace) => {
|
|
5106
|
+
const segments = [];
|
|
5107
|
+
const path = trace.tracePath;
|
|
5108
|
+
for (let segmentIndex = 1; segmentIndex < path.length - 2; segmentIndex++) {
|
|
5109
|
+
const previous = path[segmentIndex - 1];
|
|
5110
|
+
const start = path[segmentIndex];
|
|
5111
|
+
const end = path[segmentIndex + 1];
|
|
5112
|
+
const next = path[segmentIndex + 2];
|
|
5113
|
+
const orientation = getRailOrientation(start, end);
|
|
5114
|
+
if (!orientation) continue;
|
|
5115
|
+
const previousOrientation = getRailOrientation(previous, start);
|
|
5116
|
+
const nextOrientation = getRailOrientation(end, next);
|
|
5117
|
+
if (!previousOrientation || !nextOrientation) continue;
|
|
5118
|
+
if (previousOrientation === orientation || nextOrientation === orientation)
|
|
5119
|
+
continue;
|
|
5120
|
+
const alongValues = orientation === "vertical" ? [start.y, end.y] : [start.x, end.x];
|
|
5121
|
+
segments.push({
|
|
5122
|
+
traceId: trace.mspPairId,
|
|
5123
|
+
segmentIndex,
|
|
5124
|
+
globalConnNetId: trace.globalConnNetId,
|
|
5125
|
+
orientation,
|
|
5126
|
+
coordinate: orientation === "vertical" ? start.x : start.y,
|
|
5127
|
+
minAlong: Math.min(...alongValues),
|
|
5128
|
+
maxAlong: Math.max(...alongValues)
|
|
5129
|
+
});
|
|
5130
|
+
}
|
|
5131
|
+
return segments;
|
|
5132
|
+
};
|
|
5133
|
+
var railIsOutsideComponent = (segment, chip, facingDirection) => {
|
|
5134
|
+
const minX = chip.center.x - chip.width / 2;
|
|
5135
|
+
const maxX = chip.center.x + chip.width / 2;
|
|
5136
|
+
const minY = chip.center.y - chip.height / 2;
|
|
5137
|
+
const maxY = chip.center.y + chip.height / 2;
|
|
5138
|
+
switch (facingDirection) {
|
|
5139
|
+
case "x-":
|
|
5140
|
+
return segment.orientation === "vertical" && segment.coordinate <= minX + RAIL_ALIGNMENT_EPSILON;
|
|
5141
|
+
case "x+":
|
|
5142
|
+
return segment.orientation === "vertical" && segment.coordinate >= maxX - RAIL_ALIGNMENT_EPSILON;
|
|
5143
|
+
case "y+":
|
|
5144
|
+
return segment.orientation === "horizontal" && segment.coordinate >= maxY - RAIL_ALIGNMENT_EPSILON;
|
|
5145
|
+
case "y-":
|
|
5146
|
+
return segment.orientation === "horizontal" && segment.coordinate <= minY + RAIL_ALIGNMENT_EPSILON;
|
|
5147
|
+
}
|
|
5148
|
+
};
|
|
5149
|
+
var getComponentSideRailSegments = (trace, chipMap) => {
|
|
5150
|
+
const segments = [];
|
|
5151
|
+
for (const segment of getMovableRailSegments(trace)) {
|
|
5152
|
+
const associations = trace.pins.flatMap((pin, pinIndex) => {
|
|
5153
|
+
const chip = chipMap.get(pin.chipId);
|
|
5154
|
+
if (!chip) return [];
|
|
5155
|
+
const componentFacingDirection = pin._facingDirection ?? getPinDirection(pin, chip);
|
|
5156
|
+
if (!railIsOutsideComponent(segment, chip, componentFacingDirection)) {
|
|
5157
|
+
return [];
|
|
5158
|
+
}
|
|
5159
|
+
return [
|
|
5160
|
+
{
|
|
5161
|
+
componentId: chip.chipId,
|
|
5162
|
+
componentFacingDirection,
|
|
5163
|
+
distanceFromEndpoint: pinIndex === 0 ? segment.segmentIndex : trace.tracePath.length - 2 - segment.segmentIndex
|
|
5164
|
+
}
|
|
5165
|
+
];
|
|
5166
|
+
});
|
|
5167
|
+
associations.sort((a, b) => a.distanceFromEndpoint - b.distanceFromEndpoint);
|
|
5168
|
+
const association = associations[0];
|
|
5169
|
+
if (!association) continue;
|
|
5170
|
+
segments.push({ ...segment, ...association });
|
|
5171
|
+
}
|
|
5172
|
+
return segments;
|
|
5173
|
+
};
|
|
5174
|
+
|
|
5175
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts
|
|
5176
|
+
var getCorridor = (a, b) => {
|
|
5177
|
+
const overlapMin = Math.max(a.minAlong, b.minAlong);
|
|
5178
|
+
const overlapMax = Math.min(a.maxAlong, b.maxAlong);
|
|
5179
|
+
const along = (overlapMin + overlapMax) / 2;
|
|
5180
|
+
return a.orientation === "vertical" ? [
|
|
5181
|
+
{ x: a.coordinate, y: along },
|
|
5182
|
+
{ x: b.coordinate, y: along }
|
|
5183
|
+
] : [
|
|
5184
|
+
{ x: along, y: a.coordinate },
|
|
5185
|
+
{ x: along, y: b.coordinate }
|
|
5186
|
+
];
|
|
5187
|
+
};
|
|
5188
|
+
var corridorIsClear = (a, b, obstacles) => {
|
|
5189
|
+
if (!rangesTouchOrOverlap(a, b)) return true;
|
|
5190
|
+
const [start, end] = getCorridor(a, b);
|
|
5191
|
+
return !obstacles.some(
|
|
5192
|
+
(obstacle) => segmentIntersectsRect(start, end, obstacle)
|
|
5193
|
+
);
|
|
5194
|
+
};
|
|
5195
|
+
var tracesSharePin = (a, b, traceMap) => {
|
|
5196
|
+
if (a.traceId === b.traceId) return true;
|
|
5197
|
+
const aPinIds = new Set(traceMap.get(a.traceId).pins.map((pin) => pin.pinId));
|
|
5198
|
+
return traceMap.get(b.traceId).pins.some((pin) => aPinIds.has(pin.pinId));
|
|
5199
|
+
};
|
|
5200
|
+
var canJoinRailGroup = (start, current, candidate, traceMap, obstacles) => candidate.globalConnNetId === start.globalConnNetId && candidate.orientation === start.orientation && candidate.componentId === start.componentId && candidate.componentFacingDirection === start.componentFacingDirection && (rangesTouchOrOverlap(current, candidate) || tracesSharePin(current, candidate, traceMap)) && corridorIsClear(current, candidate, obstacles);
|
|
5201
|
+
var getRailGroups = (traces, eligibleTraceIds, inputProblem, obstacles) => {
|
|
5202
|
+
const chipMap = new Map(inputProblem.chips.map((chip) => [chip.chipId, chip]));
|
|
5203
|
+
const segments = traces.filter((trace) => eligibleTraceIds.has(trace.mspPairId)).flatMap((trace) => getComponentSideRailSegments(trace, chipMap));
|
|
5204
|
+
const traceMap = new Map(traces.map((trace) => [trace.mspPairId, trace]));
|
|
5205
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5206
|
+
const groups = [];
|
|
5207
|
+
for (let startIndex = 0; startIndex < segments.length; startIndex++) {
|
|
5208
|
+
if (visited.has(startIndex)) continue;
|
|
5209
|
+
const start = segments[startIndex];
|
|
5210
|
+
const queue = [startIndex];
|
|
5211
|
+
const group = [];
|
|
5212
|
+
visited.add(startIndex);
|
|
5213
|
+
for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
|
|
5214
|
+
const current = segments[queue[queueIndex]];
|
|
5215
|
+
group.push(current);
|
|
5216
|
+
for (let candidateIndex = 0; candidateIndex < segments.length; candidateIndex++) {
|
|
5217
|
+
if (visited.has(candidateIndex)) continue;
|
|
5218
|
+
const candidate = segments[candidateIndex];
|
|
5219
|
+
if (!canJoinRailGroup(start, current, candidate, traceMap, obstacles)) {
|
|
5220
|
+
continue;
|
|
5221
|
+
}
|
|
5222
|
+
visited.add(candidateIndex);
|
|
5223
|
+
queue.push(candidateIndex);
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
const traceCount = new Set(group.map((segment) => segment.traceId)).size;
|
|
5227
|
+
const hasDifferentCoordinates = group.some(
|
|
5228
|
+
(segment) => !nearlyEqual(segment.coordinate, group[0].coordinate)
|
|
5229
|
+
);
|
|
5230
|
+
if (traceCount >= 2 && hasDifferentCoordinates) groups.push(group);
|
|
5231
|
+
}
|
|
5232
|
+
return groups;
|
|
5233
|
+
};
|
|
5234
|
+
|
|
5235
|
+
// lib/solvers/TraceCleanupSolver/alignSameNetRails.ts
|
|
5236
|
+
var alignSameNetRails = ({
|
|
5237
|
+
inputProblem,
|
|
5238
|
+
traces,
|
|
5239
|
+
netLabelPlacements,
|
|
5240
|
+
eligibleTraceIds
|
|
5241
|
+
}) => {
|
|
5242
|
+
let outputTraces = [...traces];
|
|
5243
|
+
const obstacles = getObstacleRects(inputProblem);
|
|
5244
|
+
const alignedTraceIds = /* @__PURE__ */ new Set();
|
|
5245
|
+
let alignedRailGroupCount = 0;
|
|
5246
|
+
const maximumPasses = Math.max(
|
|
5247
|
+
1,
|
|
5248
|
+
traces.reduce((sum, trace) => sum + trace.tracePath.length, 0)
|
|
5249
|
+
);
|
|
5250
|
+
for (let pass = 0; pass < maximumPasses; pass++) {
|
|
5251
|
+
const groups = getRailGroups(
|
|
5252
|
+
outputTraces,
|
|
5253
|
+
eligibleTraceIds,
|
|
5254
|
+
inputProblem,
|
|
5255
|
+
obstacles
|
|
5256
|
+
);
|
|
5257
|
+
let applied = null;
|
|
5258
|
+
for (const group of groups) {
|
|
5259
|
+
applied = evaluateRailGroup({
|
|
5260
|
+
group,
|
|
5261
|
+
traces: outputTraces,
|
|
5262
|
+
netLabelPlacements,
|
|
5263
|
+
obstacles,
|
|
5264
|
+
eligibleTraceIds
|
|
5265
|
+
});
|
|
5266
|
+
if (applied) break;
|
|
5267
|
+
}
|
|
5268
|
+
if (!applied) break;
|
|
5269
|
+
outputTraces = applied.traces;
|
|
5270
|
+
alignedRailGroupCount++;
|
|
5271
|
+
for (const traceId of applied.changedTraceIds) alignedTraceIds.add(traceId);
|
|
5272
|
+
}
|
|
5273
|
+
return {
|
|
5274
|
+
traces: outputTraces,
|
|
5275
|
+
alignedRailGroupCount,
|
|
5276
|
+
alignedTraceCount: alignedTraceIds.size
|
|
5277
|
+
};
|
|
5278
|
+
};
|
|
5279
|
+
|
|
4694
5280
|
// lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts
|
|
4695
5281
|
var findAllLShapedTurns = (tracePath) => {
|
|
4696
5282
|
const lShapes = [];
|
|
@@ -4748,8 +5334,8 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
|
4748
5334
|
};
|
|
4749
5335
|
|
|
4750
5336
|
// lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
|
|
4751
|
-
var
|
|
4752
|
-
var
|
|
5337
|
+
var EPS7 = 1e-6;
|
|
5338
|
+
var isVertical3 = (a, b, eps = EPS7) => Math.abs(a.x - b.x) < eps;
|
|
4753
5339
|
var generateLShapeRerouteCandidates = ({
|
|
4754
5340
|
lShape,
|
|
4755
5341
|
rectangle,
|
|
@@ -4762,50 +5348,50 @@ var generateLShapeRerouteCandidates = ({
|
|
|
4762
5348
|
let c2;
|
|
4763
5349
|
let i1_padded = interactionPoint1;
|
|
4764
5350
|
let i2_padded = interactionPoint2;
|
|
4765
|
-
if (Math.abs(p2.x - x) <
|
|
5351
|
+
if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
|
|
4766
5352
|
c2 = { x: x + width + padding, y: y - padding };
|
|
4767
|
-
if (
|
|
5353
|
+
if (isVertical3(p1, p2)) {
|
|
4768
5354
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
4769
5355
|
} else {
|
|
4770
5356
|
i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
|
|
4771
5357
|
}
|
|
4772
|
-
if (
|
|
5358
|
+
if (isVertical3(p2, p3)) {
|
|
4773
5359
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
|
|
4774
5360
|
} else {
|
|
4775
5361
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
4776
5362
|
}
|
|
4777
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
5363
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
|
|
4778
5364
|
c2 = { x: x - padding, y: y - padding };
|
|
4779
|
-
if (
|
|
5365
|
+
if (isVertical3(p1, p2)) {
|
|
4780
5366
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
4781
5367
|
} else {
|
|
4782
5368
|
i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
|
|
4783
5369
|
}
|
|
4784
|
-
if (
|
|
5370
|
+
if (isVertical3(p2, p3)) {
|
|
4785
5371
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
|
|
4786
5372
|
} else {
|
|
4787
5373
|
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
4788
5374
|
}
|
|
4789
|
-
} else if (Math.abs(p2.x - x) <
|
|
5375
|
+
} else if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - y) < EPS7) {
|
|
4790
5376
|
c2 = { x: x + width + padding, y: y + height + padding };
|
|
4791
|
-
if (
|
|
5377
|
+
if (isVertical3(p1, p2)) {
|
|
4792
5378
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
4793
5379
|
} else {
|
|
4794
5380
|
i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
|
|
4795
5381
|
}
|
|
4796
|
-
if (
|
|
5382
|
+
if (isVertical3(p2, p3)) {
|
|
4797
5383
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
|
|
4798
5384
|
} else {
|
|
4799
5385
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
4800
5386
|
}
|
|
4801
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
5387
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - y) < EPS7) {
|
|
4802
5388
|
c2 = { x: x - padding, y: y + height + padding };
|
|
4803
|
-
if (
|
|
5389
|
+
if (isVertical3(p1, p2)) {
|
|
4804
5390
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
4805
5391
|
} else {
|
|
4806
5392
|
i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
|
|
4807
5393
|
}
|
|
4808
|
-
if (
|
|
5394
|
+
if (isVertical3(p2, p3)) {
|
|
4809
5395
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
|
|
4810
5396
|
} else {
|
|
4811
5397
|
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
@@ -5269,9 +5855,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
5269
5855
|
};
|
|
5270
5856
|
|
|
5271
5857
|
// lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
|
|
5272
|
-
var
|
|
5273
|
-
var sameX = (a, b) => Math.abs(a.x - b.x) <=
|
|
5274
|
-
var sameY = (a, b) => Math.abs(a.y - b.y) <=
|
|
5858
|
+
var EPS8 = 1e-6;
|
|
5859
|
+
var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS8;
|
|
5860
|
+
var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS8;
|
|
5275
5861
|
var is4PointRectangle = (path) => {
|
|
5276
5862
|
if (path.length !== 4) return false;
|
|
5277
5863
|
const [p0, p1, p2, p3] = path;
|
|
@@ -5281,12 +5867,19 @@ var is4PointRectangle = (path) => {
|
|
|
5281
5867
|
};
|
|
5282
5868
|
|
|
5283
5869
|
// lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
5870
|
+
var DEFAULT_OPERATIONS = [
|
|
5871
|
+
"untangling_traces",
|
|
5872
|
+
"minimizing_turns",
|
|
5873
|
+
"balancing_l_shapes"
|
|
5874
|
+
];
|
|
5284
5875
|
var TraceCleanupSolver = class extends BaseSolver {
|
|
5285
5876
|
input;
|
|
5286
5877
|
outputTraces;
|
|
5287
5878
|
traceIdQueue;
|
|
5288
5879
|
tracesMap;
|
|
5289
|
-
|
|
5880
|
+
operations;
|
|
5881
|
+
operationIndex = 0;
|
|
5882
|
+
pipelineStep;
|
|
5290
5883
|
activeTraceId = null;
|
|
5291
5884
|
// New property
|
|
5292
5885
|
activeSubSolver = null;
|
|
@@ -5295,6 +5888,8 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5295
5888
|
this.input = solverInput;
|
|
5296
5889
|
this.outputTraces = [...solverInput.allTraces];
|
|
5297
5890
|
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
5891
|
+
this.operations = solverInput.operations ?? DEFAULT_OPERATIONS;
|
|
5892
|
+
this.pipelineStep = this.operations[0] ?? null;
|
|
5298
5893
|
this.traceIdQueue = Array.from(
|
|
5299
5894
|
solverInput.allTraces.map((e) => e.mspPairId)
|
|
5300
5895
|
);
|
|
@@ -5307,13 +5902,17 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5307
5902
|
this.outputTraces = output.traces;
|
|
5308
5903
|
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
5309
5904
|
this.activeSubSolver = null;
|
|
5310
|
-
this.
|
|
5905
|
+
this._advancePipeline();
|
|
5311
5906
|
} else if (this.activeSubSolver.failed) {
|
|
5312
5907
|
this.activeSubSolver = null;
|
|
5313
|
-
this.
|
|
5908
|
+
this._advancePipeline();
|
|
5314
5909
|
}
|
|
5315
5910
|
return;
|
|
5316
5911
|
}
|
|
5912
|
+
if (!this.pipelineStep) {
|
|
5913
|
+
this.solved = true;
|
|
5914
|
+
return;
|
|
5915
|
+
}
|
|
5317
5916
|
switch (this.pipelineStep) {
|
|
5318
5917
|
case "untangling_traces":
|
|
5319
5918
|
this._runUntangleTracesStep();
|
|
@@ -5324,8 +5923,17 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5324
5923
|
case "balancing_l_shapes":
|
|
5325
5924
|
this._runBalanceLShapesStep();
|
|
5326
5925
|
break;
|
|
5926
|
+
case "aligning_same_net_rails":
|
|
5927
|
+
this._runAlignSameNetRailsStep();
|
|
5928
|
+
break;
|
|
5327
5929
|
}
|
|
5328
5930
|
}
|
|
5931
|
+
_advancePipeline() {
|
|
5932
|
+
this.operationIndex++;
|
|
5933
|
+
this.pipelineStep = this.operations[this.operationIndex] ?? null;
|
|
5934
|
+
this.traceIdQueue = this.outputTraces.map((trace) => trace.mspPairId);
|
|
5935
|
+
if (this.pipelineStep) this.activeTraceId = null;
|
|
5936
|
+
}
|
|
5329
5937
|
_runUntangleTracesStep() {
|
|
5330
5938
|
this.activeSubSolver = new UntangleTraceSubsolver({
|
|
5331
5939
|
...this.input,
|
|
@@ -5334,17 +5942,14 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5334
5942
|
}
|
|
5335
5943
|
_runMinimizeTurnsStep() {
|
|
5336
5944
|
if (this.traceIdQueue.length === 0) {
|
|
5337
|
-
this.
|
|
5338
|
-
this.traceIdQueue = Array.from(
|
|
5339
|
-
this.input.allTraces.map((e) => e.mspPairId)
|
|
5340
|
-
);
|
|
5945
|
+
this._advancePipeline();
|
|
5341
5946
|
return;
|
|
5342
5947
|
}
|
|
5343
5948
|
this._processTrace("minimizing_turns");
|
|
5344
5949
|
}
|
|
5345
5950
|
_runBalanceLShapesStep() {
|
|
5346
5951
|
if (this.traceIdQueue.length === 0) {
|
|
5347
|
-
this.
|
|
5952
|
+
this._advancePipeline();
|
|
5348
5953
|
return;
|
|
5349
5954
|
}
|
|
5350
5955
|
this._processTrace("balancing_l_shapes");
|
|
@@ -5374,6 +5979,19 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5374
5979
|
this.tracesMap.set(targetMspConnectionPairId, updatedTrace);
|
|
5375
5980
|
this.outputTraces = Array.from(this.tracesMap.values());
|
|
5376
5981
|
}
|
|
5982
|
+
_runAlignSameNetRailsStep() {
|
|
5983
|
+
const alignment = alignSameNetRails({
|
|
5984
|
+
inputProblem: this.input.inputProblem,
|
|
5985
|
+
traces: this.outputTraces,
|
|
5986
|
+
netLabelPlacements: this.input.allLabelPlacements,
|
|
5987
|
+
eligibleTraceIds: this.input.eligibleTraceIds ?? new Set(this.outputTraces.map((trace) => trace.mspPairId))
|
|
5988
|
+
});
|
|
5989
|
+
this.outputTraces = alignment.traces;
|
|
5990
|
+
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
5991
|
+
this.stats.alignedRailGroupCount = alignment.alignedRailGroupCount;
|
|
5992
|
+
this.stats.alignedTraceCount = alignment.alignedTraceCount;
|
|
5993
|
+
this._advancePipeline();
|
|
5994
|
+
}
|
|
5377
5995
|
getOutput() {
|
|
5378
5996
|
return {
|
|
5379
5997
|
traces: this.outputTraces
|
|
@@ -5452,160 +6070,6 @@ var dir = (facingDirection) => {
|
|
|
5452
6070
|
throw new Error(`Invalid facing direction: ${facingDirection}`);
|
|
5453
6071
|
};
|
|
5454
6072
|
|
|
5455
|
-
// lib/solvers/Example28Solver/types.ts
|
|
5456
|
-
var EPS7 = 1e-9;
|
|
5457
|
-
|
|
5458
|
-
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
5459
|
-
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
5460
|
-
const isVertical4 = Math.abs(start.x - end.x) < EPS7;
|
|
5461
|
-
const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
|
|
5462
|
-
if (isVertical4) {
|
|
5463
|
-
return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
|
|
5464
|
-
}
|
|
5465
|
-
if (isHorizontal3) {
|
|
5466
|
-
return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
|
|
5467
|
-
}
|
|
5468
|
-
return false;
|
|
5469
|
-
};
|
|
5470
|
-
|
|
5471
|
-
// lib/solvers/Example28Solver/geometry.ts
|
|
5472
|
-
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
5473
|
-
var getPathLength = (path) => {
|
|
5474
|
-
let length = 0;
|
|
5475
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5476
|
-
length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
|
|
5477
|
-
}
|
|
5478
|
-
return length;
|
|
5479
|
-
};
|
|
5480
|
-
var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
5481
|
-
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
|
|
5482
|
-
var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
|
|
5483
|
-
var projectPointToSegment = (point, start, end) => {
|
|
5484
|
-
if (Math.abs(start.x - end.x) < EPS7) {
|
|
5485
|
-
return {
|
|
5486
|
-
x: start.x,
|
|
5487
|
-
y: Math.min(
|
|
5488
|
-
Math.max(point.y, Math.min(start.y, end.y)),
|
|
5489
|
-
Math.max(start.y, end.y)
|
|
5490
|
-
)
|
|
5491
|
-
};
|
|
5492
|
-
}
|
|
5493
|
-
if (Math.abs(start.y - end.y) < EPS7) {
|
|
5494
|
-
return {
|
|
5495
|
-
x: Math.min(
|
|
5496
|
-
Math.max(point.x, Math.min(start.x, end.x)),
|
|
5497
|
-
Math.max(start.x, end.x)
|
|
5498
|
-
),
|
|
5499
|
-
y: start.y
|
|
5500
|
-
};
|
|
5501
|
-
}
|
|
5502
|
-
return start;
|
|
5503
|
-
};
|
|
5504
|
-
var getPointToSegmentDistance = (point, start, end) => getDistance(point, projectPointToSegment(point, start, end));
|
|
5505
|
-
var getSegments = (path) => {
|
|
5506
|
-
const segments = [];
|
|
5507
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5508
|
-
const start = path[i];
|
|
5509
|
-
const end = path[i + 1];
|
|
5510
|
-
if (!isAxisAlignedSegment(start, end)) continue;
|
|
5511
|
-
segments.push({
|
|
5512
|
-
start,
|
|
5513
|
-
end,
|
|
5514
|
-
orientation: getSegmentOrientation(start, end)
|
|
5515
|
-
});
|
|
5516
|
-
}
|
|
5517
|
-
return segments;
|
|
5518
|
-
};
|
|
5519
|
-
var findSegmentContainingPoint = (path, point) => {
|
|
5520
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5521
|
-
const start = path[i];
|
|
5522
|
-
const end = path[i + 1];
|
|
5523
|
-
if (!isAxisAlignedSegment(start, end)) continue;
|
|
5524
|
-
if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
|
|
5525
|
-
return {
|
|
5526
|
-
index: i,
|
|
5527
|
-
start,
|
|
5528
|
-
end,
|
|
5529
|
-
orientation: getSegmentOrientation(start, end)
|
|
5530
|
-
};
|
|
5531
|
-
}
|
|
5532
|
-
return null;
|
|
5533
|
-
};
|
|
5534
|
-
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
5535
|
-
if (segment.orientation === "horizontal") {
|
|
5536
|
-
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
|
|
5537
|
-
}
|
|
5538
|
-
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
|
|
5539
|
-
};
|
|
5540
|
-
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
5541
|
-
const matchingSegments = getSegments(path).filter(
|
|
5542
|
-
(segment) => segment.orientation === orientation
|
|
5543
|
-
);
|
|
5544
|
-
if (matchingSegments.length === 0) return null;
|
|
5545
|
-
const containingSegments = matchingSegments.filter(
|
|
5546
|
-
(segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
|
|
5547
|
-
);
|
|
5548
|
-
const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
|
|
5549
|
-
const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
|
|
5550
|
-
return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
|
|
5551
|
-
};
|
|
5552
|
-
var projectPointToPath = (point, path) => {
|
|
5553
|
-
let bestPoint = null;
|
|
5554
|
-
let bestDistance = Number.POSITIVE_INFINITY;
|
|
5555
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5556
|
-
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
5557
|
-
const distance3 = getDistance(point, projectedPoint);
|
|
5558
|
-
if (distance3 < bestDistance) {
|
|
5559
|
-
bestPoint = projectedPoint;
|
|
5560
|
-
bestDistance = distance3;
|
|
5561
|
-
}
|
|
5562
|
-
}
|
|
5563
|
-
return bestPoint;
|
|
5564
|
-
};
|
|
5565
|
-
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
5566
|
-
const aVertical = Math.abs(a1.x - a2.x) < EPS7;
|
|
5567
|
-
const bVertical = Math.abs(b1.x - b2.x) < EPS7;
|
|
5568
|
-
const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
|
|
5569
|
-
if (aVertical && bVertical) {
|
|
5570
|
-
if (Math.abs(a1.x - b1.x) > EPS7) return false;
|
|
5571
|
-
const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
|
|
5572
|
-
return overlap > EPS7;
|
|
5573
|
-
}
|
|
5574
|
-
if (!aVertical && !bVertical) {
|
|
5575
|
-
if (Math.abs(a1.y - b1.y) > EPS7) return false;
|
|
5576
|
-
const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
|
|
5577
|
-
return overlap > EPS7;
|
|
5578
|
-
}
|
|
5579
|
-
const verticalA = aVertical ? a1 : b1;
|
|
5580
|
-
const verticalB = aVertical ? a2 : b2;
|
|
5581
|
-
const horizontalA = aVertical ? b1 : a1;
|
|
5582
|
-
const horizontalB = aVertical ? b2 : a2;
|
|
5583
|
-
return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
|
|
5584
|
-
};
|
|
5585
|
-
var countPathIntersections = (pathA, pathB) => {
|
|
5586
|
-
let count = 0;
|
|
5587
|
-
for (let i = 0; i < pathA.length - 1; i++) {
|
|
5588
|
-
for (let j = 0; j < pathB.length - 1; j++) {
|
|
5589
|
-
if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
|
|
5590
|
-
count++;
|
|
5591
|
-
}
|
|
5592
|
-
}
|
|
5593
|
-
}
|
|
5594
|
-
return count;
|
|
5595
|
-
};
|
|
5596
|
-
var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
5597
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5598
|
-
const start = path[i];
|
|
5599
|
-
const end = path[i + 1];
|
|
5600
|
-
for (const obstacle of chipObstacles) {
|
|
5601
|
-
if (!segmentIntersectsRect(start, end, obstacle)) continue;
|
|
5602
|
-
if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5603
|
-
return true;
|
|
5604
|
-
}
|
|
5605
|
-
}
|
|
5606
|
-
return false;
|
|
5607
|
-
};
|
|
5608
|
-
|
|
5609
6073
|
// lib/solvers/Example28Solver/getMovedAnchorPointForReroute.ts
|
|
5610
6074
|
var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
|
|
5611
6075
|
const originalSegment = findSegmentContainingPoint(
|
|
@@ -5668,7 +6132,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
|
|
|
5668
6132
|
const end = path[i + 1];
|
|
5669
6133
|
for (const obstacle of chipObstacles) {
|
|
5670
6134
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5671
|
-
if (getSegmentOverlapWithRectSpan(start, end, obstacle) >
|
|
6135
|
+
if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS6) {
|
|
5672
6136
|
return true;
|
|
5673
6137
|
}
|
|
5674
6138
|
}
|
|
@@ -5683,14 +6147,14 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
|
|
|
5683
6147
|
for (const obstacle of chipObstacles) {
|
|
5684
6148
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5685
6149
|
const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
|
|
5686
|
-
if (overlap >
|
|
6150
|
+
if (overlap > EPS6) total += overlap;
|
|
5687
6151
|
}
|
|
5688
6152
|
}
|
|
5689
6153
|
return total;
|
|
5690
6154
|
};
|
|
5691
6155
|
var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
5692
|
-
const
|
|
5693
|
-
if (
|
|
6156
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS6;
|
|
6157
|
+
if (isVertical5) {
|
|
5694
6158
|
return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
|
|
5695
6159
|
}
|
|
5696
6160
|
return Math.min(Math.max(start.x, end.x), rect.maxX) - Math.max(Math.min(start.x, end.x), rect.minX);
|
|
@@ -6453,16 +6917,16 @@ var createPortOnlyLabelConnectorTrace = ({
|
|
|
6453
6917
|
// lib/solvers/AvailableNetOrientationSolver/constants.ts
|
|
6454
6918
|
var LABEL_SEARCH_STEP = 0.05;
|
|
6455
6919
|
var WICK_CLEARANCE = 1e-3;
|
|
6456
|
-
var
|
|
6457
|
-
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE +
|
|
6920
|
+
var EPS9 = 1e-9;
|
|
6921
|
+
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS9;
|
|
6458
6922
|
var CANDIDATE_SELECTED_COLOR2 = "blue";
|
|
6459
6923
|
var CANDIDATE_REJECTED_COLOR2 = "red";
|
|
6460
6924
|
|
|
6461
6925
|
// lib/solvers/AvailableNetOrientationSolver/geometry.ts
|
|
6462
6926
|
var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
|
|
6463
6927
|
var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
|
|
6464
|
-
var
|
|
6465
|
-
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) >
|
|
6928
|
+
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
|
|
6929
|
+
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
|
|
6466
6930
|
var traceCrossesBoundsInterior = (bounds, traceMap) => {
|
|
6467
6931
|
for (const trace of Object.values(traceMap)) {
|
|
6468
6932
|
const points = trace.tracePath;
|
|
@@ -6485,7 +6949,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
6485
6949
|
return false;
|
|
6486
6950
|
}
|
|
6487
6951
|
if (sameX2(p1, p2)) {
|
|
6488
|
-
if (p1.x <= interiorBounds.minX +
|
|
6952
|
+
if (p1.x <= interiorBounds.minX + EPS9 || p1.x >= interiorBounds.maxX - EPS9) {
|
|
6489
6953
|
return false;
|
|
6490
6954
|
}
|
|
6491
6955
|
return rangesOverlap(
|
|
@@ -6496,7 +6960,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
6496
6960
|
);
|
|
6497
6961
|
}
|
|
6498
6962
|
if (sameY2(p1, p2)) {
|
|
6499
|
-
if (p1.y <= interiorBounds.minY +
|
|
6963
|
+
if (p1.y <= interiorBounds.minY + EPS9 || p1.y >= interiorBounds.maxY - EPS9) {
|
|
6500
6964
|
return false;
|
|
6501
6965
|
}
|
|
6502
6966
|
return rangesOverlap(
|
|
@@ -6535,7 +6999,7 @@ var getConnectorTracePath = (source, target, orientation) => simplifyOrthogonalP
|
|
|
6535
6999
|
);
|
|
6536
7000
|
var simplifyOrthogonalPath = (path) => {
|
|
6537
7001
|
const deduped = path.filter(
|
|
6538
|
-
(point, index) => index === 0 || !
|
|
7002
|
+
(point, index) => index === 0 || !pointsEqual2(point, path[index - 1])
|
|
6539
7003
|
);
|
|
6540
7004
|
if (deduped.length < 3) return deduped;
|
|
6541
7005
|
const simplified = [deduped[0]];
|
|
@@ -6551,9 +7015,9 @@ var simplifyOrthogonalPath = (path) => {
|
|
|
6551
7015
|
simplified.push(deduped[deduped.length - 1]);
|
|
6552
7016
|
return simplified;
|
|
6553
7017
|
};
|
|
6554
|
-
var
|
|
6555
|
-
var sameX2 = (a, b) => Math.abs(a.x - b.x) <=
|
|
6556
|
-
var sameY2 = (a, b) => Math.abs(a.y - b.y) <=
|
|
7018
|
+
var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
|
|
7019
|
+
var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS9;
|
|
7020
|
+
var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS9;
|
|
6557
7021
|
var getMaxSearchDistance = (inputProblem) => {
|
|
6558
7022
|
const maxChipWidth = Math.max(
|
|
6559
7023
|
...inputProblem.chips.map((chip) => chip.width),
|
|
@@ -6856,7 +7320,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6856
7320
|
);
|
|
6857
7321
|
const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
|
|
6858
7322
|
const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
|
|
6859
|
-
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance +
|
|
7323
|
+
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS9; outwardDistance += LABEL_SEARCH_STEP) {
|
|
6860
7324
|
const baseAnchor = {
|
|
6861
7325
|
x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
|
|
6862
7326
|
y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
|
|
@@ -6929,7 +7393,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6929
7393
|
outwardDistance,
|
|
6930
7394
|
phase = "shift"
|
|
6931
7395
|
} = params;
|
|
6932
|
-
for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance +
|
|
7396
|
+
for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS9; distance3 += LABEL_SEARCH_STEP) {
|
|
6933
7397
|
const anchorPoint = {
|
|
6934
7398
|
x: baseAnchor.x + direction.x * distance3,
|
|
6935
7399
|
y: baseAnchor.y + direction.y * distance3
|
|
@@ -7166,9 +7630,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7166
7630
|
const { anchorPoint, orientation } = candidate;
|
|
7167
7631
|
const chipBounds = chip.bounds;
|
|
7168
7632
|
if (isYOrientation(orientation)) {
|
|
7169
|
-
return anchorPoint.x < chipBounds.minX -
|
|
7633
|
+
return anchorPoint.x < chipBounds.minX - EPS9 || anchorPoint.x > chipBounds.maxX + EPS9;
|
|
7170
7634
|
}
|
|
7171
|
-
return anchorPoint.y < chipBounds.minY -
|
|
7635
|
+
return anchorPoint.y < chipBounds.minY - EPS9 || anchorPoint.y > chipBounds.maxY + EPS9;
|
|
7172
7636
|
});
|
|
7173
7637
|
}
|
|
7174
7638
|
getBoundsStatus(candidateBoundsCheck) {
|
|
@@ -7235,15 +7699,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7235
7699
|
if (i === labelIndex) continue;
|
|
7236
7700
|
const label = this.outputNetLabelPlacements[i];
|
|
7237
7701
|
const otherBounds = getRectBounds(label.center, label.width, label.height);
|
|
7238
|
-
if (
|
|
7702
|
+
if (rectsOverlap2(bounds, otherBounds)) return true;
|
|
7239
7703
|
}
|
|
7240
7704
|
return false;
|
|
7241
7705
|
}
|
|
7242
7706
|
sharesChipBoundary(bounds) {
|
|
7243
7707
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7244
7708
|
const chipBounds = chip.bounds;
|
|
7245
|
-
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE +
|
|
7246
|
-
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE +
|
|
7709
|
+
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS9;
|
|
7710
|
+
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS9;
|
|
7247
7711
|
if (adjacentToVerticalSide && rangesOverlap(
|
|
7248
7712
|
bounds.minY,
|
|
7249
7713
|
bounds.maxY,
|
|
@@ -7291,7 +7755,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7291
7755
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
7292
7756
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7293
7757
|
const bounds = chip.bounds;
|
|
7294
|
-
if (point.x < bounds.minX -
|
|
7758
|
+
if (point.x < bounds.minX - EPS9 || point.x > bounds.maxX + EPS9 || point.y < bounds.minY - EPS9 || point.y > bounds.maxY + EPS9) {
|
|
7295
7759
|
continue;
|
|
7296
7760
|
}
|
|
7297
7761
|
for (const [side, distance3] of getSideDistances(point, bounds)) {
|
|
@@ -7308,8 +7772,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7308
7772
|
let nearestDistanceSq = Number.POSITIVE_INFINITY;
|
|
7309
7773
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7310
7774
|
const bounds = chip.bounds;
|
|
7311
|
-
const dx = point.x < bounds.minX -
|
|
7312
|
-
const dy = point.y < bounds.minY -
|
|
7775
|
+
const dx = point.x < bounds.minX - EPS9 ? bounds.minX - point.x : point.x > bounds.maxX + EPS9 ? point.x - bounds.maxX : 0;
|
|
7776
|
+
const dy = point.y < bounds.minY - EPS9 ? bounds.minY - point.y : point.y > bounds.maxY + EPS9 ? point.y - bounds.maxY : 0;
|
|
7313
7777
|
if (dx === 0 && dy === 0) continue;
|
|
7314
7778
|
const distanceSq = dx ** 2 + dy ** 2;
|
|
7315
7779
|
if (distanceSq >= nearestDistanceSq) continue;
|
|
@@ -7347,39 +7811,6 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7347
7811
|
}
|
|
7348
7812
|
};
|
|
7349
7813
|
|
|
7350
|
-
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
7351
|
-
var EPS9 = 1e-6;
|
|
7352
|
-
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
7353
|
-
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
|
|
7354
|
-
var getTraceCorners = (path) => {
|
|
7355
|
-
const corners = [];
|
|
7356
|
-
for (let i = 1; i < path.length - 1; i++) {
|
|
7357
|
-
const previousPoint = path[i - 1];
|
|
7358
|
-
const cornerPoint = path[i];
|
|
7359
|
-
const nextPoint = path[i + 1];
|
|
7360
|
-
if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
|
|
7361
|
-
corners.push(cornerPoint);
|
|
7362
|
-
}
|
|
7363
|
-
}
|
|
7364
|
-
return corners;
|
|
7365
|
-
};
|
|
7366
|
-
var tracePathContainsPoint = (path, point) => {
|
|
7367
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
7368
|
-
if (isPointOnSegment(point, path[i], path[i + 1])) return true;
|
|
7369
|
-
}
|
|
7370
|
-
return false;
|
|
7371
|
-
};
|
|
7372
|
-
var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
7373
|
-
var getUniquePinCount = (pinIds) => new Set(pinIds).size;
|
|
7374
|
-
var isTraceCorner = (a, b, c) => getSegmentOrientation2(a, b) !== getSegmentOrientation2(b, c);
|
|
7375
|
-
var isPointOnSegment = (point, start, end) => {
|
|
7376
|
-
if (getSegmentOrientation2(start, end) === "horizontal") {
|
|
7377
|
-
return Math.abs(point.y - start.y) <= EPS9 && point.x >= Math.min(start.x, end.x) - EPS9 && point.x <= Math.max(start.x, end.x) + EPS9;
|
|
7378
|
-
}
|
|
7379
|
-
return Math.abs(point.x - start.x) <= EPS9 && point.y >= Math.min(start.y, end.y) - EPS9 && point.y <= Math.max(start.y, end.y) + EPS9;
|
|
7380
|
-
};
|
|
7381
|
-
var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS9 ? "horizontal" : "vertical";
|
|
7382
|
-
|
|
7383
7814
|
// lib/solvers/RailNetLabelCornerPlacementSolver/visualize.ts
|
|
7384
7815
|
var CANDIDATE_SELECTED_COLOR3 = "blue";
|
|
7385
7816
|
var CANDIDATE_REJECTED_COLOR3 = "red";
|
|
@@ -7602,20 +8033,20 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7602
8033
|
}
|
|
7603
8034
|
intersectsAnyChip(bounds) {
|
|
7604
8035
|
return this.inputProblem.chips.some(
|
|
7605
|
-
(chip) =>
|
|
8036
|
+
(chip) => rectsOverlap(bounds, getRectBounds(chip.center, chip.width, chip.height))
|
|
7606
8037
|
);
|
|
7607
8038
|
}
|
|
7608
8039
|
intersectsAnyOtherNetLabel(bounds, labelIndex) {
|
|
7609
8040
|
return this.outputNetLabelPlacements.some((label, index) => {
|
|
7610
8041
|
if (index === labelIndex) return false;
|
|
7611
|
-
return
|
|
8042
|
+
return rectsOverlap(
|
|
7612
8043
|
bounds,
|
|
7613
8044
|
getRectBounds(label.center, label.width, label.height)
|
|
7614
8045
|
);
|
|
7615
8046
|
});
|
|
7616
8047
|
}
|
|
7617
8048
|
getCornerCandidatesForLabel(label) {
|
|
7618
|
-
const
|
|
8049
|
+
const isVertical5 = label.orientation === "y+" || label.orientation === "y-";
|
|
7619
8050
|
const candidates = [];
|
|
7620
8051
|
const seenCornerKeys = /* @__PURE__ */ new Set();
|
|
7621
8052
|
for (const trace of this.getTraceLinesForLabel(label)) {
|
|
@@ -7626,14 +8057,14 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7626
8057
|
if (seenCornerKeys.has(key)) continue;
|
|
7627
8058
|
seenCornerKeys.add(key);
|
|
7628
8059
|
const pinAligned = pins.some((pin) => {
|
|
7629
|
-
if (
|
|
7630
|
-
return Math.abs(pin.y - anchorPoint.y) <
|
|
8060
|
+
if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS5;
|
|
8061
|
+
return Math.abs(pin.y - anchorPoint.y) < EPS5;
|
|
7631
8062
|
});
|
|
7632
8063
|
if (!pinAligned) continue;
|
|
7633
8064
|
candidates.push({
|
|
7634
8065
|
anchorPoint,
|
|
7635
8066
|
traceId: trace.mspPairId,
|
|
7636
|
-
distance:
|
|
8067
|
+
distance: getDistance(anchorPoint, label.anchorPoint),
|
|
7637
8068
|
pinAligned
|
|
7638
8069
|
});
|
|
7639
8070
|
}
|
|
@@ -7808,7 +8239,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
7808
8239
|
if (interior.minX >= interior.maxX || interior.minY >= interior.maxY) {
|
|
7809
8240
|
return false;
|
|
7810
8241
|
}
|
|
7811
|
-
if (
|
|
8242
|
+
if (isVertical4(start, end)) {
|
|
7812
8243
|
if (start.x <= interior.minX + EPS10 || start.x >= interior.maxX - EPS10) {
|
|
7813
8244
|
return false;
|
|
7814
8245
|
}
|
|
@@ -7819,7 +8250,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
7819
8250
|
interior.maxY
|
|
7820
8251
|
);
|
|
7821
8252
|
}
|
|
7822
|
-
if (
|
|
8253
|
+
if (isHorizontal3(start, end)) {
|
|
7823
8254
|
if (start.y <= interior.minY + EPS10 || start.y >= interior.maxY - EPS10) {
|
|
7824
8255
|
return false;
|
|
7825
8256
|
}
|
|
@@ -7833,20 +8264,20 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
7833
8264
|
return false;
|
|
7834
8265
|
};
|
|
7835
8266
|
var isPointOnSegment2 = (point, start, end) => {
|
|
7836
|
-
if (
|
|
8267
|
+
if (isHorizontal3(start, end)) {
|
|
7837
8268
|
return Math.abs(point.y - start.y) <= EPS10 && point.x >= Math.min(start.x, end.x) - EPS10 && point.x <= Math.max(start.x, end.x) + EPS10;
|
|
7838
8269
|
}
|
|
7839
|
-
if (
|
|
8270
|
+
if (isVertical4(start, end)) {
|
|
7840
8271
|
return Math.abs(point.x - start.x) <= EPS10 && point.y >= Math.min(start.y, end.y) - EPS10 && point.y <= Math.max(start.y, end.y) + EPS10;
|
|
7841
8272
|
}
|
|
7842
8273
|
return false;
|
|
7843
8274
|
};
|
|
7844
8275
|
var getSegmentDirection = (start, end) => ({
|
|
7845
|
-
x:
|
|
7846
|
-
y:
|
|
8276
|
+
x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
|
|
8277
|
+
y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
|
|
7847
8278
|
});
|
|
7848
|
-
var
|
|
7849
|
-
var
|
|
8279
|
+
var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
|
|
8280
|
+
var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
|
|
7850
8281
|
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
|
|
7851
8282
|
var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
|
|
7852
8283
|
|
|
@@ -8855,11 +9286,11 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
8855
9286
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
8856
9287
|
const segStart = pts[si];
|
|
8857
9288
|
const segEnd = pts[si + 1];
|
|
8858
|
-
const
|
|
8859
|
-
const
|
|
8860
|
-
if (!
|
|
9289
|
+
const isHorizontal4 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
|
|
9290
|
+
const isVertical5 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
|
|
9291
|
+
if (!isHorizontal4 && !isVertical5) continue;
|
|
8861
9292
|
let perpendicularOrientations;
|
|
8862
|
-
if (
|
|
9293
|
+
if (isHorizontal4) {
|
|
8863
9294
|
perpendicularOrientations = ["y+", "y-"];
|
|
8864
9295
|
} else {
|
|
8865
9296
|
perpendicularOrientations = ["x+", "x-"];
|
|
@@ -9072,7 +9503,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9072
9503
|
availableNetOrientationSolver;
|
|
9073
9504
|
railNetLabelCornerPlacementSolver;
|
|
9074
9505
|
traceAnchoredNetLabelOverlapSolver;
|
|
9506
|
+
preAlignmentNetLabelTraceCollisionSolver;
|
|
9075
9507
|
netLabelTraceCollisionSolver;
|
|
9508
|
+
traceCleanupSolver2;
|
|
9076
9509
|
netLabelNetLabelCollisionSolver;
|
|
9077
9510
|
startTimeOfPhase;
|
|
9078
9511
|
endTimeOfPhase;
|
|
@@ -9257,7 +9690,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9257
9690
|
]
|
|
9258
9691
|
),
|
|
9259
9692
|
definePipelineStep(
|
|
9260
|
-
"
|
|
9693
|
+
"preAlignmentNetLabelTraceCollisionSolver",
|
|
9261
9694
|
NetLabelTraceCollisionSolver,
|
|
9262
9695
|
(instance) => [
|
|
9263
9696
|
{
|
|
@@ -9267,6 +9700,41 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9267
9700
|
}
|
|
9268
9701
|
]
|
|
9269
9702
|
),
|
|
9703
|
+
definePipelineStep(
|
|
9704
|
+
"traceCleanupSolver2",
|
|
9705
|
+
TraceCleanupSolver,
|
|
9706
|
+
(instance) => {
|
|
9707
|
+
const collisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
|
|
9708
|
+
const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
|
|
9709
|
+
return [
|
|
9710
|
+
{
|
|
9711
|
+
inputProblem: instance.inputProblem,
|
|
9712
|
+
allTraces: collisionOutput.traces,
|
|
9713
|
+
allLabelPlacements: collisionOutput.netLabelPlacements,
|
|
9714
|
+
mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
|
|
9715
|
+
paddingBuffer: 0.1,
|
|
9716
|
+
operations: ["aligning_same_net_rails"],
|
|
9717
|
+
eligibleTraceIds: new Set(
|
|
9718
|
+
instance.traceCleanupSolver.getOutput().traces.map((trace) => trace.mspPairId)
|
|
9719
|
+
)
|
|
9720
|
+
}
|
|
9721
|
+
];
|
|
9722
|
+
}
|
|
9723
|
+
),
|
|
9724
|
+
definePipelineStep(
|
|
9725
|
+
"netLabelTraceCollisionSolver",
|
|
9726
|
+
NetLabelTraceCollisionSolver,
|
|
9727
|
+
(instance) => {
|
|
9728
|
+
const previousCollisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
|
|
9729
|
+
return [
|
|
9730
|
+
{
|
|
9731
|
+
inputProblem: instance.inputProblem,
|
|
9732
|
+
traces: instance.traceCleanupSolver2.getOutput().traces,
|
|
9733
|
+
netLabelPlacements: previousCollisionOutput.netLabelPlacements
|
|
9734
|
+
}
|
|
9735
|
+
];
|
|
9736
|
+
}
|
|
9737
|
+
),
|
|
9270
9738
|
definePipelineStep(
|
|
9271
9739
|
"netLabelNetLabelCollisionSolver",
|
|
9272
9740
|
NetLabelNetLabelCollisionSolver,
|