@tscircuit/schematic-trace-solver 0.0.102 → 0.0.104
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 +1 -0
- package/dist/index.js +74 -15
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +58 -3
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +45 -15
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260717T042845Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260708T053736Z/__snapshots__/bug-report-20260708T053736Z.snap.svg +2 -6
- package/tests/bug-reports/bug-report-20260717T031704Z/__snapshots__/bug-report-20260717T031704Z.snap.svg +6 -6
- package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +54 -0
- package/tests/bug-reports/bug-report-20260717T042845Z/bug-report-20260717T042845Z.json +113 -0
- package/tests/bug-reports/bug-report-20260717T042845Z/bug-report-20260717T042845Z.test.ts +12 -0
package/dist/index.d.ts
CHANGED
|
@@ -760,6 +760,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
760
760
|
private finish;
|
|
761
761
|
private setCurrentLabel;
|
|
762
762
|
private findCorrectedCandidate;
|
|
763
|
+
private hasTraceContinuingInOrientation;
|
|
763
764
|
private findValidRotatedCandidate;
|
|
764
765
|
private findValidTraceAnchorCandidate;
|
|
765
766
|
private getTraceAnchorCandidatePoints;
|
package/dist/index.js
CHANGED
|
@@ -1051,6 +1051,23 @@ var getObstacleRects = (problem, opts = {}) => {
|
|
|
1051
1051
|
};
|
|
1052
1052
|
|
|
1053
1053
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
1054
|
+
var calculateElbowForPins = ({
|
|
1055
|
+
pin1,
|
|
1056
|
+
pin2,
|
|
1057
|
+
overshoot
|
|
1058
|
+
}) => calculateElbow2(
|
|
1059
|
+
{
|
|
1060
|
+
x: pin1.x,
|
|
1061
|
+
y: pin1.y,
|
|
1062
|
+
facingDirection: pin1._facingDirection
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
x: pin2.x,
|
|
1066
|
+
y: pin2.y,
|
|
1067
|
+
facingDirection: pin2._facingDirection
|
|
1068
|
+
},
|
|
1069
|
+
{ overshoot }
|
|
1070
|
+
);
|
|
1054
1071
|
var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
1055
1072
|
pins;
|
|
1056
1073
|
connectionPair;
|
|
@@ -1090,19 +1107,25 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1090
1107
|
);
|
|
1091
1108
|
const [pin1, pin2] = this.pins;
|
|
1092
1109
|
const directShortPath = calculateDirectShortPath(pin1, pin2);
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1110
|
+
const defaultElbow = calculateElbowForPins({
|
|
1111
|
+
pin1,
|
|
1112
|
+
pin2,
|
|
1113
|
+
overshoot: 0.2
|
|
1114
|
+
});
|
|
1115
|
+
const routingDistance = Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y);
|
|
1116
|
+
const adaptiveElbow = calculateElbowForPins({
|
|
1117
|
+
pin1,
|
|
1118
|
+
pin2,
|
|
1119
|
+
overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4))
|
|
1120
|
+
});
|
|
1121
|
+
const shouldUseAdaptiveElbow = findFirstCollision(defaultElbow, this.obstacles) !== null && findFirstCollision(adaptiveElbow, this.obstacles) === null;
|
|
1122
|
+
this.baseElbow = defaultElbow;
|
|
1123
|
+
if (shouldUseAdaptiveElbow) {
|
|
1124
|
+
this.baseElbow = adaptiveElbow;
|
|
1125
|
+
}
|
|
1126
|
+
if (directShortPath) {
|
|
1127
|
+
this.baseElbow = directShortPath;
|
|
1128
|
+
}
|
|
1106
1129
|
this.solvedTracePath = directShortPath;
|
|
1107
1130
|
this.aabb = aabbFromPoints(
|
|
1108
1131
|
{ x: pin1.x, y: pin1.y },
|
|
@@ -7484,6 +7507,26 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7484
7507
|
}
|
|
7485
7508
|
findCorrectedCandidate(label, labelIndex) {
|
|
7486
7509
|
const orientations = this.getAvailableOrientations(label);
|
|
7510
|
+
const requiredOrientation = orientations[0];
|
|
7511
|
+
const isTwoPinNet = this.inputProblem.netConnections.some(
|
|
7512
|
+
(connection) => connection.netId === label.netId && connection.pinIds.length === 2
|
|
7513
|
+
);
|
|
7514
|
+
if (isTwoPinNet && orientations.length === 1 && isYOrientation(requiredOrientation) && this.hasTraceContinuingInOrientation(label, requiredOrientation)) {
|
|
7515
|
+
const alignedCandidate = this.findValidCandidateInShiftColumn({
|
|
7516
|
+
label,
|
|
7517
|
+
labelIndex,
|
|
7518
|
+
orientation: requiredOrientation,
|
|
7519
|
+
direction: dir(requiredOrientation),
|
|
7520
|
+
baseAnchor: this.getWickOffsetAnchor(
|
|
7521
|
+
label.anchorPoint,
|
|
7522
|
+
requiredOrientation
|
|
7523
|
+
),
|
|
7524
|
+
maxSearchDistance: this.maxSearchDistance,
|
|
7525
|
+
outwardDistance: 0,
|
|
7526
|
+
stopOnTraceCollision: false
|
|
7527
|
+
});
|
|
7528
|
+
if (alignedCandidate) return alignedCandidate;
|
|
7529
|
+
}
|
|
7487
7530
|
const rotatedCandidate = this.findValidRotatedCandidate(
|
|
7488
7531
|
label,
|
|
7489
7532
|
labelIndex,
|
|
@@ -7508,6 +7551,21 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7508
7551
|
labelIndex
|
|
7509
7552
|
);
|
|
7510
7553
|
}
|
|
7554
|
+
hasTraceContinuingInOrientation(label, orientation) {
|
|
7555
|
+
const direction = dir(orientation);
|
|
7556
|
+
return Object.values(this.traceMap).some((trace) => {
|
|
7557
|
+
if (trace.globalConnNetId !== label.globalConnNetId) return false;
|
|
7558
|
+
const path = trace.tracePath;
|
|
7559
|
+
return path.some((point, pointIndex) => {
|
|
7560
|
+
if (Math.abs(point.x - label.anchorPoint.x) > EPS9 || Math.abs(point.y - label.anchorPoint.y) > EPS9) {
|
|
7561
|
+
return false;
|
|
7562
|
+
}
|
|
7563
|
+
return [path[pointIndex - 1], path[pointIndex + 1]].some(
|
|
7564
|
+
(neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS9 && (neighbor.y - point.y) * direction.y > EPS9
|
|
7565
|
+
);
|
|
7566
|
+
});
|
|
7567
|
+
});
|
|
7568
|
+
}
|
|
7511
7569
|
findValidRotatedCandidate(label, labelIndex, orientations) {
|
|
7512
7570
|
for (const orientation of orientations) {
|
|
7513
7571
|
const candidate = this.createCandidate(
|
|
@@ -7653,7 +7711,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7653
7711
|
baseAnchor,
|
|
7654
7712
|
maxSearchDistance,
|
|
7655
7713
|
outwardDistance,
|
|
7656
|
-
phase = "shift"
|
|
7714
|
+
phase = "shift",
|
|
7715
|
+
stopOnTraceCollision = true
|
|
7657
7716
|
} = params;
|
|
7658
7717
|
for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS9; distance4 += LABEL_SEARCH_STEP) {
|
|
7659
7718
|
const anchorPoint = {
|
|
@@ -7674,7 +7733,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7674
7733
|
result.selected = true;
|
|
7675
7734
|
return result;
|
|
7676
7735
|
}
|
|
7677
|
-
if (result.status === "trace-collision") break;
|
|
7736
|
+
if (stopOnTraceCollision && result.status === "trace-collision") break;
|
|
7678
7737
|
}
|
|
7679
7738
|
return null;
|
|
7680
7739
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
12
12
|
import type { InputPin, InputProblem } from "lib/types/InputProblem"
|
|
13
13
|
import { dir, type FacingDirection } from "lib/utils/dir"
|
|
14
|
+
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
14
15
|
import { EPS, LABEL_SEARCH_STEP, WICK_CLEARANCE } from "./constants"
|
|
15
16
|
import {
|
|
16
17
|
getConnectorTracePath,
|
|
@@ -22,8 +23,8 @@ import {
|
|
|
22
23
|
rectsOverlap,
|
|
23
24
|
simplifyOrthogonalPath,
|
|
24
25
|
traceCrossesBoundsInterior,
|
|
25
|
-
tracePathIntersectsBounds,
|
|
26
26
|
tracePathCrossesAnyBounds,
|
|
27
|
+
tracePathIntersectsBounds,
|
|
27
28
|
} from "./geometry"
|
|
28
29
|
import { getPinMap, getTracePins, toNetLabelPlacementPatch } from "./traces"
|
|
29
30
|
import type {
|
|
@@ -36,7 +37,6 @@ import type {
|
|
|
36
37
|
EvaluatedCandidate,
|
|
37
38
|
} from "./types"
|
|
38
39
|
import { visualizeAvailableNetOrientationSolver } from "./visualize"
|
|
39
|
-
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
40
40
|
|
|
41
41
|
const LABEL_TRACE_CLEARANCE = 0.1
|
|
42
42
|
|
|
@@ -182,6 +182,34 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
182
182
|
|
|
183
183
|
private findCorrectedCandidate(label: NetLabelPlacement, labelIndex: number) {
|
|
184
184
|
const orientations = this.getAvailableOrientations(label)
|
|
185
|
+
const requiredOrientation = orientations[0]!
|
|
186
|
+
const isTwoPinNet = this.inputProblem.netConnections.some(
|
|
187
|
+
(connection) =>
|
|
188
|
+
connection.netId === label.netId && connection.pinIds.length === 2,
|
|
189
|
+
)
|
|
190
|
+
if (
|
|
191
|
+
isTwoPinNet &&
|
|
192
|
+
orientations.length === 1 &&
|
|
193
|
+
isYOrientation(requiredOrientation) &&
|
|
194
|
+
this.hasTraceContinuingInOrientation(label, requiredOrientation)
|
|
195
|
+
) {
|
|
196
|
+
// Keep two-pin vertical net labels aligned with the existing trace.
|
|
197
|
+
const alignedCandidate = this.findValidCandidateInShiftColumn({
|
|
198
|
+
label,
|
|
199
|
+
labelIndex,
|
|
200
|
+
orientation: requiredOrientation,
|
|
201
|
+
direction: dir(requiredOrientation),
|
|
202
|
+
baseAnchor: this.getWickOffsetAnchor(
|
|
203
|
+
label.anchorPoint,
|
|
204
|
+
requiredOrientation,
|
|
205
|
+
),
|
|
206
|
+
maxSearchDistance: this.maxSearchDistance,
|
|
207
|
+
outwardDistance: 0,
|
|
208
|
+
stopOnTraceCollision: false,
|
|
209
|
+
})
|
|
210
|
+
if (alignedCandidate) return alignedCandidate
|
|
211
|
+
}
|
|
212
|
+
|
|
185
213
|
const rotatedCandidate = this.findValidRotatedCandidate(
|
|
186
214
|
label,
|
|
187
215
|
labelIndex,
|
|
@@ -210,6 +238,31 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
210
238
|
)
|
|
211
239
|
}
|
|
212
240
|
|
|
241
|
+
private hasTraceContinuingInOrientation(
|
|
242
|
+
label: NetLabelPlacement,
|
|
243
|
+
orientation: "y+" | "y-",
|
|
244
|
+
) {
|
|
245
|
+
const direction = dir(orientation)
|
|
246
|
+
return Object.values(this.traceMap).some((trace) => {
|
|
247
|
+
if (trace.globalConnNetId !== label.globalConnNetId) return false
|
|
248
|
+
const path = trace.tracePath
|
|
249
|
+
return path.some((point, pointIndex) => {
|
|
250
|
+
if (
|
|
251
|
+
Math.abs(point.x - label.anchorPoint.x) > EPS ||
|
|
252
|
+
Math.abs(point.y - label.anchorPoint.y) > EPS
|
|
253
|
+
) {
|
|
254
|
+
return false
|
|
255
|
+
}
|
|
256
|
+
return [path[pointIndex - 1], path[pointIndex + 1]].some(
|
|
257
|
+
(neighbor) =>
|
|
258
|
+
neighbor &&
|
|
259
|
+
Math.abs(neighbor.x - point.x) <= EPS &&
|
|
260
|
+
(neighbor.y - point.y) * direction.y > EPS,
|
|
261
|
+
)
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
213
266
|
private findValidRotatedCandidate(
|
|
214
267
|
label: NetLabelPlacement,
|
|
215
268
|
labelIndex: number,
|
|
@@ -401,6 +454,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
401
454
|
maxSearchDistance: number
|
|
402
455
|
outwardDistance: number
|
|
403
456
|
phase?: CandidatePhase
|
|
457
|
+
stopOnTraceCollision?: boolean
|
|
404
458
|
}) {
|
|
405
459
|
const {
|
|
406
460
|
label,
|
|
@@ -411,6 +465,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
411
465
|
maxSearchDistance,
|
|
412
466
|
outwardDistance,
|
|
413
467
|
phase = "shift",
|
|
468
|
+
stopOnTraceCollision = true,
|
|
414
469
|
} = params
|
|
415
470
|
|
|
416
471
|
for (
|
|
@@ -437,7 +492,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
437
492
|
result.selected = true
|
|
438
493
|
return result
|
|
439
494
|
}
|
|
440
|
-
if (result.status === "trace-collision") break
|
|
495
|
+
if (stopOnTraceCollision && result.status === "trace-collision") break
|
|
441
496
|
}
|
|
442
497
|
|
|
443
498
|
return null
|
|
@@ -28,6 +28,29 @@ import { getObstacleRects, type ObstacleRect } from "./rect"
|
|
|
28
28
|
|
|
29
29
|
type PathKey = string
|
|
30
30
|
|
|
31
|
+
const calculateElbowForPins = ({
|
|
32
|
+
pin1,
|
|
33
|
+
pin2,
|
|
34
|
+
overshoot,
|
|
35
|
+
}: {
|
|
36
|
+
pin1: MspConnectionPair["pins"][number]
|
|
37
|
+
pin2: MspConnectionPair["pins"][number]
|
|
38
|
+
overshoot: number
|
|
39
|
+
}) =>
|
|
40
|
+
calculateElbow(
|
|
41
|
+
{
|
|
42
|
+
x: pin1.x,
|
|
43
|
+
y: pin1.y,
|
|
44
|
+
facingDirection: pin1._facingDirection!,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
x: pin2.x,
|
|
48
|
+
y: pin2.y,
|
|
49
|
+
facingDirection: pin2._facingDirection!,
|
|
50
|
+
},
|
|
51
|
+
{ overshoot },
|
|
52
|
+
)
|
|
53
|
+
|
|
31
54
|
export class SchematicTraceSingleLineSolver2 extends BaseSolver {
|
|
32
55
|
pins: MspConnectionPair["pins"]
|
|
33
56
|
connectionPair?: MspConnectionPair
|
|
@@ -89,23 +112,30 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver {
|
|
|
89
112
|
|
|
90
113
|
const [pin1, pin2] = this.pins
|
|
91
114
|
const directShortPath = calculateDirectShortPath(pin1, pin2)
|
|
115
|
+
const defaultElbow = calculateElbowForPins({
|
|
116
|
+
pin1,
|
|
117
|
+
pin2,
|
|
118
|
+
overshoot: 0.2,
|
|
119
|
+
})
|
|
120
|
+
const routingDistance =
|
|
121
|
+
Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y)
|
|
122
|
+
const adaptiveElbow = calculateElbowForPins({
|
|
123
|
+
pin1,
|
|
124
|
+
pin2,
|
|
125
|
+
overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4)),
|
|
126
|
+
})
|
|
127
|
+
const shouldUseAdaptiveElbow =
|
|
128
|
+
findFirstCollision(defaultElbow, this.obstacles) !== null &&
|
|
129
|
+
findFirstCollision(adaptiveElbow, this.obstacles) === null
|
|
92
130
|
|
|
93
131
|
// Build initial elbow path
|
|
94
|
-
this.baseElbow =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
x: pin2.x,
|
|
104
|
-
y: pin2.y,
|
|
105
|
-
facingDirection: pin2._facingDirection!,
|
|
106
|
-
},
|
|
107
|
-
{ overshoot: 0.2 },
|
|
108
|
-
)
|
|
132
|
+
this.baseElbow = defaultElbow
|
|
133
|
+
if (shouldUseAdaptiveElbow) {
|
|
134
|
+
this.baseElbow = adaptiveElbow
|
|
135
|
+
}
|
|
136
|
+
if (directShortPath) {
|
|
137
|
+
this.baseElbow = directShortPath
|
|
138
|
+
}
|
|
109
139
|
this.solvedTracePath = directShortPath
|
|
110
140
|
|
|
111
141
|
// Bounds defined by PA and PB
|
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="0.5279125,3.0973754500000004 0.7,3" data-type="line" data-label="" points="363.46296663543797,319.86511717601365 416.18376153125485,349.6970987396827" fill="none" stroke="hsl(72, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="1" data-y="3" x="416.18376153125485" y="245.5345636073937" width="183.81623846874515" height="208.32507026457802" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="3.1" x="40" y="186.14036612802857" width="323.46296663543797" height="265.8413857337265" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><
|
|
2
|
-
globalConnNetId: connectivity_net0" data-x="0.7539125" data-y="3.0973754500000004" x="363.7693270328858" y="289.2290774312228" width="137.86217885155884" height="61.272079489581756" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><rect data-type="rect" data-label="netId: .L1 > .pin2 to .R1 > .pin1
|
|
3
|
-
globalConnNetId: connectivity_net0" data-x="0.474" data-y="3" x="278.01522228224815" y="319.0610589948918" width="137.86217885155884" height="61.272079489581756" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><circle data-type="point" data-label="R1.1
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="0.5279125,3.0973754500000004 0.7,3" data-type="line" data-label="" points="363.46296663543797,319.86511717601365 416.18376153125485,349.6970987396827" fill="none" stroke="hsl(72, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="0.5279125,3.0973754500000004 0.61395625,3.0973754500000004 0.61395625,3 0.7,3" data-type="line" data-label="" points="363.46296663543797,319.86511717601365 389.8233640833464,319.86511717601365 389.8233640833464,349.6970987396827 416.18376153125485,349.6970987396827" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="1" data-y="3" x="416.18376153125485" y="245.5345636073937" width="183.81623846874515" height="208.32507026457802" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="3.1" x="40" y="186.14036612802857" width="323.46296663543797" height="265.8413857337265" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><circle data-type="point" data-label="R1.1
|
|
4
2
|
x-" data-x="0.7" data-y="3" cx="416.18376153125485" cy="349.6970987396827" r="3" fill="hsl(226, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="R1.2
|
|
5
3
|
x+" data-x="1.3" data-y="3" cx="600" cy="349.6970987396827" r="3" fill="hsl(227, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="L1.1
|
|
6
4
|
x-" data-x="-0.5279125" data-y="3.10262455" cx="40.00000000000003" cy="318.25700081377" r="3" fill="hsl(40, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="L1.2
|
|
7
|
-
x+" data-x="0.5279125" data-y="3.0973754500000004" cx="363.46296663543797" cy="319.86511717601365" r="3" fill="hsl(41, 100%, 50%, 0.8)"/></g><g><
|
|
8
|
-
orientation: x+" data-x="0.5279125" data-y="3.0973754500000004" cx="363.46296663543797" cy="319.86511717601365" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
9
|
-
orientation: x-" data-x="0.7" data-y="3" cx="416.18376153125485" cy="349.6970987396827" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
5
|
+
x+" data-x="0.5279125" data-y="3.0973754500000004" cx="363.46296663543797" cy="319.86511717601365" r="3" fill="hsl(41, 100%, 50%, 0.8)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
10
6
|
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
11
7
|
const svg = e.currentTarget;
|
|
12
8
|
const rect = svg.getBoundingClientRect();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="-2.275,-2.5 -2.275,-3.5" data-type="line" data-label="" points="
|
|
2
|
-
globalConnNetId: connectivity_net0" data-x="-
|
|
3
|
-
x+" data-x="-2.275" data-y="-2.5" cx="
|
|
4
|
-
x+" data-x="-2.275" data-y="-3.5" cx="
|
|
5
|
-
orientation: y-" data-x="-
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="-2.275,-2.5 -2.275,-3.5" data-type="line" data-label="" points="453.96494552344836,156.72193273330174 453.96494552344836,421.99905258171486" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-2.275,-2.5 -2.0749999999999997,-2.5 -2.0749999999999997,-3.5 -2.275,-3.5" data-type="line" data-label="" points="453.96494552344836,156.72193273330174 507.02036949313106,156.72193273330174 507.02036949313106,421.99905258171486 453.96494552344836,421.99905258171486" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-2.0749999999999997,-2.5 -2.0749999999999997,-3.7510000000000003" data-type="line" data-label="" points="507.02036949313106,156.72193273330174 507.02036949313106,488.5836096636666" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="-3" data-y="-2.5" x="69.31312174324955" y="103.6665087636191" width="384.6518237801988" height="106.11084793936527" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.003769642857142858"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="-3" data-y="-3.5" x="69.31312174324955" y="368.9436286120322" width="384.6518237801988" height="106.11084793936527" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.003769642857142858"/></g><g><rect data-type="rect" data-label="J3" data-x="-3.205" data-y="-2.1849999999999996" x="159.50734249170984" y="39.99999999999994" width="95.4997631454288" height="66.31927996210322" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.003769642857142858"/></g><g><rect data-type="rect" data-label="J4" data-x="-3.205" data-y="-3.1849999999999996" x="159.50734249170984" y="305.27711984841295" width="95.4997631454288" height="66.31927996210322" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.003769642857142858"/></g><g><rect data-type="rect" data-label="netId: GND
|
|
2
|
+
globalConnNetId: connectivity_net0" data-x="-2.0749999999999997" data-y="-3.9610000000000003" x="443.353860729512" y="488.5836096636665" width="127.3330175272381" height="111.4163903363335" fill="#00000066" stroke="#000000" stroke-width="0.003769642857142858"/></g><g><circle data-type="point" data-label="J3.1
|
|
3
|
+
x+" data-x="-2.275" data-y="-2.5" cx="453.96494552344836" cy="156.72193273330174" r="3" fill="hsl(340, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J4.1
|
|
4
|
+
x+" data-x="-2.275" data-y="-3.5" cx="453.96494552344836" cy="421.99905258171486" r="3" fill="hsl(221, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
5
|
+
orientation: y-" data-x="-2.0749999999999997" data-y="-3.7510000000000003" cx="507.02036949313106" cy="488.5836096636666" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
6
6
|
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
7
7
|
const svg = e.currentTarget;
|
|
8
8
|
const rect = svg.getBoundingClientRect();
|
|
@@ -24,7 +24,7 @@ orientation: y-" data-x="-1.7849999999999997" data-y="-2.501" cx="538.3486238532
|
|
|
24
24
|
v.setAttribute('y2', '640');
|
|
25
25
|
|
|
26
26
|
// Calculate real coordinates using inverse transformation
|
|
27
|
-
const matrix = {"a":
|
|
27
|
+
const matrix = {"a":265.277119848413,"c":0,"e":1057.470393178588,"b":0,"d":-265.277119848413,"f":-506.4708668877307};
|
|
28
28
|
// Manually invert and apply the affine transform
|
|
29
29
|
// Since we only use translate and scale, we can directly compute:
|
|
30
30
|
// x' = (x - tx) / sx
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="-0.6000000000000001,0.1 -0.6000000000000001,-0.1" data-type="line" data-label="" points="213.9715237806725,494.81975159042713 213.9715237806725,528.748863980612" fill="none" stroke="hsl(27, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-0.6000000000000001,0.1 -0.6599999999999999,2.36" data-type="line" data-label="" points="213.9715237806725,494.81975159042713 203.79279006361708,111.42078158133899" fill="none" stroke="hsl(27, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-0.6000000000000001,-0.1 -0.6599999999999999,2.36" data-type="line" data-label="" points="213.9715237806725,528.748863980612 203.79279006361708,111.42078158133899" fill="none" stroke="hsl(27, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="0.6000000000000001,0.1 0.6000000000000001,-0.1" data-type="line" data-label="" points="417.5461981217813,494.81975159042713 417.5461981217813,528.748863980612" fill="none" stroke="hsl(64, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="0.6000000000000001,0.1 0.6599999999999999,2.36" data-type="line" data-label="" points="417.5461981217813,494.81975159042713 427.7249318388367,111.42078158133899" fill="none" stroke="hsl(64, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="0.6000000000000001,-0.1 0.6599999999999999,2.36" data-type="line" data-label="" points="417.5461981217813,528.748863980612 427.7249318388367,111.42078158133899" fill="none" stroke="hsl(64, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-0.6000000000000001,-0.1 -0.8,-0.1 -0.8,0.1 -0.6000000000000001,0.1" data-type="line" data-label="" points="213.9715237806725,528.748863980612 180.0424113904877,528.748863980612 180.0424113904877,494.81975159042713 213.9715237806725,494.81975159042713" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-0.6599999999999999,2.36 -0.8599999999999999,2.36 -0.8599999999999999,0.1 -0.6000000000000001,0.1" data-type="line" data-label="" points="203.79279006361708,111.42078158133899 169.86367767343228,111.42078158133899 169.86367767343228,494.81975159042713 213.9715237806725,494.81975159042713" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0.6000000000000001,0.1 1.4400000000000002,0.1 1.4400000000000002,-0.1 0.6000000000000001,-0.1" data-type="line" data-label="" points="417.5461981217813,494.81975159042713 560.0484701605575,494.81975159042713 560.0484701605575,528.748863980612 417.5461981217813,528.748863980612" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0.6599999999999999,2.36 1.4400000000000002,2.36 1.4400000000000002,0.1 0.6000000000000001,0.1" data-type="line" data-label="" points="427.7249318388367,111.42078158133899 560.0484701605575,111.42078158133899 560.0484701605575,494.81975159042713 417.5461981217813,494.81975159042713" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-0.8599999999999999,2.36 -1.15,2.36 -1.15,2.3609999999999998" data-type="line" data-label="" points="169.86367767343228,111.42078158133899 120.66646470766432,111.42078158133899 120.66646470766432,111.25113601938807" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="213.9715237806725" y="460.8906392002424" width="203.5746743411088" height="101.7873371705544" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.005894642857142856"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="2.32" x="203.79279006361708" y="46.95546803998786" width="223.93214177521963" height="142.50227203877614" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.005894642857142856"/></g><g><rect data-type="rect" data-label="5x20-BLX-A" data-x="0.4" data-y="-0.43000000000000005" x="281.8297485610421" y="569.4637988488337" width="203.5746743411088" height="30.536201151166324" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.005894642857142856"/></g><g><rect data-type="rect" data-label="FUSEX20MM" data-x="0.3400000000000001" data-y="0.41500000000000004" x="271.6510148439867" y="420.1757043320206" width="203.5746743411088" height="42.41139048773101" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.005894642857142856"/></g><g><rect data-type="rect" data-label="netId: VIN
|
|
2
|
+
globalConnNetId: connectivity_net0" data-x="-1.15" data-y="2.5709999999999997" x="79.95152983944257" y="39.99999999999997" width="81.4298697364435" height="71.25113601938807" fill="#ef444466" stroke="#ef4444" stroke-width="0.005894642857142856"/></g><g><rect data-type="rect" data-label="netId: VOUT
|
|
3
|
+
globalConnNetId: connectivity_net1" data-x="1.05" data-y="2.57" x="442.9930324144199" y="40.16964556195089" width="101.78733717055445" height="71.25113601938807" fill="#ef444466" stroke="#ef4444" stroke-width="0.005894642857142856"/></g><g><circle data-type="point" data-label="F1.1
|
|
4
|
+
x-" data-x="-0.6000000000000001" data-y="0.1" cx="213.9715237806725" cy="494.81975159042713" r="3" fill="hsl(214, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F1.2
|
|
5
|
+
x-" data-x="-0.6000000000000001" data-y="-0.1" cx="213.9715237806725" cy="528.748863980612" r="3" fill="hsl(215, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F1.4
|
|
6
|
+
x+" data-x="0.6000000000000001" data-y="0.1" cx="417.5461981217813" cy="494.81975159042713" r="3" fill="hsl(217, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F1.3
|
|
7
|
+
x+" data-x="0.6000000000000001" data-y="-0.1" cx="417.5461981217813" cy="528.748863980612" r="3" fill="hsl(216, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F2.1
|
|
8
|
+
x-" data-x="-0.6599999999999999" data-y="2.36" cx="203.79279006361708" cy="111.42078158133899" r="3" fill="hsl(95, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F2.2
|
|
9
|
+
x+" data-x="0.6599999999999999" data-y="2.36" cx="427.7249318388367" cy="111.42078158133899" r="3" fill="hsl(96, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
10
|
+
orientation: y+" data-x="-1.15" data-y="2.3609999999999998" cx="120.66646470766432" cy="111.25113601938807" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
11
|
+
orientation: y+" data-x="1.05" data-y="2.36" cx="493.8867009996971" cy="111.42078158133899" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
12
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
13
|
+
const svg = e.currentTarget;
|
|
14
|
+
const rect = svg.getBoundingClientRect();
|
|
15
|
+
const x = e.clientX - rect.left;
|
|
16
|
+
const y = e.clientY - rect.top;
|
|
17
|
+
const crosshair = svg.getElementById('crosshair');
|
|
18
|
+
const h = svg.getElementById('crosshair-h');
|
|
19
|
+
const v = svg.getElementById('crosshair-v');
|
|
20
|
+
const coords = svg.getElementById('coordinates');
|
|
21
|
+
|
|
22
|
+
crosshair.style.display = 'block';
|
|
23
|
+
h.setAttribute('x1', '0');
|
|
24
|
+
h.setAttribute('x2', '640');
|
|
25
|
+
h.setAttribute('y1', y);
|
|
26
|
+
h.setAttribute('y2', y);
|
|
27
|
+
v.setAttribute('x1', x);
|
|
28
|
+
v.setAttribute('x2', x);
|
|
29
|
+
v.setAttribute('y1', '0');
|
|
30
|
+
v.setAttribute('y2', '640');
|
|
31
|
+
|
|
32
|
+
// Calculate real coordinates using inverse transformation
|
|
33
|
+
const matrix = {"a":169.645561950924,"c":0,"e":315.7588609512269,"b":0,"d":-169.645561950924,"f":511.78430778551956};
|
|
34
|
+
// Manually invert and apply the affine transform
|
|
35
|
+
// Since we only use translate and scale, we can directly compute:
|
|
36
|
+
// x' = (x - tx) / sx
|
|
37
|
+
// y' = (y - ty) / sy
|
|
38
|
+
const sx = matrix.a;
|
|
39
|
+
const sy = matrix.d;
|
|
40
|
+
const tx = matrix.e;
|
|
41
|
+
const ty = matrix.f;
|
|
42
|
+
const realPoint = {
|
|
43
|
+
x: (x - tx) / sx,
|
|
44
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
48
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
49
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
50
|
+
});
|
|
51
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
52
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
53
|
+
});
|
|
54
|
+
]]></script></svg>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_0",
|
|
5
|
+
"center": {
|
|
6
|
+
"x": 0,
|
|
7
|
+
"y": 0
|
|
8
|
+
},
|
|
9
|
+
"width": 1.2000000000000002,
|
|
10
|
+
"height": 0.6000000000000001,
|
|
11
|
+
"pins": [
|
|
12
|
+
{
|
|
13
|
+
"pinId": "F1.1",
|
|
14
|
+
"x": -0.6000000000000001,
|
|
15
|
+
"y": 0.1
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"pinId": "F1.2",
|
|
19
|
+
"x": -0.6000000000000001,
|
|
20
|
+
"y": -0.1
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"pinId": "F1.4",
|
|
24
|
+
"x": 0.6000000000000001,
|
|
25
|
+
"y": 0.1
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"pinId": "F1.3",
|
|
29
|
+
"x": 0.6000000000000001,
|
|
30
|
+
"y": -0.1
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"chipId": "schematic_component_1",
|
|
36
|
+
"center": {
|
|
37
|
+
"x": 0,
|
|
38
|
+
"y": 2.32
|
|
39
|
+
},
|
|
40
|
+
"width": 1.3199999999999998,
|
|
41
|
+
"height": 0.8399999999999999,
|
|
42
|
+
"pins": [
|
|
43
|
+
{
|
|
44
|
+
"pinId": "F2.1",
|
|
45
|
+
"x": -0.6599999999999999,
|
|
46
|
+
"y": 2.36,
|
|
47
|
+
"_facingDirection": "x-"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"pinId": "F2.2",
|
|
51
|
+
"x": 0.6599999999999999,
|
|
52
|
+
"y": 2.36,
|
|
53
|
+
"_facingDirection": "x+"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"directConnections": [],
|
|
59
|
+
"netConnections": [
|
|
60
|
+
{
|
|
61
|
+
"netId": "VIN",
|
|
62
|
+
"pinIds": [
|
|
63
|
+
"F1.1",
|
|
64
|
+
"F1.2",
|
|
65
|
+
"F2.1"
|
|
66
|
+
],
|
|
67
|
+
"netLabelWidth": 0.42,
|
|
68
|
+
"netLabelHeight": 0.48
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"netId": "VOUT",
|
|
72
|
+
"pinIds": [
|
|
73
|
+
"F1.4",
|
|
74
|
+
"F1.3",
|
|
75
|
+
"F2.2"
|
|
76
|
+
],
|
|
77
|
+
"netLabelWidth": 0.42,
|
|
78
|
+
"netLabelHeight": 0.6
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"textBoxes": [
|
|
82
|
+
{
|
|
83
|
+
"chipId": "schematic_component_0",
|
|
84
|
+
"center": {
|
|
85
|
+
"x": 0.4,
|
|
86
|
+
"y": -0.43000000000000005
|
|
87
|
+
},
|
|
88
|
+
"width": 1.2,
|
|
89
|
+
"height": 0.17999999999999994,
|
|
90
|
+
"text": "5x20-BLX-A"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"chipId": "schematic_component_0",
|
|
94
|
+
"center": {
|
|
95
|
+
"x": 0.3400000000000001,
|
|
96
|
+
"y": 0.41500000000000004
|
|
97
|
+
},
|
|
98
|
+
"width": 1.2000000000000002,
|
|
99
|
+
"height": 0.25,
|
|
100
|
+
"text": "FUSEX20MM"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"availableNetLabelOrientations": {
|
|
104
|
+
"VIN": [
|
|
105
|
+
"y+"
|
|
106
|
+
],
|
|
107
|
+
"VOUT": [
|
|
108
|
+
"y+"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"maxMspPairDistance": 2.4,
|
|
112
|
+
"_hideRatsNet": false
|
|
113
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import inputProblem from "./bug-report-20260717T042845Z.json"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
|
|
6
|
+
test("bug-report-20260717T042845Z", () => {
|
|
7
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
8
|
+
|
|
9
|
+
solver.solve()
|
|
10
|
+
|
|
11
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
12
|
+
})
|