@tscircuit/schematic-trace-solver 0.0.123 → 0.0.125
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 +85 -4
- package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +5 -1
- package/lib/solvers/RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver.ts +49 -6
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +7 -2
- package/lib/solvers/TraceCleanupSolver/shortenExcessiveLabelPaddingDetour.ts +64 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260805T061316Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260806T061548Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +18 -18
- package/tests/bug-reports/bug-report-20260804T225912Z/bug-report-20260804T225912Z.test.ts +18 -0
- package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +212 -0
- package/tests/bug-reports/bug-report-20260805T061316Z/bug-report-20260805T061316Z.json +889 -0
- package/tests/bug-reports/bug-report-20260805T061316Z/bug-report-20260805T061316Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +212 -0
- package/tests/bug-reports/bug-report-20260806T061548Z/bug-report-20260806T061548Z.json +960 -0
- package/tests/bug-reports/bug-report-20260806T061548Z/bug-report-20260806T061548Z.test.ts +32 -0
- package/tests/examples/__snapshots__/example01.snap.svg +18 -18
package/dist/index.d.ts
CHANGED
|
@@ -875,6 +875,7 @@ declare class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
875
875
|
private intersectsAnyOtherNetLabel;
|
|
876
876
|
private getCornerCandidatesForLabel;
|
|
877
877
|
private isConfiguredRailLabel;
|
|
878
|
+
private isLabelCrossedByOtherNetTrace;
|
|
878
879
|
private pointsEqual;
|
|
879
880
|
private getVerticalSegmentPointIndices;
|
|
880
881
|
private isPinAlignedCorner;
|
package/dist/index.js
CHANGED
|
@@ -5108,6 +5108,51 @@ var getVisibleTraceMetrics = (traces) => {
|
|
|
5108
5108
|
var getVisibleTraceLength = (traces) => getVisibleTraceMetrics(traces).length;
|
|
5109
5109
|
var getVisibleTraceSegmentCount = (traces) => getVisibleTraceMetrics(traces).segmentCount;
|
|
5110
5110
|
|
|
5111
|
+
// lib/solvers/TraceCleanupSolver/shortenExcessiveLabelPaddingDetour.ts
|
|
5112
|
+
var MIN_EXCESSIVE_DETOUR_LENGTH_RATIO = 2.5;
|
|
5113
|
+
var getTracePathLength = (tracePath) => {
|
|
5114
|
+
let pathLength = 0;
|
|
5115
|
+
for (let pointIndex = 1; pointIndex < tracePath.length; pointIndex++) {
|
|
5116
|
+
const previousPoint = tracePath[pointIndex - 1];
|
|
5117
|
+
const point = tracePath[pointIndex];
|
|
5118
|
+
pathLength += Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
|
|
5119
|
+
}
|
|
5120
|
+
return pathLength;
|
|
5121
|
+
};
|
|
5122
|
+
var shortenExcessiveLabelPaddingDetour = ({
|
|
5123
|
+
inputProblem,
|
|
5124
|
+
trace
|
|
5125
|
+
}) => {
|
|
5126
|
+
const exactConnection = inputProblem.directConnections.find(
|
|
5127
|
+
(connection) => connection.pinIds.every((pinId) => trace.pinIds.includes(pinId))
|
|
5128
|
+
);
|
|
5129
|
+
if (!exactConnection) return trace;
|
|
5130
|
+
const chipMap = {};
|
|
5131
|
+
for (const chip of inputProblem.chips) chipMap[chip.chipId] = chip;
|
|
5132
|
+
const candidateSolver = new SchematicTraceSingleLineSolver2({
|
|
5133
|
+
pins: trace.pins,
|
|
5134
|
+
connectionPair: trace,
|
|
5135
|
+
inputProblem: {
|
|
5136
|
+
...inputProblem,
|
|
5137
|
+
directConnections: [
|
|
5138
|
+
exactConnection,
|
|
5139
|
+
...inputProblem.directConnections.filter(
|
|
5140
|
+
(connection) => connection !== exactConnection
|
|
5141
|
+
)
|
|
5142
|
+
]
|
|
5143
|
+
},
|
|
5144
|
+
chipMap
|
|
5145
|
+
});
|
|
5146
|
+
candidateSolver.solve();
|
|
5147
|
+
if (!candidateSolver.solvedTracePath) return trace;
|
|
5148
|
+
const currentLength = getTracePathLength(trace.tracePath);
|
|
5149
|
+
const candidateLength = getTracePathLength(candidateSolver.solvedTracePath);
|
|
5150
|
+
if (currentLength <= candidateLength * MIN_EXCESSIVE_DETOUR_LENGTH_RATIO) {
|
|
5151
|
+
return trace;
|
|
5152
|
+
}
|
|
5153
|
+
return { ...trace, tracePath: candidateSolver.solvedTracePath };
|
|
5154
|
+
};
|
|
5155
|
+
|
|
5111
5156
|
// lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
|
|
5112
5157
|
var minimizeTurnsWithFilteredLabels = ({
|
|
5113
5158
|
targetMspConnectionPairId,
|
|
@@ -5117,12 +5162,16 @@ var minimizeTurnsWithFilteredLabels = ({
|
|
|
5117
5162
|
mergedLabelNetIdMap,
|
|
5118
5163
|
paddingBuffer
|
|
5119
5164
|
}) => {
|
|
5120
|
-
const
|
|
5165
|
+
const originalTargetTrace = traces.find(
|
|
5121
5166
|
(t) => t.mspPairId === targetMspConnectionPairId
|
|
5122
5167
|
);
|
|
5123
|
-
if (!
|
|
5168
|
+
if (!originalTargetTrace) {
|
|
5124
5169
|
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
5125
5170
|
}
|
|
5171
|
+
const targetTrace = shortenExcessiveLabelPaddingDetour({
|
|
5172
|
+
inputProblem,
|
|
5173
|
+
trace: originalTargetTrace
|
|
5174
|
+
});
|
|
5126
5175
|
const targetPinIds = new Set(targetTrace.pinIds);
|
|
5127
5176
|
const otherTraces = traces.filter(
|
|
5128
5177
|
(trace) => trace.mspPairId !== targetMspConnectionPairId
|
|
@@ -8996,6 +9045,7 @@ var ensureGraphicsArrays2 = (graphics) => {
|
|
|
8996
9045
|
};
|
|
8997
9046
|
|
|
8998
9047
|
// lib/solvers/RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver.ts
|
|
9048
|
+
var LABEL_TRACE_CLEARANCE3 = 0.1;
|
|
8999
9049
|
var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
9000
9050
|
inputProblem;
|
|
9001
9051
|
traces;
|
|
@@ -9177,11 +9227,11 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9177
9227
|
const anchorAlignedCandidates = [];
|
|
9178
9228
|
const railAlignedCandidates = [];
|
|
9179
9229
|
const labelTraces = this.getTraceLinesForLabel(label);
|
|
9180
|
-
const allowRailAlignedFallback = this.isConfiguredRailLabel(label) && !labelTraces.some(
|
|
9230
|
+
const allowRailAlignedFallback = this.isConfiguredRailLabel(label) && (this.isLabelCrossedByOtherNetTrace(label) || !labelTraces.some(
|
|
9181
9231
|
(trace) => getTraceCorners(trace.tracePath).some(
|
|
9182
9232
|
(corner) => this.pointsEqual(corner, label.anchorPoint)
|
|
9183
9233
|
)
|
|
9184
|
-
);
|
|
9234
|
+
));
|
|
9185
9235
|
for (const trace of labelTraces) {
|
|
9186
9236
|
const path = trace.tracePath;
|
|
9187
9237
|
const pins = [path[0], path[path.length - 1]];
|
|
@@ -9220,6 +9270,13 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9220
9270
|
(orientation) => orientation === "y+" || orientation === "y-"
|
|
9221
9271
|
);
|
|
9222
9272
|
}
|
|
9273
|
+
isLabelCrossedByOtherNetTrace(label) {
|
|
9274
|
+
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
9275
|
+
const otherNetTraceMap = Object.fromEntries(
|
|
9276
|
+
this.traces.filter((trace) => trace.globalConnNetId !== label.globalConnNetId).map((trace) => [trace.mspPairId, trace])
|
|
9277
|
+
);
|
|
9278
|
+
return traceCrossesBoundsInterior(bounds, otherNetTraceMap);
|
|
9279
|
+
}
|
|
9223
9280
|
pointsEqual(a, b) {
|
|
9224
9281
|
return Math.abs(a.x - b.x) <= EPS6 && Math.abs(a.y - b.y) <= EPS6;
|
|
9225
9282
|
}
|
|
@@ -9269,6 +9326,30 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9269
9326
|
shiftedCoordinates.add(chipBounds.minX - label.width / 2);
|
|
9270
9327
|
shiftedCoordinates.add(chipBounds.maxX + label.width / 2);
|
|
9271
9328
|
}
|
|
9329
|
+
for (const otherTrace of this.traces) {
|
|
9330
|
+
if (otherTrace.globalConnNetId === label.globalConnNetId) continue;
|
|
9331
|
+
for (let i = 0; i < otherTrace.tracePath.length - 1; i++) {
|
|
9332
|
+
const start = otherTrace.tracePath[i];
|
|
9333
|
+
const end = otherTrace.tracePath[i + 1];
|
|
9334
|
+
if (!segmentCrossesBoundsInterior(start, end, labelBounds)) continue;
|
|
9335
|
+
const halfLabelWidth = label.width / 2;
|
|
9336
|
+
if (Math.abs(start.x - end.x) <= EPS6) {
|
|
9337
|
+
shiftedCoordinates.add(
|
|
9338
|
+
start.x - halfLabelWidth - LABEL_TRACE_CLEARANCE3
|
|
9339
|
+
);
|
|
9340
|
+
shiftedCoordinates.add(
|
|
9341
|
+
start.x + halfLabelWidth + LABEL_TRACE_CLEARANCE3
|
|
9342
|
+
);
|
|
9343
|
+
} else {
|
|
9344
|
+
shiftedCoordinates.add(
|
|
9345
|
+
Math.min(start.x, end.x) - halfLabelWidth - LABEL_TRACE_CLEARANCE3
|
|
9346
|
+
);
|
|
9347
|
+
shiftedCoordinates.add(
|
|
9348
|
+
Math.max(start.x, end.x) + halfLabelWidth + LABEL_TRACE_CLEARANCE3
|
|
9349
|
+
);
|
|
9350
|
+
}
|
|
9351
|
+
}
|
|
9352
|
+
}
|
|
9272
9353
|
return [...shiftedCoordinates].flatMap((x) => {
|
|
9273
9354
|
if (Math.abs(x - corner.x) <= EPS6) return [];
|
|
9274
9355
|
const reroutedTracePath = simplifyPath(
|
|
@@ -41,7 +41,11 @@ export const traceCrossesBoundsInterior = (
|
|
|
41
41
|
return false
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const segmentCrossesBoundsInterior = (
|
|
44
|
+
export const segmentCrossesBoundsInterior = (
|
|
45
|
+
p1: Point,
|
|
46
|
+
p2: Point,
|
|
47
|
+
bounds: Bounds,
|
|
48
|
+
) => {
|
|
45
49
|
const interiorBounds = {
|
|
46
50
|
minX: bounds.minX + TRACE_BOUNDARY_TOLERANCE,
|
|
47
51
|
minY: bounds.minY + TRACE_BOUNDARY_TOLERANCE,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { GraphicsObject } from "graphics-debug"
|
|
2
2
|
import type { Point } from "@tscircuit/math-utils"
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
segmentCrossesBoundsInterior,
|
|
5
|
+
traceCrossesBoundsInterior,
|
|
6
|
+
} from "lib/solvers/AvailableNetOrientationSolver/geometry"
|
|
4
7
|
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
5
8
|
import { moveAttachedLabelsToReroutedTrace } from "lib/solvers/Example28Solver/labelMovement"
|
|
6
9
|
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
@@ -32,6 +35,8 @@ import type {
|
|
|
32
35
|
import { visualizeRailNetLabelCornerPlacementSolver } from "./visualize"
|
|
33
36
|
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
34
37
|
|
|
38
|
+
const LABEL_TRACE_CLEARANCE = 0.1
|
|
39
|
+
|
|
35
40
|
export class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
36
41
|
inputProblem: InputProblem
|
|
37
42
|
traces: SolvedTracePath[]
|
|
@@ -271,11 +276,12 @@ export class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
271
276
|
const labelTraces = this.getTraceLinesForLabel(label)
|
|
272
277
|
const allowRailAlignedFallback =
|
|
273
278
|
this.isConfiguredRailLabel(label) &&
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
+
(this.isLabelCrossedByOtherNetTrace(label) ||
|
|
280
|
+
!labelTraces.some((trace) =>
|
|
281
|
+
getTraceCorners(trace.tracePath).some((corner) =>
|
|
282
|
+
this.pointsEqual(corner, label.anchorPoint),
|
|
283
|
+
),
|
|
284
|
+
))
|
|
279
285
|
|
|
280
286
|
for (const trace of labelTraces) {
|
|
281
287
|
const path = trace.tracePath
|
|
@@ -324,6 +330,17 @@ export class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
324
330
|
)
|
|
325
331
|
}
|
|
326
332
|
|
|
333
|
+
private isLabelCrossedByOtherNetTrace(label: NetLabelPlacement) {
|
|
334
|
+
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
335
|
+
const otherNetTraceMap = Object.fromEntries(
|
|
336
|
+
this.traces
|
|
337
|
+
.filter((trace) => trace.globalConnNetId !== label.globalConnNetId)
|
|
338
|
+
.map((trace) => [trace.mspPairId, trace]),
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
return traceCrossesBoundsInterior(bounds, otherNetTraceMap)
|
|
342
|
+
}
|
|
343
|
+
|
|
327
344
|
private pointsEqual(a: Point, b: Point) {
|
|
328
345
|
return Math.abs(a.x - b.x) <= EPS && Math.abs(a.y - b.y) <= EPS
|
|
329
346
|
}
|
|
@@ -394,6 +411,32 @@ export class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
394
411
|
shiftedCoordinates.add(chipBounds.minX - label.width / 2)
|
|
395
412
|
shiftedCoordinates.add(chipBounds.maxX + label.width / 2)
|
|
396
413
|
}
|
|
414
|
+
for (const otherTrace of this.traces) {
|
|
415
|
+
if (otherTrace.globalConnNetId === label.globalConnNetId) continue
|
|
416
|
+
|
|
417
|
+
for (let i = 0; i < otherTrace.tracePath.length - 1; i++) {
|
|
418
|
+
const start = otherTrace.tracePath[i]!
|
|
419
|
+
const end = otherTrace.tracePath[i + 1]!
|
|
420
|
+
if (!segmentCrossesBoundsInterior(start, end, labelBounds)) continue
|
|
421
|
+
|
|
422
|
+
const halfLabelWidth = label.width / 2
|
|
423
|
+
if (Math.abs(start.x - end.x) <= EPS) {
|
|
424
|
+
shiftedCoordinates.add(
|
|
425
|
+
start.x - halfLabelWidth - LABEL_TRACE_CLEARANCE,
|
|
426
|
+
)
|
|
427
|
+
shiftedCoordinates.add(
|
|
428
|
+
start.x + halfLabelWidth + LABEL_TRACE_CLEARANCE,
|
|
429
|
+
)
|
|
430
|
+
} else {
|
|
431
|
+
shiftedCoordinates.add(
|
|
432
|
+
Math.min(start.x, end.x) - halfLabelWidth - LABEL_TRACE_CLEARANCE,
|
|
433
|
+
)
|
|
434
|
+
shiftedCoordinates.add(
|
|
435
|
+
Math.max(start.x, end.x) + halfLabelWidth + LABEL_TRACE_CLEARANCE,
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
397
440
|
|
|
398
441
|
return [...shiftedCoordinates].flatMap((x) => {
|
|
399
442
|
if (Math.abs(x - corner.x) <= EPS) return []
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getVisibleTraceSegmentCount,
|
|
10
10
|
RAIL_ALIGNMENT_EPSILON,
|
|
11
11
|
} from "./sameNetRailAlignment/geometry"
|
|
12
|
+
import { shortenExcessiveLabelPaddingDetour } from "./shortenExcessiveLabelPaddingDetour"
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Minimizes turns with a strict pass that treats every other trace as an
|
|
@@ -31,12 +32,16 @@ export const minimizeTurnsWithFilteredLabels = ({
|
|
|
31
32
|
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
32
33
|
paddingBuffer: number
|
|
33
34
|
}): SolvedTracePath => {
|
|
34
|
-
const
|
|
35
|
+
const originalTargetTrace = traces.find(
|
|
35
36
|
(t) => t.mspPairId === targetMspConnectionPairId,
|
|
36
37
|
)
|
|
37
|
-
if (!
|
|
38
|
+
if (!originalTargetTrace) {
|
|
38
39
|
throw new Error(`Target trace ${targetMspConnectionPairId} not found`)
|
|
39
40
|
}
|
|
41
|
+
const targetTrace = shortenExcessiveLabelPaddingDetour({
|
|
42
|
+
inputProblem,
|
|
43
|
+
trace: originalTargetTrace,
|
|
44
|
+
})
|
|
40
45
|
|
|
41
46
|
const targetPinIds = new Set(targetTrace.pinIds)
|
|
42
47
|
const otherTraces = traces.filter(
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
2
|
+
import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
3
|
+
import type { InputChip, InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
|
|
5
|
+
const MIN_EXCESSIVE_DETOUR_LENGTH_RATIO = 2.5
|
|
6
|
+
|
|
7
|
+
const getTracePathLength = (
|
|
8
|
+
tracePath: SolvedTracePath["tracePath"],
|
|
9
|
+
): number => {
|
|
10
|
+
let pathLength = 0
|
|
11
|
+
for (let pointIndex = 1; pointIndex < tracePath.length; pointIndex++) {
|
|
12
|
+
const previousPoint = tracePath[pointIndex - 1]!
|
|
13
|
+
const point = tracePath[pointIndex]!
|
|
14
|
+
pathLength +=
|
|
15
|
+
Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y)
|
|
16
|
+
}
|
|
17
|
+
return pathLength
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Retries an already-solved severe detour with its exact direct connection
|
|
22
|
+
* first, preventing a sibling branch's label width from inflating text padding.
|
|
23
|
+
* The shorter route is used only when it is less than 40% of the original.
|
|
24
|
+
*/
|
|
25
|
+
export const shortenExcessiveLabelPaddingDetour = ({
|
|
26
|
+
inputProblem,
|
|
27
|
+
trace,
|
|
28
|
+
}: {
|
|
29
|
+
inputProblem: InputProblem
|
|
30
|
+
trace: SolvedTracePath
|
|
31
|
+
}): SolvedTracePath => {
|
|
32
|
+
const exactConnection = inputProblem.directConnections.find((connection) =>
|
|
33
|
+
connection.pinIds.every((pinId) => trace.pinIds.includes(pinId)),
|
|
34
|
+
)
|
|
35
|
+
if (!exactConnection) return trace
|
|
36
|
+
|
|
37
|
+
const chipMap: Record<string, InputChip> = {}
|
|
38
|
+
for (const chip of inputProblem.chips) chipMap[chip.chipId] = chip
|
|
39
|
+
|
|
40
|
+
const candidateSolver = new SchematicTraceSingleLineSolver2({
|
|
41
|
+
pins: trace.pins,
|
|
42
|
+
connectionPair: trace,
|
|
43
|
+
inputProblem: {
|
|
44
|
+
...inputProblem,
|
|
45
|
+
directConnections: [
|
|
46
|
+
exactConnection,
|
|
47
|
+
...inputProblem.directConnections.filter(
|
|
48
|
+
(connection) => connection !== exactConnection,
|
|
49
|
+
),
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
chipMap,
|
|
53
|
+
})
|
|
54
|
+
candidateSolver.solve()
|
|
55
|
+
if (!candidateSolver.solvedTracePath) return trace
|
|
56
|
+
|
|
57
|
+
const currentLength = getTracePathLength(trace.tracePath)
|
|
58
|
+
const candidateLength = getTracePathLength(candidateSolver.solvedTracePath)
|
|
59
|
+
if (currentLength <= candidateLength * MIN_EXCESSIVE_DETOUR_LENGTH_RATIO) {
|
|
60
|
+
return trace
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { ...trace, tracePath: candidateSolver.solvedTracePath }
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
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="-1.2000000000000002,0.30000000000000004 -3.79,0.39" data-type="line" data-label="" points="357.4007220216606,
|
|
2
|
-
globalConnNetId: connectivity_net1" data-x="-3.79" data-y="-0.8" x="71.33574007220213" y="
|
|
3
|
-
globalConnNetId: connectivity_net0" data-x="-
|
|
4
|
-
y-" data-x="-3.79" data-y="-0.3899999999999999" cx="95.5956678700361" cy="
|
|
5
|
-
y+" data-x="-3.79" data-y="0.39" cx="95.5956678700361" cy="
|
|
6
|
-
y+" data-x="-2.22" data-y="0.38000000000000006" cx="254.29602888086637" cy="
|
|
7
|
-
y-" data-x="-2.22" data-y="-0.37999999999999995" cx="254.29602888086637" cy="
|
|
8
|
-
x-" data-x="-1.2000000000000002" data-y="0.30000000000000004" cx="357.4007220216606" cy="
|
|
9
|
-
x-" data-x="-1.2000000000000002" data-y="0.10000000000000003" cx="357.4007220216606" cy="
|
|
10
|
-
x-" data-x="-1.2000000000000002" data-y="-0.09999999999999998" cx="357.4007220216606" cy="
|
|
11
|
-
x-" data-x="-1.2000000000000002" data-y="-0.30000000000000004" cx="357.4007220216606" cy="
|
|
12
|
-
x+" data-x="1.2000000000000002" data-y="-0.30000000000000004" cx="600" cy="
|
|
13
|
-
x+" data-x="1.2000000000000002" data-y="-0.10000000000000003" cx="600" cy="
|
|
14
|
-
x+" data-x="1.2000000000000002" data-y="0.09999999999999998" cx="600" cy="
|
|
15
|
-
x+" data-x="1.2000000000000002" data-y="0.30000000000000004" cx="600" cy="
|
|
16
|
-
orientation: y-" data-x="-3.79" data-y="-0.5900000000000001" cx="95.5956678700361" cy="
|
|
17
|
-
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="-1.2000000000000002,0.30000000000000004 -3.79,0.39" data-type="line" data-label="" points="357.4007220216606,296.2454873646209 95.5956678700361,287.1480144404332" fill="none" stroke="hsl(136, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="-1.2000000000000002,0.30000000000000004 -2.22,0.38000000000000006" data-type="line" data-label="" points="357.4007220216606,296.2454873646209 254.29602888086637,288.1588447653429" fill="none" stroke="hsl(136, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="-3.79,-0.3899999999999999 -2.22,-0.37999999999999995" data-type="line" data-label="" points="95.5956678700361,365.9927797833935 254.29602888086637,364.9819494584837" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-2.22,0.3800000000000002 -2.22,0.5800000000000003 -3.79,0.5800000000000003 -3.79,0.39" data-type="line" data-label="" points="254.29602888086637,288.1588447653429 254.29602888086637,267.94223826714796 95.5956678700361,267.94223826714796 95.5956678700361,287.1480144404332" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.2000000000000002,0.3 -1.4125,0.3 -1.4125,0.5800000000000003 -2.22,0.5800000000000003 -2.22,0.38000000000000006" data-type="line" data-label="" points="357.4007220216606,296.24548736462094 335.9205776173285,296.24548736462094 335.9205776173285,267.94223826714796 254.29602888086637,267.94223826714796 254.29602888086637,288.1588447653429" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-3.79,-0.3899999999999999 -3.79,-0.5900000000000001 -2.22,-0.5900000000000001 -2.22,-0.3800000000000001" data-type="line" data-label="" points="95.5956678700361,365.9927797833935 95.5956678700361,386.2093862815884 254.29602888086637,386.2093862815884 254.29602888086637,364.98194945848377" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="-3.675" data-y="5.551115123125783e-17" x="40" y="287.1480144404332" width="134.44043321299637" height="78.84476534296027" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009892857142857142"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="-2.085" data-y="5.551115123125783e-17" x="221.44404332129963" y="288.1588447653429" width="92.99638989169677" height="76.8231046931408" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009892857142857142"/></g><g><rect data-type="rect" data-label="schematic_component_2" data-x="0" data-y="0" x="357.4007220216606" y="276.028880866426" width="242.5992779783394" height="101.08303249097469" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009892857142857142"/></g><g><rect data-type="rect" data-label="U1" data-x="-0.68" data-y="0.615" x="391.76895306859205" y="251.76895306859208" width="36.38989169675091" height="25.270758122743644" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.009892857142857142"/></g><g><rect data-type="rect" data-label="netId: GND
|
|
2
|
+
globalConnNetId: connectivity_net1" data-x="-3.79" data-y="-0.8" x="71.33574007220213" y="386.2093862815884" width="48.519855595667934" height="42.45487364620942" fill="#00000066" stroke="#000000" stroke-width="0.009892857142857142"/></g><g><rect data-type="rect" data-label="netId: .U1 > .pin1 to .C2 > .pin1
|
|
3
|
+
globalConnNetId: connectivity_net0" data-x="-1.3062500000000001" data-y="0.72" x="336.55234657039705" y="211.33574007220213" width="20.216606498195006" height="84.90974729241879" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.009892857142857142"/></g><g><circle data-type="point" data-label="schematic_port_0
|
|
4
|
+
y-" data-x="-3.79" data-y="-0.3899999999999999" cx="95.5956678700361" cy="365.9927797833935" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_1
|
|
5
|
+
y+" data-x="-3.79" data-y="0.39" cx="95.5956678700361" cy="287.1480144404332" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_2
|
|
6
|
+
y+" data-x="-2.22" data-y="0.38000000000000006" cx="254.29602888086637" cy="288.1588447653429" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_3
|
|
7
|
+
y-" data-x="-2.22" data-y="-0.37999999999999995" cx="254.29602888086637" cy="364.9819494584837" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_4
|
|
8
|
+
x-" data-x="-1.2000000000000002" data-y="0.30000000000000004" cx="357.4007220216606" cy="296.2454873646209" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_5
|
|
9
|
+
x-" data-x="-1.2000000000000002" data-y="0.10000000000000003" cx="357.4007220216606" cy="316.46209386281583" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_6
|
|
10
|
+
x-" data-x="-1.2000000000000002" data-y="-0.09999999999999998" cx="357.4007220216606" cy="336.6787003610108" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_7
|
|
11
|
+
x-" data-x="-1.2000000000000002" data-y="-0.30000000000000004" cx="357.4007220216606" cy="356.8953068592058" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_8
|
|
12
|
+
x+" data-x="1.2000000000000002" data-y="-0.30000000000000004" cx="600" cy="356.8953068592058" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_9
|
|
13
|
+
x+" data-x="1.2000000000000002" data-y="-0.10000000000000003" cx="600" cy="336.67870036101084" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_10
|
|
14
|
+
x+" data-x="1.2000000000000002" data-y="0.09999999999999998" cx="600" cy="316.4620938628159" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_11
|
|
15
|
+
x+" data-x="1.2000000000000002" data-y="0.30000000000000004" cx="600" cy="296.2454873646209" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
16
|
+
orientation: y-" data-x="-3.79" data-y="-0.5900000000000001" cx="95.5956678700361" cy="386.2093862815884" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
17
|
+
orientation: y+" data-x="-1.3062500000000001" data-y="0.3" cx="346.66064981949455" cy="296.24548736462094" 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[
|
|
18
18
|
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
19
19
|
const svg = e.currentTarget;
|
|
20
20
|
const rect = svg.getBoundingClientRect();
|
|
@@ -36,7 +36,7 @@ orientation: y+" data-x="-2.22" data-y="1.78" cx="254.29602888086637" cy="221.44
|
|
|
36
36
|
v.setAttribute('y2', '640');
|
|
37
37
|
|
|
38
38
|
// Calculate real coordinates using inverse transformation
|
|
39
|
-
const matrix = {"a":101.08303249097473,"c":0,"e":478.7003610108303,"b":0,"d":-101.08303249097473,"f":
|
|
39
|
+
const matrix = {"a":101.08303249097473,"c":0,"e":478.7003610108303,"b":0,"d":-101.08303249097473,"f":326.57039711191334};
|
|
40
40
|
// Manually invert and apply the affine transform
|
|
41
41
|
// Since we only use translate and scale, we can directly compute:
|
|
42
42
|
// x' = (x - tx) / sx
|
|
@@ -3,10 +3,28 @@ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipeline
|
|
|
3
3
|
import inputProblem from "./bug-report-20260804T225912Z.json"
|
|
4
4
|
import "tests/fixtures/matcher"
|
|
5
5
|
|
|
6
|
+
const EXPECTED_CHIP_TO_CAPACITOR_MAX_Y = 0.58
|
|
7
|
+
|
|
6
8
|
test("bug-report-20260804T225912Z", () => {
|
|
7
9
|
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
8
10
|
|
|
9
11
|
solver.solve()
|
|
10
12
|
|
|
13
|
+
const chipToCapacitorTrace = solver.traceCleanupSolver
|
|
14
|
+
?.getOutput()
|
|
15
|
+
.traces.find(
|
|
16
|
+
(trace) =>
|
|
17
|
+
trace.pinIds.includes("schematic_port_4") &&
|
|
18
|
+
trace.pinIds.includes("schematic_port_2"),
|
|
19
|
+
)
|
|
20
|
+
expect(chipToCapacitorTrace).toBeDefined()
|
|
21
|
+
if (!chipToCapacitorTrace)
|
|
22
|
+
throw new Error("Chip-to-capacitor trace not found")
|
|
23
|
+
|
|
24
|
+
const maxTraceY = Math.max(
|
|
25
|
+
...chipToCapacitorTrace.tracePath.map((point) => point.y),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
expect(maxTraceY).toBeCloseTo(EXPECTED_CHIP_TO_CAPACITOR_MAX_Y)
|
|
11
29
|
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
12
30
|
})
|