@tscircuit/schematic-trace-solver 0.0.21 → 0.0.23
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/.github/workflows/bun-pver-release.yml +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +184 -90
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +10 -1
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts +3 -1
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts +234 -87
- package/package.json +1 -1
- package/site/MspConnectionPairSolver/MspConnectionPairSolver01_params.json +1 -1
- package/site/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver01.page.tsx +894 -0
- package/site/examples/example01-basic.page.tsx +1 -1
- package/site/examples/example05.page.tsx +1 -1
- package/site/examples/example06.page.tsx +1 -1
- package/site/examples/example09.page.tsx +417 -0
- package/site/examples/example10.page.tsx +78 -0
- package/tests/functions/generateElbowVariants.test.ts +1 -1
- package/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro2.test.ts +92 -0
- package/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro02.test.ts +904 -0
package/dist/index.d.ts
CHANGED
|
@@ -161,6 +161,7 @@ declare class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
161
161
|
chipMap: Record<string, InputChip>;
|
|
162
162
|
movableSegments: Array<MovableSegment>;
|
|
163
163
|
baseElbow: Point[];
|
|
164
|
+
allCandidatePaths: Array<Point[]>;
|
|
164
165
|
queuedCandidatePaths: Array<Point[]>;
|
|
165
166
|
chipObstacleSpatialIndex: ChipObstacleSpatialIndex;
|
|
166
167
|
solvedTracePath: {
|
package/dist/index.js
CHANGED
|
@@ -5503,6 +5503,12 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
5503
5503
|
}
|
|
5504
5504
|
if (directlyConnectedPins.length === 2) {
|
|
5505
5505
|
const [pin1, pin2] = directlyConnectedPins;
|
|
5506
|
+
const p1 = this.pinMap[pin1];
|
|
5507
|
+
const p2 = this.pinMap[pin2];
|
|
5508
|
+
const manhattanDist = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
|
|
5509
|
+
if (manhattanDist > this.maxMspPairDistance) {
|
|
5510
|
+
return;
|
|
5511
|
+
}
|
|
5506
5512
|
const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
|
|
5507
5513
|
const userNetId = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
|
|
5508
5514
|
this.mspConnectionPairs.push({
|
|
@@ -5510,7 +5516,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
5510
5516
|
dcConnNetId: dcNetId,
|
|
5511
5517
|
globalConnNetId,
|
|
5512
5518
|
userNetId,
|
|
5513
|
-
pins: [
|
|
5519
|
+
pins: [p1, p2]
|
|
5514
5520
|
});
|
|
5515
5521
|
return;
|
|
5516
5522
|
}
|
|
@@ -6520,81 +6526,167 @@ var dir = (facingDirection) => {
|
|
|
6520
6526
|
};
|
|
6521
6527
|
|
|
6522
6528
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts
|
|
6523
|
-
var
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
const
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6529
|
+
var EPS = 1e-6;
|
|
6530
|
+
var MIN_LEN = 1e-6;
|
|
6531
|
+
var isAxisAligned = (a, b) => Math.abs(a.x - b.x) < EPS || Math.abs(a.y - b.y) < EPS;
|
|
6532
|
+
var orientationOf = (a, b) => {
|
|
6533
|
+
const dx = Math.abs(a.x - b.x);
|
|
6534
|
+
const dy = Math.abs(a.y - b.y);
|
|
6535
|
+
if (dx < EPS && dy >= EPS) return "vertical";
|
|
6536
|
+
if (dy < EPS && dx >= EPS) return "horizontal";
|
|
6537
|
+
throw new Error("Non-orthogonal or degenerate segment detected.");
|
|
6538
|
+
};
|
|
6539
|
+
var assertOrthogonalPolyline = (pts) => {
|
|
6540
|
+
if (pts.length < 2) {
|
|
6541
|
+
throw new Error("Polyline must have at least two points.");
|
|
6542
|
+
}
|
|
6543
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
6544
|
+
const a = pts[i];
|
|
6545
|
+
const b = pts[i + 1];
|
|
6546
|
+
if (!isAxisAligned(a, b)) {
|
|
6547
|
+
throw new Error("Polyline contains a non-orthogonal segment.");
|
|
6548
|
+
}
|
|
6549
|
+
const manhattan = Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
6550
|
+
if (manhattan < MIN_LEN) {
|
|
6551
|
+
throw new Error("Polyline contains a near-zero length segment.");
|
|
6538
6552
|
}
|
|
6539
|
-
movableSegments.push({ start, end, freedom, dir: dir(freedom) });
|
|
6540
6553
|
}
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6554
|
+
};
|
|
6555
|
+
var uniqSortedWithTol = (vals, tol = EPS) => {
|
|
6556
|
+
const sorted = vals.slice().sort((a, b) => a - b);
|
|
6557
|
+
const out = [];
|
|
6558
|
+
for (const v of sorted) {
|
|
6559
|
+
if (out.length === 0 || Math.abs(v - out[out.length - 1]) > tol) out.push(v);
|
|
6560
|
+
}
|
|
6561
|
+
return out;
|
|
6562
|
+
};
|
|
6563
|
+
var keyForPolyline = (pts, decimals = 9) => pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|");
|
|
6564
|
+
var preprocessElbow = (pts, tol = MIN_LEN) => {
|
|
6565
|
+
if (pts.length === 0) return [];
|
|
6566
|
+
const out = [{ ...pts[0] }];
|
|
6567
|
+
for (let i = 1; i < pts.length; i++) {
|
|
6568
|
+
const p = pts[i];
|
|
6569
|
+
const last = out[out.length - 1];
|
|
6570
|
+
const manhattan = Math.abs(p.x - last.x) + Math.abs(p.y - last.y);
|
|
6571
|
+
if (manhattan > tol) out.push({ ...p });
|
|
6572
|
+
}
|
|
6573
|
+
return out;
|
|
6574
|
+
};
|
|
6575
|
+
var collectAxisCandidates = (axis, guidelines, currentCoord) => {
|
|
6576
|
+
const positions = [];
|
|
6577
|
+
for (const g of guidelines) {
|
|
6578
|
+
if (axis === "x") {
|
|
6579
|
+
if (g.orientation === "vertical" && typeof g.x === "number") {
|
|
6580
|
+
positions.push(g.x);
|
|
6549
6581
|
}
|
|
6550
|
-
relevantPositions.push(segment.start.x);
|
|
6551
6582
|
} else {
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
relevantPositions.push(guideline.y);
|
|
6555
|
-
}
|
|
6583
|
+
if (g.orientation === "horizontal" && typeof g.y === "number") {
|
|
6584
|
+
positions.push(g.y);
|
|
6556
6585
|
}
|
|
6557
|
-
relevantPositions.push(segment.start.y);
|
|
6558
6586
|
}
|
|
6559
|
-
segmentGuidelineOptions.push(
|
|
6560
|
-
[...new Set(relevantPositions)].sort((a, b) => a - b)
|
|
6561
|
-
);
|
|
6562
6587
|
}
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6588
|
+
positions.push(currentCoord);
|
|
6589
|
+
return uniqSortedWithTol(positions);
|
|
6590
|
+
};
|
|
6591
|
+
var computeFreedom = (prev, start, end) => {
|
|
6592
|
+
const orient = orientationOf(start, end);
|
|
6593
|
+
if (orient === "horizontal") {
|
|
6594
|
+
const facing = prev.y <= start.y ? "y+" : "y-";
|
|
6595
|
+
return { axis: "y", facing };
|
|
6596
|
+
} else {
|
|
6597
|
+
const facing = prev.x <= start.x ? "x+" : "x-";
|
|
6598
|
+
return { axis: "x", facing };
|
|
6599
|
+
}
|
|
6600
|
+
};
|
|
6601
|
+
var cartesian = (arrays) => arrays.length === 0 ? [[]] : arrays.reduce(
|
|
6602
|
+
(acc, curr) => acc.flatMap((a) => curr.map((c) => [...a, c])),
|
|
6603
|
+
[[]]
|
|
6604
|
+
);
|
|
6605
|
+
var generateElbowVariants = ({
|
|
6606
|
+
baseElbow,
|
|
6607
|
+
guidelines
|
|
6608
|
+
}) => {
|
|
6609
|
+
const elbow = preprocessElbow(baseElbow);
|
|
6610
|
+
assertOrthogonalPolyline(elbow);
|
|
6611
|
+
const nPts = elbow.length;
|
|
6612
|
+
const nSegs = nPts - 1;
|
|
6613
|
+
if (nSegs < 5) {
|
|
6614
|
+
return {
|
|
6615
|
+
elbowVariants: [elbow.map((p) => ({ ...p }))],
|
|
6616
|
+
movableSegments: []
|
|
6617
|
+
};
|
|
6618
|
+
}
|
|
6619
|
+
const firstMovableIndex = 2;
|
|
6620
|
+
const lastMovableIndex = nPts - 4;
|
|
6621
|
+
const movableSegments = [];
|
|
6622
|
+
const movableIdx = [];
|
|
6623
|
+
const axes = [];
|
|
6624
|
+
const optionsPerSegment = [];
|
|
6625
|
+
for (let i = firstMovableIndex; i <= lastMovableIndex; i++) {
|
|
6626
|
+
const prev = elbow[i - 1];
|
|
6627
|
+
const start = elbow[i];
|
|
6628
|
+
const end = elbow[i + 1];
|
|
6629
|
+
const next2 = elbow[i + 2];
|
|
6630
|
+
const { axis, facing } = computeFreedom(prev, start, end);
|
|
6631
|
+
movableSegments.push({
|
|
6632
|
+
start: { ...start },
|
|
6633
|
+
end: { ...end },
|
|
6634
|
+
freedom: facing,
|
|
6635
|
+
dir: dir(facing)
|
|
6636
|
+
});
|
|
6637
|
+
movableIdx.push(i);
|
|
6638
|
+
axes.push(axis);
|
|
6639
|
+
const currentCoord = axis === "x" ? start.x : start.y;
|
|
6640
|
+
const rawCandidates = collectAxisCandidates(axis, guidelines, currentCoord);
|
|
6641
|
+
const filtered = rawCandidates.filter((pos) => {
|
|
6642
|
+
if (axis === "y") {
|
|
6643
|
+
const lenPrev = Math.abs(prev.y - pos);
|
|
6644
|
+
const lenNext = Math.abs(next2.y - pos);
|
|
6645
|
+
return lenPrev > MIN_LEN && lenNext > MIN_LEN;
|
|
6646
|
+
} else {
|
|
6647
|
+
const lenPrev = Math.abs(prev.x - pos);
|
|
6648
|
+
const lenNext = Math.abs(next2.x - pos);
|
|
6649
|
+
return lenPrev > MIN_LEN && lenNext > MIN_LEN;
|
|
6572
6650
|
}
|
|
6573
|
-
}
|
|
6574
|
-
|
|
6575
|
-
}
|
|
6576
|
-
const
|
|
6651
|
+
});
|
|
6652
|
+
optionsPerSegment.push(filtered);
|
|
6653
|
+
}
|
|
6654
|
+
const combos = cartesian(optionsPerSegment);
|
|
6655
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6577
6656
|
const elbowVariants = [];
|
|
6578
|
-
|
|
6579
|
-
const
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6657
|
+
{
|
|
6658
|
+
const key = keyForPolyline(elbow);
|
|
6659
|
+
seen.add(key);
|
|
6660
|
+
elbowVariants.push(elbow.map((p) => ({ ...p })));
|
|
6661
|
+
}
|
|
6662
|
+
for (const combo of combos) {
|
|
6663
|
+
if (combo.length === 0) continue;
|
|
6664
|
+
const variant = elbow.map((p) => ({ ...p }));
|
|
6665
|
+
for (let k = 0; k < movableIdx.length; k++) {
|
|
6666
|
+
const segIndex = movableIdx[k];
|
|
6667
|
+
const axis = axes[k];
|
|
6668
|
+
const newPos = combo[k];
|
|
6669
|
+
if (typeof newPos !== "number" || Number.isNaN(newPos)) continue;
|
|
6670
|
+
if (axis === "y") {
|
|
6671
|
+
variant[segIndex] = { ...variant[segIndex], y: newPos };
|
|
6672
|
+
variant[segIndex + 1] = { ...variant[segIndex + 1], y: newPos };
|
|
6587
6673
|
} else {
|
|
6588
|
-
variant[
|
|
6589
|
-
variant[
|
|
6674
|
+
variant[segIndex] = { ...variant[segIndex], x: newPos };
|
|
6675
|
+
variant[segIndex + 1] = { ...variant[segIndex + 1], x: newPos };
|
|
6590
6676
|
}
|
|
6591
6677
|
}
|
|
6592
|
-
|
|
6678
|
+
try {
|
|
6679
|
+
assertOrthogonalPolyline(variant);
|
|
6680
|
+
} catch {
|
|
6681
|
+
continue;
|
|
6682
|
+
}
|
|
6683
|
+
const key = keyForPolyline(variant);
|
|
6684
|
+
if (!seen.has(key)) {
|
|
6685
|
+
seen.add(key);
|
|
6686
|
+
elbowVariants.push(variant);
|
|
6687
|
+
}
|
|
6593
6688
|
}
|
|
6594
|
-
return {
|
|
6595
|
-
elbowVariants,
|
|
6596
|
-
movableSegments
|
|
6597
|
-
};
|
|
6689
|
+
return { elbowVariants, movableSegments };
|
|
6598
6690
|
};
|
|
6599
6691
|
|
|
6600
6692
|
// lib/solvers/GuidelinesSolver/visualizeGuidelines.ts
|
|
@@ -6642,6 +6734,7 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
6642
6734
|
chipMap;
|
|
6643
6735
|
movableSegments;
|
|
6644
6736
|
baseElbow;
|
|
6737
|
+
allCandidatePaths;
|
|
6645
6738
|
queuedCandidatePaths;
|
|
6646
6739
|
chipObstacleSpatialIndex;
|
|
6647
6740
|
solvedTracePath = null;
|
|
@@ -6691,7 +6784,8 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
6691
6784
|
}
|
|
6692
6785
|
return len;
|
|
6693
6786
|
};
|
|
6694
|
-
this.
|
|
6787
|
+
this.allCandidatePaths = [this.baseElbow, ...elbowVariants];
|
|
6788
|
+
this.queuedCandidatePaths = [...this.allCandidatePaths].sort(
|
|
6695
6789
|
(a, b) => getPathLength(a) - getPathLength(b)
|
|
6696
6790
|
);
|
|
6697
6791
|
}
|
|
@@ -6728,12 +6822,12 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
6728
6822
|
const bounds = getInputChipBounds(this.chipMap[startPin.chipId]);
|
|
6729
6823
|
const dx = end.x - start.x;
|
|
6730
6824
|
const dy = end.y - start.y;
|
|
6731
|
-
const
|
|
6825
|
+
const EPS2 = 1e-9;
|
|
6732
6826
|
const onLeft = Math.abs(start.x - bounds.minX) < 1e-9;
|
|
6733
6827
|
const onRight = Math.abs(start.x - bounds.maxX) < 1e-9;
|
|
6734
6828
|
const onBottom = Math.abs(start.y - bounds.minY) < 1e-9;
|
|
6735
6829
|
const onTop = Math.abs(start.y - bounds.maxY) < 1e-9;
|
|
6736
|
-
const entersInterior = onLeft && dx >
|
|
6830
|
+
const entersInterior = onLeft && dx > EPS2 || onRight && dx < -EPS2 || onBottom && dy > EPS2 || onTop && dy < -EPS2;
|
|
6737
6831
|
if (entersInterior) return;
|
|
6738
6832
|
if (!excludeChipIds.includes(startPin.chipId)) {
|
|
6739
6833
|
excludeChipIds.push(startPin.chipId);
|
|
@@ -6748,12 +6842,12 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
6748
6842
|
const bounds = getInputChipBounds(this.chipMap[endPin.chipId]);
|
|
6749
6843
|
const dx = start.x - end.x;
|
|
6750
6844
|
const dy = start.y - end.y;
|
|
6751
|
-
const
|
|
6845
|
+
const EPS2 = 1e-9;
|
|
6752
6846
|
const onLeft = Math.abs(end.x - bounds.minX) < 1e-9;
|
|
6753
6847
|
const onRight = Math.abs(end.x - bounds.maxX) < 1e-9;
|
|
6754
6848
|
const onBottom = Math.abs(end.y - bounds.minY) < 1e-9;
|
|
6755
6849
|
const onTop = Math.abs(end.y - bounds.maxY) < 1e-9;
|
|
6756
|
-
const entersInterior = onLeft && dx >
|
|
6850
|
+
const entersInterior = onLeft && dx > EPS2 || onRight && dx < -EPS2 || onBottom && dy > EPS2 || onTop && dy < -EPS2;
|
|
6757
6851
|
if (entersInterior) return;
|
|
6758
6852
|
if (!excludeChipIds.includes(endPin.chipId)) {
|
|
6759
6853
|
excludeChipIds.push(endPin.chipId);
|
|
@@ -6956,13 +7050,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
6956
7050
|
}
|
|
6957
7051
|
}
|
|
6958
7052
|
_step() {
|
|
6959
|
-
const
|
|
7053
|
+
const EPS2 = 1e-6;
|
|
6960
7054
|
const offsets = this.overlappingTraceSegments.map((_, idx) => {
|
|
6961
7055
|
const n = Math.floor(idx / 2) + 1;
|
|
6962
7056
|
const signed = idx % 2 === 0 ? -n : n;
|
|
6963
7057
|
return signed * this.SHIFT_DISTANCE;
|
|
6964
7058
|
});
|
|
6965
|
-
const eq = (a, b) => Math.abs(a - b) <
|
|
7059
|
+
const eq = (a, b) => Math.abs(a - b) < EPS2;
|
|
6966
7060
|
const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
|
|
6967
7061
|
this.overlappingTraceSegments.forEach((group, gidx) => {
|
|
6968
7062
|
const offset = offsets[gidx];
|
|
@@ -6985,8 +7079,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
6985
7079
|
if (si === 0 || si === pts.length - 2) continue;
|
|
6986
7080
|
const start = pts[si];
|
|
6987
7081
|
const end = pts[si + 1];
|
|
6988
|
-
const isVertical = Math.abs(start.x - end.x) <
|
|
6989
|
-
const isHorizontal = Math.abs(start.y - end.y) <
|
|
7082
|
+
const isVertical = Math.abs(start.x - end.x) < EPS2;
|
|
7083
|
+
const isHorizontal = Math.abs(start.y - end.y) < EPS2;
|
|
6990
7084
|
if (!isVertical && !isHorizontal) continue;
|
|
6991
7085
|
if (isVertical) {
|
|
6992
7086
|
if (!appliedX.has(si)) {
|
|
@@ -7095,7 +7189,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
7095
7189
|
return islands;
|
|
7096
7190
|
}
|
|
7097
7191
|
findNextOverlapIssue() {
|
|
7098
|
-
const
|
|
7192
|
+
const EPS2 = 1e-6;
|
|
7099
7193
|
const netIds = Object.keys(this.traceNetIslands);
|
|
7100
7194
|
for (let i = 0; i < netIds.length; i++) {
|
|
7101
7195
|
for (let j = i + 1; j < netIds.length; j++) {
|
|
@@ -7113,7 +7207,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
7113
7207
|
const minB = Math.min(b1, b2);
|
|
7114
7208
|
const maxB = Math.max(b1, b2);
|
|
7115
7209
|
const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
|
|
7116
|
-
return overlap >
|
|
7210
|
+
return overlap > EPS2;
|
|
7117
7211
|
};
|
|
7118
7212
|
for (let pa = 0; pa < pathsA.length; pa++) {
|
|
7119
7213
|
const pathA = pathsA[pa];
|
|
@@ -7121,8 +7215,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
7121
7215
|
for (let sa = 0; sa < ptsA.length - 1; sa++) {
|
|
7122
7216
|
const a1 = ptsA[sa];
|
|
7123
7217
|
const a2 = ptsA[sa + 1];
|
|
7124
|
-
const aVert = Math.abs(a1.x - a2.x) <
|
|
7125
|
-
const aHorz = Math.abs(a1.y - a2.y) <
|
|
7218
|
+
const aVert = Math.abs(a1.x - a2.x) < EPS2;
|
|
7219
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS2;
|
|
7126
7220
|
if (!aVert && !aHorz) continue;
|
|
7127
7221
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
7128
7222
|
const pathB = pathsB[pb];
|
|
@@ -7130,11 +7224,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
7130
7224
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
7131
7225
|
const b1 = ptsB[sb];
|
|
7132
7226
|
const b2 = ptsB[sb + 1];
|
|
7133
|
-
const bVert = Math.abs(b1.x - b2.x) <
|
|
7134
|
-
const bHorz = Math.abs(b1.y - b2.y) <
|
|
7227
|
+
const bVert = Math.abs(b1.x - b2.x) < EPS2;
|
|
7228
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS2;
|
|
7135
7229
|
if (!bVert && !bHorz) continue;
|
|
7136
7230
|
if (aVert && bVert) {
|
|
7137
|
-
if (Math.abs(a1.x - b1.x) <
|
|
7231
|
+
if (Math.abs(a1.x - b1.x) < EPS2) {
|
|
7138
7232
|
if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
|
|
7139
7233
|
const keyA = `${pa}:${sa}`;
|
|
7140
7234
|
const keyB = `${pb}:${sb}`;
|
|
@@ -7155,7 +7249,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
7155
7249
|
}
|
|
7156
7250
|
}
|
|
7157
7251
|
} else if (aHorz && bHorz) {
|
|
7158
|
-
if (Math.abs(a1.y - b1.y) <
|
|
7252
|
+
if (Math.abs(a1.y - b1.y) < EPS2) {
|
|
7159
7253
|
if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
|
|
7160
7254
|
const keyA = `${pa}:${sa}`;
|
|
7161
7255
|
const keyB = `${pb}:${sb}`;
|
|
@@ -7270,24 +7364,24 @@ function getRectBounds(center, w, h) {
|
|
|
7270
7364
|
}
|
|
7271
7365
|
|
|
7272
7366
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
7273
|
-
function segmentIntersectsRect(p1, p2, rect,
|
|
7274
|
-
const isVert = Math.abs(p1.x - p2.x) <
|
|
7275
|
-
const isHorz = Math.abs(p1.y - p2.y) <
|
|
7367
|
+
function segmentIntersectsRect(p1, p2, rect, EPS2 = 1e-9) {
|
|
7368
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS2;
|
|
7369
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS2;
|
|
7276
7370
|
if (!isVert && !isHorz) return false;
|
|
7277
7371
|
if (isVert) {
|
|
7278
7372
|
const x = p1.x;
|
|
7279
|
-
if (x < rect.minX -
|
|
7373
|
+
if (x < rect.minX - EPS2 || x > rect.maxX + EPS2) return false;
|
|
7280
7374
|
const segMinY = Math.min(p1.y, p2.y);
|
|
7281
7375
|
const segMaxY = Math.max(p1.y, p2.y);
|
|
7282
7376
|
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
7283
|
-
return overlap >
|
|
7377
|
+
return overlap > EPS2;
|
|
7284
7378
|
} else {
|
|
7285
7379
|
const y = p1.y;
|
|
7286
|
-
if (y < rect.minY -
|
|
7380
|
+
if (y < rect.minY - EPS2 || y > rect.maxY + EPS2) return false;
|
|
7287
7381
|
const segMinX = Math.min(p1.x, p2.x);
|
|
7288
7382
|
const segMaxX = Math.max(p1.x, p2.x);
|
|
7289
7383
|
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
7290
|
-
return overlap >
|
|
7384
|
+
return overlap > EPS2;
|
|
7291
7385
|
}
|
|
7292
7386
|
}
|
|
7293
7387
|
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
@@ -7612,14 +7706,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
7612
7706
|
};
|
|
7613
7707
|
let bestCandidate = null;
|
|
7614
7708
|
let bestScore = -Infinity;
|
|
7615
|
-
const
|
|
7709
|
+
const EPS2 = 1e-6;
|
|
7616
7710
|
for (const curr of tracesToScan) {
|
|
7617
7711
|
const pts = curr.tracePath.slice();
|
|
7618
7712
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
7619
7713
|
const a = pts[si];
|
|
7620
7714
|
const b = pts[si + 1];
|
|
7621
|
-
const isH = Math.abs(a.y - b.y) <
|
|
7622
|
-
const isV = Math.abs(a.x - b.x) <
|
|
7715
|
+
const isH = Math.abs(a.y - b.y) < EPS2;
|
|
7716
|
+
const isV = Math.abs(a.x - b.x) < EPS2;
|
|
7623
7717
|
if (!isH && !isV) continue;
|
|
7624
7718
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
7625
7719
|
const candidateOrients = orientations.filter(
|
|
@@ -96,6 +96,15 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
96
96
|
|
|
97
97
|
if (directlyConnectedPins.length === 2) {
|
|
98
98
|
const [pin1, pin2] = directlyConnectedPins
|
|
99
|
+
const p1 = this.pinMap[pin1!]!
|
|
100
|
+
const p2 = this.pinMap[pin2!]!
|
|
101
|
+
// Enforce max pair distance (use Manhattan to match orthogonal routing metric)
|
|
102
|
+
const manhattanDist = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)
|
|
103
|
+
if (manhattanDist > this.maxMspPairDistance) {
|
|
104
|
+
// Too far apart; skip creating an MSP pair for this net
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1!)!
|
|
100
109
|
const userNetId =
|
|
101
110
|
this.userNetIdByPinId[pin1!] ?? this.userNetIdByPinId[pin2!]
|
|
@@ -105,7 +114,7 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
105
114
|
dcConnNetId: dcNetId,
|
|
106
115
|
globalConnNetId,
|
|
107
116
|
userNetId,
|
|
108
|
-
pins: [
|
|
117
|
+
pins: [p1, p2],
|
|
109
118
|
})
|
|
110
119
|
|
|
111
120
|
return
|
|
@@ -24,6 +24,7 @@ export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
24
24
|
movableSegments: Array<MovableSegment>
|
|
25
25
|
baseElbow: Point[]
|
|
26
26
|
|
|
27
|
+
allCandidatePaths: Array<Point[]>
|
|
27
28
|
queuedCandidatePaths: Array<Point[]>
|
|
28
29
|
|
|
29
30
|
chipObstacleSpatialIndex: ChipObstacleSpatialIndex
|
|
@@ -91,7 +92,8 @@ export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
91
92
|
return len
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
this.
|
|
95
|
+
this.allCandidatePaths = [this.baseElbow, ...elbowVariants]
|
|
96
|
+
this.queuedCandidatePaths = [...this.allCandidatePaths].sort(
|
|
95
97
|
(a, b) => getPathLength(a) - getPathLength(b),
|
|
96
98
|
)
|
|
97
99
|
}
|