@tscircuit/schematic-trace-solver 0.0.122 → 0.0.123
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.js +51 -3
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +50 -5
- package/lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts +29 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260804T171919Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260804T225912Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +355 -0
- package/tests/bug-reports/bug-report-20260804T171919Z/bug-report-20260804T171919Z.json +1775 -0
- package/tests/bug-reports/bug-report-20260804T171919Z/bug-report-20260804T171919Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +60 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/bug-report-20260804T225912Z.json +148 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/bug-report-20260804T225912Z.test.ts +12 -0
- package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +112 -0
- package/tests/repros/assets/repro161-power-section-autolayout.input.json +578 -0
- package/tests/repros/repro161-power-section-autolayout.test.ts +34 -0
package/dist/index.js
CHANGED
|
@@ -11181,6 +11181,7 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
|
11181
11181
|
};
|
|
11182
11182
|
|
|
11183
11183
|
// lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
|
|
11184
|
+
var NET_LABEL_INTERIOR_INSET = 1e-6;
|
|
11184
11185
|
var pathIntersectsAnyNetLabel = ({
|
|
11185
11186
|
path,
|
|
11186
11187
|
netLabelPlacements
|
|
@@ -11195,9 +11196,30 @@ var pathIntersectsAnyNetLabel = ({
|
|
|
11195
11196
|
}
|
|
11196
11197
|
return false;
|
|
11197
11198
|
};
|
|
11199
|
+
var pathEntersAnyNetLabel = ({
|
|
11200
|
+
path,
|
|
11201
|
+
netLabelPlacements
|
|
11202
|
+
}) => {
|
|
11203
|
+
for (const label of netLabelPlacements) {
|
|
11204
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
11205
|
+
const labelInteriorBounds = {
|
|
11206
|
+
minX: labelBounds.minX + NET_LABEL_INTERIOR_INSET,
|
|
11207
|
+
minY: labelBounds.minY + NET_LABEL_INTERIOR_INSET,
|
|
11208
|
+
maxX: labelBounds.maxX - NET_LABEL_INTERIOR_INSET,
|
|
11209
|
+
maxY: labelBounds.maxY - NET_LABEL_INTERIOR_INSET
|
|
11210
|
+
};
|
|
11211
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
11212
|
+
if (segmentIntersectsRect2(path[index], path[index + 1], labelInteriorBounds)) {
|
|
11213
|
+
return true;
|
|
11214
|
+
}
|
|
11215
|
+
}
|
|
11216
|
+
}
|
|
11217
|
+
return false;
|
|
11218
|
+
};
|
|
11198
11219
|
|
|
11199
11220
|
// lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
|
|
11200
11221
|
var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
|
|
11222
|
+
var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
|
|
11201
11223
|
var getSharedPin = ({
|
|
11202
11224
|
donorTrace,
|
|
11203
11225
|
branchTrace
|
|
@@ -11274,6 +11296,7 @@ var getAlignedBranchPath = ({
|
|
|
11274
11296
|
};
|
|
11275
11297
|
var candidateIsClear = ({
|
|
11276
11298
|
candidateTrace,
|
|
11299
|
+
originalTrace,
|
|
11277
11300
|
traces,
|
|
11278
11301
|
inputProblem,
|
|
11279
11302
|
netLabelPlacements
|
|
@@ -11288,10 +11311,34 @@ var candidateIsClear = ({
|
|
|
11288
11311
|
if (doesPathCoincideWithTraces(candidateTrace.tracePath, otherNetTraces)) {
|
|
11289
11312
|
return false;
|
|
11290
11313
|
}
|
|
11291
|
-
|
|
11314
|
+
const otherNetLabelPlacements = netLabelPlacements.filter(
|
|
11315
|
+
(label) => label.globalConnNetId !== candidateTrace.globalConnNetId
|
|
11316
|
+
);
|
|
11317
|
+
if (pathIntersectsAnyNetLabel({
|
|
11292
11318
|
path: candidateTrace.tracePath,
|
|
11293
|
-
netLabelPlacements
|
|
11294
|
-
})
|
|
11319
|
+
netLabelPlacements: otherNetLabelPlacements
|
|
11320
|
+
})) {
|
|
11321
|
+
return false;
|
|
11322
|
+
}
|
|
11323
|
+
const sameNetLabelPlacements = netLabelPlacements.filter(
|
|
11324
|
+
(label) => label.globalConnNetId === candidateTrace.globalConnNetId
|
|
11325
|
+
);
|
|
11326
|
+
if (!pathIntersectsAnyNetLabel({
|
|
11327
|
+
path: candidateTrace.tracePath,
|
|
11328
|
+
netLabelPlacements: sameNetLabelPlacements
|
|
11329
|
+
})) {
|
|
11330
|
+
return true;
|
|
11331
|
+
}
|
|
11332
|
+
if (pathEntersAnyNetLabel({
|
|
11333
|
+
path: candidateTrace.tracePath,
|
|
11334
|
+
netLabelPlacements: sameNetLabelPlacements
|
|
11335
|
+
})) {
|
|
11336
|
+
return false;
|
|
11337
|
+
}
|
|
11338
|
+
const originalRail = getLongestHorizontalSegment(originalTrace);
|
|
11339
|
+
const candidateRail = getLongestHorizontalSegment(candidateTrace);
|
|
11340
|
+
if (!originalRail || !candidateRail) return false;
|
|
11341
|
+
return Math.abs(originalRail.start.y - candidateRail.start.y) <= MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET;
|
|
11295
11342
|
};
|
|
11296
11343
|
var alignSameNetJunctions = ({
|
|
11297
11344
|
inputProblem,
|
|
@@ -11324,6 +11371,7 @@ var alignSameNetJunctions = ({
|
|
|
11324
11371
|
}
|
|
11325
11372
|
if (!candidateIsClear({
|
|
11326
11373
|
candidateTrace,
|
|
11374
|
+
originalTrace: branchTrace,
|
|
11327
11375
|
traces: outputTraces,
|
|
11328
11376
|
inputProblem,
|
|
11329
11377
|
netLabelPlacements
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
} from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
|
|
13
13
|
import type { InputPin, InputProblem } from "lib/types/InputProblem"
|
|
14
14
|
import { doesPathCoincideWithTraces } from "lib/utils/doesPathCoincideWithTraces"
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
pathEntersAnyNetLabel,
|
|
17
|
+
pathIntersectsAnyNetLabel,
|
|
18
|
+
} from "./pathIntersectsAnyNetLabel"
|
|
16
19
|
|
|
17
20
|
interface AlignSameNetJunctionsInput {
|
|
18
21
|
inputProblem: InputProblem
|
|
@@ -25,7 +28,10 @@ interface HorizontalSegment {
|
|
|
25
28
|
end: Point
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
// Only near-level load pins should be combined onto one horizontal rail.
|
|
28
32
|
const MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2
|
|
33
|
+
// Limit label-boundary alignment to small corrections that cannot create spikes.
|
|
34
|
+
const MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2
|
|
29
35
|
|
|
30
36
|
const getSharedPin = ({
|
|
31
37
|
donorTrace,
|
|
@@ -135,11 +141,13 @@ const getAlignedBranchPath = ({
|
|
|
135
141
|
|
|
136
142
|
const candidateIsClear = ({
|
|
137
143
|
candidateTrace,
|
|
144
|
+
originalTrace,
|
|
138
145
|
traces,
|
|
139
146
|
inputProblem,
|
|
140
147
|
netLabelPlacements,
|
|
141
148
|
}: {
|
|
142
149
|
candidateTrace: SolvedTracePath
|
|
150
|
+
originalTrace: SolvedTracePath
|
|
143
151
|
traces: SolvedTracePath[]
|
|
144
152
|
inputProblem: InputProblem
|
|
145
153
|
netLabelPlacements: NetLabelPlacement[]
|
|
@@ -156,10 +164,46 @@ const candidateIsClear = ({
|
|
|
156
164
|
return false
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
const otherNetLabelPlacements = netLabelPlacements.filter(
|
|
168
|
+
(label) => label.globalConnNetId !== candidateTrace.globalConnNetId,
|
|
169
|
+
)
|
|
170
|
+
if (
|
|
171
|
+
pathIntersectsAnyNetLabel({
|
|
172
|
+
path: candidateTrace.tracePath,
|
|
173
|
+
netLabelPlacements: otherNetLabelPlacements,
|
|
174
|
+
})
|
|
175
|
+
) {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const sameNetLabelPlacements = netLabelPlacements.filter(
|
|
180
|
+
(label) => label.globalConnNetId === candidateTrace.globalConnNetId,
|
|
181
|
+
)
|
|
182
|
+
// A same-net rail may follow its label edge, but never enter the label body.
|
|
183
|
+
if (
|
|
184
|
+
!pathIntersectsAnyNetLabel({
|
|
185
|
+
path: candidateTrace.tracePath,
|
|
186
|
+
netLabelPlacements: sameNetLabelPlacements,
|
|
187
|
+
})
|
|
188
|
+
) {
|
|
189
|
+
return true
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
pathEntersAnyNetLabel({
|
|
193
|
+
path: candidateTrace.tracePath,
|
|
194
|
+
netLabelPlacements: sameNetLabelPlacements,
|
|
195
|
+
})
|
|
196
|
+
) {
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const originalRail = getLongestHorizontalSegment(originalTrace)
|
|
201
|
+
const candidateRail = getLongestHorizontalSegment(candidateTrace)
|
|
202
|
+
if (!originalRail || !candidateRail) return false
|
|
203
|
+
return (
|
|
204
|
+
Math.abs(originalRail.start.y - candidateRail.start.y) <=
|
|
205
|
+
MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET
|
|
206
|
+
)
|
|
163
207
|
}
|
|
164
208
|
|
|
165
209
|
export const alignSameNetJunctions = ({
|
|
@@ -202,6 +246,7 @@ export const alignSameNetJunctions = ({
|
|
|
202
246
|
if (
|
|
203
247
|
!candidateIsClear({
|
|
204
248
|
candidateTrace,
|
|
249
|
+
originalTrace: branchTrace,
|
|
205
250
|
traces: outputTraces,
|
|
206
251
|
inputProblem,
|
|
207
252
|
netLabelPlacements,
|
|
@@ -3,6 +3,9 @@ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetL
|
|
|
3
3
|
import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
4
4
|
import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
5
5
|
|
|
6
|
+
// Insetting distinguishes harmless edge contact from entering the label body.
|
|
7
|
+
const NET_LABEL_INTERIOR_INSET = 1e-6
|
|
8
|
+
|
|
6
9
|
export const pathIntersectsAnyNetLabel = ({
|
|
7
10
|
path,
|
|
8
11
|
netLabelPlacements,
|
|
@@ -20,3 +23,29 @@ export const pathIntersectsAnyNetLabel = ({
|
|
|
20
23
|
}
|
|
21
24
|
return false
|
|
22
25
|
}
|
|
26
|
+
|
|
27
|
+
export const pathEntersAnyNetLabel = ({
|
|
28
|
+
path,
|
|
29
|
+
netLabelPlacements,
|
|
30
|
+
}: {
|
|
31
|
+
path: Point[]
|
|
32
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
33
|
+
}) => {
|
|
34
|
+
for (const label of netLabelPlacements) {
|
|
35
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height)
|
|
36
|
+
const labelInteriorBounds = {
|
|
37
|
+
minX: labelBounds.minX + NET_LABEL_INTERIOR_INSET,
|
|
38
|
+
minY: labelBounds.minY + NET_LABEL_INTERIOR_INSET,
|
|
39
|
+
maxX: labelBounds.maxX - NET_LABEL_INTERIOR_INSET,
|
|
40
|
+
maxY: labelBounds.maxY - NET_LABEL_INTERIOR_INSET,
|
|
41
|
+
}
|
|
42
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
43
|
+
if (
|
|
44
|
+
segmentIntersectsRect(path[index], path[index + 1], labelInteriorBounds)
|
|
45
|
+
) {
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false
|
|
51
|
+
}
|
package/package.json
CHANGED