@tscircuit/schematic-trace-solver 0.0.85 → 0.0.87

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 CHANGED
@@ -5213,6 +5213,19 @@ var dir = (facingDirection) => {
5213
5213
  // lib/solvers/Example28Solver/types.ts
5214
5214
  var EPS7 = 1e-9;
5215
5215
 
5216
+ // lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
5217
+ var segmentRunsAlongRectBoundary = (start, end, rect) => {
5218
+ const isVertical4 = Math.abs(start.x - end.x) < EPS7;
5219
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
5220
+ if (isVertical4) {
5221
+ return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
5222
+ }
5223
+ if (isHorizontal3) {
5224
+ return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
5225
+ }
5226
+ return false;
5227
+ };
5228
+
5216
5229
  // lib/solvers/Example28Solver/geometry.ts
5217
5230
  var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
5218
5231
  var getPathLength = (path) => {
@@ -5350,17 +5363,6 @@ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
5350
5363
  }
5351
5364
  return false;
5352
5365
  };
5353
- var segmentRunsAlongRectBoundary = (start, end, rect) => {
5354
- const isVertical4 = Math.abs(start.x - end.x) < EPS7;
5355
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
5356
- if (isVertical4) {
5357
- return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
5358
- }
5359
- if (isHorizontal3) {
5360
- return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
5361
- }
5362
- return false;
5363
- };
5364
5366
 
5365
5367
  // lib/solvers/Example28Solver/getMovedAnchorPointForReroute.ts
5366
5368
  var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
@@ -5775,6 +5777,57 @@ var getPointDistanceFromRect = (point, rect) => {
5775
5777
  return dx + dy;
5776
5778
  };
5777
5779
 
5780
+ // lib/solvers/Example28Solver/countCrossingsWithOtherTraces.ts
5781
+ var countCrossingsWithOtherTraces = (params) => {
5782
+ const { trace, outputTraces } = params;
5783
+ let crossingCount = 0;
5784
+ for (const otherTrace of outputTraces) {
5785
+ if (otherTrace.mspPairId === trace.mspPairId) continue;
5786
+ if (otherTrace.globalConnNetId === trace.globalConnNetId) continue;
5787
+ crossingCount += countPathIntersections(
5788
+ trace.tracePath,
5789
+ otherTrace.tracePath
5790
+ );
5791
+ }
5792
+ return crossingCount;
5793
+ };
5794
+
5795
+ // lib/solvers/Example28Solver/doesPathRunAlongChipBoundary.ts
5796
+ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
5797
+ for (let i = 0; i < path.length - 1; i++) {
5798
+ const start = path[i];
5799
+ const end = path[i + 1];
5800
+ for (const obstacle of chipObstacles) {
5801
+ if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
5802
+ if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS7) {
5803
+ return true;
5804
+ }
5805
+ }
5806
+ }
5807
+ return false;
5808
+ };
5809
+ var getSegmentOverlapWithRectSpan = (start, end, rect) => {
5810
+ const isVertical4 = Math.abs(start.x - end.x) < EPS7;
5811
+ if (isVertical4) {
5812
+ return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
5813
+ }
5814
+ return Math.min(Math.max(start.x, end.x), rect.maxX) - Math.max(Math.min(start.x, end.x), rect.minX);
5815
+ };
5816
+
5817
+ // lib/solvers/Example28Solver/scoreSolutionTracePath.ts
5818
+ var scoreSolutionTracePath = (params) => {
5819
+ const { traces, outputTraces, chipObstacles } = params;
5820
+ let chipEdgeRuns = 0;
5821
+ let traceCrossings = 0;
5822
+ for (const trace of traces) {
5823
+ if (doesPathRunAlongChipBoundary(trace.tracePath, chipObstacles)) {
5824
+ chipEdgeRuns += 1;
5825
+ }
5826
+ traceCrossings += countCrossingsWithOtherTraces({ trace, outputTraces });
5827
+ }
5828
+ return { chipEdgeRuns, traceCrossings };
5829
+ };
5830
+
5778
5831
  // lib/solvers/Example28Solver/visualize.ts
5779
5832
  var CANDIDATE_SELECTED_COLOR = "blue";
5780
5833
  var CANDIDATE_REJECTED_COLOR = "red";
@@ -5847,6 +5900,13 @@ ${label.netId ?? label.globalConnNetId}`
5847
5900
  var LABEL_OUTWARD_STEP = 0.1;
5848
5901
  var LABEL_MAX_OUTWARD_STEPS = 10;
5849
5902
  var LABEL_TRACE_CLEARANCE = 0.1;
5903
+ var isScoreBetter = (rerouteScore, labelMoveScore) => {
5904
+ if (!rerouteScore) return false;
5905
+ if (rerouteScore.chipEdgeRuns !== labelMoveScore.chipEdgeRuns) {
5906
+ return rerouteScore.chipEdgeRuns < labelMoveScore.chipEdgeRuns;
5907
+ }
5908
+ return rerouteScore.traceCrossings < labelMoveScore.traceCrossings;
5909
+ };
5850
5910
  var Example28Solver = class extends BaseSolver {
5851
5911
  inputProblem;
5852
5912
  traces;
@@ -5949,7 +6009,7 @@ var Example28Solver = class extends BaseSolver {
5949
6009
  );
5950
6010
  if (traceIndex === -1) return;
5951
6011
  const currentTrace = this.outputTraces[traceIndex];
5952
- if (this.tryMoveLabelOutward(overlap.label)) return;
6012
+ const labelMove = this.tryMoveLabelOutward(overlap.label);
5953
6013
  const rerouteResult = findBestReroutePath({
5954
6014
  trace: currentTrace,
5955
6015
  obstacleLabel: overlap.label,
@@ -5959,6 +6019,26 @@ var Example28Solver = class extends BaseSolver {
5959
6019
  chipObstacles: this.chipObstacles
5960
6020
  });
5961
6021
  this.currentCandidateResults = rerouteResult.candidateResults;
6022
+ let rerouteScore = null;
6023
+ if (rerouteResult.bestPath) {
6024
+ rerouteScore = scoreSolutionTracePath({
6025
+ traces: [{ ...currentTrace, tracePath: rerouteResult.bestPath }],
6026
+ outputTraces: this.outputTraces,
6027
+ chipObstacles: this.chipObstacles
6028
+ });
6029
+ }
6030
+ if (labelMove) {
6031
+ const labelMoveScore = scoreSolutionTracePath({
6032
+ traces: [labelMove.connectorTrace, currentTrace],
6033
+ outputTraces: this.outputTraces,
6034
+ chipObstacles: this.chipObstacles
6035
+ });
6036
+ if (!isScoreBetter(rerouteScore, labelMoveScore)) {
6037
+ this.outputNetLabelPlacements[labelMove.labelIndex] = labelMove.movedLabel;
6038
+ this.outputTraces.push(labelMove.connectorTrace);
6039
+ return;
6040
+ }
6041
+ }
5962
6042
  if (!rerouteResult.bestPath) return;
5963
6043
  const originalTracePath = currentTrace.tracePath;
5964
6044
  this.outputTraces[traceIndex] = {
@@ -5976,17 +6056,17 @@ var Example28Solver = class extends BaseSolver {
5976
6056
  const labelIndex = this.outputNetLabelPlacements.findIndex(
5977
6057
  (label2) => label2.globalConnNetId === labelToMove.globalConnNetId && label2.anchorPoint.x === labelToMove.anchorPoint.x && label2.anchorPoint.y === labelToMove.anchorPoint.y && label2.pinIds.join(",") === labelToMove.pinIds.join(",")
5978
6058
  );
5979
- if (labelIndex === -1) return false;
6059
+ if (labelIndex === -1) return null;
5980
6060
  const label = this.outputNetLabelPlacements[labelIndex];
5981
- if (label.mspConnectionPairIds.length > 0) return false;
5982
- if (label.pinIds.length !== 1) return false;
6061
+ if (label.mspConnectionPairIds.length > 0) return null;
6062
+ if (label.pinIds.length !== 1) return null;
5983
6063
  if (this.outputNetLabelPlacements.filter(
5984
6064
  (otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId
5985
6065
  ).length !== 1) {
5986
- return false;
6066
+ return null;
5987
6067
  }
5988
6068
  const outward = dir(label.orientation);
5989
- if (outward.x === 0 && outward.y === 0) return false;
6069
+ if (outward.x === 0 && outward.y === 0) return null;
5990
6070
  for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
5991
6071
  const distance3 = step * LABEL_OUTWARD_STEP;
5992
6072
  const candidate = {
@@ -6022,11 +6102,9 @@ var Example28Solver = class extends BaseSolver {
6022
6102
  }).length > 0) {
6023
6103
  continue;
6024
6104
  }
6025
- this.outputNetLabelPlacements[labelIndex] = candidate;
6026
- this.outputTraces.push(connectorTrace);
6027
- return true;
6105
+ return { labelIndex, movedLabel: candidate, connectorTrace };
6028
6106
  }
6029
- return false;
6107
+ return null;
6030
6108
  }
6031
6109
  visualize() {
6032
6110
  return visualizeExample28Solver({
@@ -20,6 +20,10 @@ import type {
20
20
  import { dir } from "lib/utils/dir"
21
21
  import { moveAttachedLabelsToReroutedTrace } from "./labelMovement"
22
22
  import { findBestReroutePath } from "./reroute"
23
+ import {
24
+ scoreSolutionTracePath,
25
+ type TracePathScore,
26
+ } from "./scoreSolutionTracePath"
23
27
  import type { Example28SolverParams, RerouteCandidateResult } from "./types"
24
28
  import { visualizeExample28Solver } from "./visualize"
25
29
 
@@ -27,6 +31,17 @@ const LABEL_OUTWARD_STEP = 0.1
27
31
  const LABEL_MAX_OUTWARD_STEPS = 10
28
32
  const LABEL_TRACE_CLEARANCE = 0.1
29
33
 
34
+ const isScoreBetter = (
35
+ rerouteScore: TracePathScore | null,
36
+ labelMoveScore: TracePathScore,
37
+ ) => {
38
+ if (!rerouteScore) return false
39
+ if (rerouteScore.chipEdgeRuns !== labelMoveScore.chipEdgeRuns) {
40
+ return rerouteScore.chipEdgeRuns < labelMoveScore.chipEdgeRuns
41
+ }
42
+ return rerouteScore.traceCrossings < labelMoveScore.traceCrossings
43
+ }
44
+
30
45
  export class Example28Solver extends BaseSolver {
31
46
  inputProblem: InputProblem
32
47
  traces: SolvedTracePath[]
@@ -160,7 +175,7 @@ export class Example28Solver extends BaseSolver {
160
175
  if (traceIndex === -1) return
161
176
 
162
177
  const currentTrace = this.outputTraces[traceIndex]!
163
- if (this.tryMoveLabelOutward(overlap.label)) return
178
+ const labelMove = this.tryMoveLabelOutward(overlap.label)
164
179
 
165
180
  const rerouteResult = findBestReroutePath({
166
181
  trace: currentTrace,
@@ -172,6 +187,29 @@ export class Example28Solver extends BaseSolver {
172
187
  })
173
188
  this.currentCandidateResults = rerouteResult.candidateResults
174
189
 
190
+ let rerouteScore: TracePathScore | null = null
191
+ if (rerouteResult.bestPath) {
192
+ rerouteScore = scoreSolutionTracePath({
193
+ traces: [{ ...currentTrace, tracePath: rerouteResult.bestPath }],
194
+ outputTraces: this.outputTraces,
195
+ chipObstacles: this.chipObstacles,
196
+ })
197
+ }
198
+
199
+ if (labelMove) {
200
+ const labelMoveScore = scoreSolutionTracePath({
201
+ traces: [labelMove.connectorTrace, currentTrace],
202
+ outputTraces: this.outputTraces,
203
+ chipObstacles: this.chipObstacles,
204
+ })
205
+ if (!isScoreBetter(rerouteScore, labelMoveScore)) {
206
+ this.outputNetLabelPlacements[labelMove.labelIndex] =
207
+ labelMove.movedLabel
208
+ this.outputTraces.push(labelMove.connectorTrace)
209
+ return
210
+ }
211
+ }
212
+
175
213
  if (!rerouteResult.bestPath) return
176
214
 
177
215
  const originalTracePath = currentTrace.tracePath
@@ -195,21 +233,21 @@ export class Example28Solver extends BaseSolver {
195
233
  label.anchorPoint.y === labelToMove.anchorPoint.y &&
196
234
  label.pinIds.join(",") === labelToMove.pinIds.join(","),
197
235
  )
198
- if (labelIndex === -1) return false
236
+ if (labelIndex === -1) return null
199
237
 
200
238
  const label = this.outputNetLabelPlacements[labelIndex]!
201
- if (label.mspConnectionPairIds.length > 0) return false
202
- if (label.pinIds.length !== 1) return false
239
+ if (label.mspConnectionPairIds.length > 0) return null
240
+ if (label.pinIds.length !== 1) return null
203
241
  if (
204
242
  this.outputNetLabelPlacements.filter(
205
243
  (otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId,
206
244
  ).length !== 1
207
245
  ) {
208
- return false
246
+ return null
209
247
  }
210
248
 
211
249
  const outward = dir(label.orientation)
212
- if (outward.x === 0 && outward.y === 0) return false
250
+ if (outward.x === 0 && outward.y === 0) return null
213
251
 
214
252
  for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
215
253
  const distance = step * LABEL_OUTWARD_STEP
@@ -252,12 +290,10 @@ export class Example28Solver extends BaseSolver {
252
290
  continue
253
291
  }
254
292
 
255
- this.outputNetLabelPlacements[labelIndex] = candidate
256
- this.outputTraces.push(connectorTrace)
257
- return true
293
+ return { labelIndex, movedLabel: candidate, connectorTrace }
258
294
  }
259
295
 
260
- return false
296
+ return null
261
297
  }
262
298
 
263
299
  override visualize(): GraphicsObject {
@@ -0,0 +1,19 @@
1
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
2
+ import { countPathIntersections } from "./geometry"
3
+
4
+ export const countCrossingsWithOtherTraces = (params: {
5
+ trace: SolvedTracePath
6
+ outputTraces: SolvedTracePath[]
7
+ }) => {
8
+ const { trace, outputTraces } = params
9
+ let crossingCount = 0
10
+ for (const otherTrace of outputTraces) {
11
+ if (otherTrace.mspPairId === trace.mspPairId) continue
12
+ if (otherTrace.globalConnNetId === trace.globalConnNetId) continue
13
+ crossingCount += countPathIntersections(
14
+ trace.tracePath,
15
+ otherTrace.tracePath,
16
+ )
17
+ }
18
+ return crossingCount
19
+ }
@@ -0,0 +1,41 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import { segmentRunsAlongRectBoundary } from "./segmentRunsAlongRectBoundary"
3
+ import type { ChipObstacle } from "./types"
4
+ import { EPS } from "./types"
5
+
6
+ export const doesPathRunAlongChipBoundary = (
7
+ path: Point[],
8
+ chipObstacles: ChipObstacle[],
9
+ ) => {
10
+ for (let i = 0; i < path.length - 1; i++) {
11
+ const start = path[i]!
12
+ const end = path[i + 1]!
13
+ for (const obstacle of chipObstacles) {
14
+ if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue
15
+ if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS) {
16
+ return true
17
+ }
18
+ }
19
+ }
20
+ return false
21
+ }
22
+
23
+ const getSegmentOverlapWithRectSpan = (
24
+ start: Point,
25
+ end: Point,
26
+ rect: ChipObstacle,
27
+ ) => {
28
+ const isVertical = Math.abs(start.x - end.x) < EPS
29
+
30
+ if (isVertical) {
31
+ return (
32
+ Math.min(Math.max(start.y, end.y), rect.maxY) -
33
+ Math.max(Math.min(start.y, end.y), rect.minY)
34
+ )
35
+ }
36
+
37
+ return (
38
+ Math.min(Math.max(start.x, end.x), rect.maxX) -
39
+ Math.max(Math.min(start.x, end.x), rect.minX)
40
+ )
41
+ }
@@ -1,5 +1,6 @@
1
1
  import type { Point } from "@tscircuit/math-utils"
2
2
  import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
3
+ import { segmentRunsAlongRectBoundary } from "./segmentRunsAlongRectBoundary"
3
4
  import type { ChipObstacle, PathSegment, SegmentOrientation } from "./types"
4
5
  import { EPS } from "./types"
5
6
 
@@ -221,26 +222,3 @@ export const isPathCollidingWithChipInterior = (
221
222
  }
222
223
  return false
223
224
  }
224
-
225
- const segmentRunsAlongRectBoundary = (
226
- start: Point,
227
- end: Point,
228
- rect: ChipObstacle,
229
- ) => {
230
- const isVertical = Math.abs(start.x - end.x) < EPS
231
- const isHorizontal = Math.abs(start.y - end.y) < EPS
232
-
233
- if (isVertical) {
234
- return (
235
- Math.abs(start.x - rect.minX) < EPS || Math.abs(start.x - rect.maxX) < EPS
236
- )
237
- }
238
-
239
- if (isHorizontal) {
240
- return (
241
- Math.abs(start.y - rect.minY) < EPS || Math.abs(start.y - rect.maxY) < EPS
242
- )
243
- }
244
-
245
- return false
246
- }
@@ -0,0 +1,26 @@
1
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
2
+ import { countCrossingsWithOtherTraces } from "./countCrossingsWithOtherTraces"
3
+ import { doesPathRunAlongChipBoundary } from "./doesPathRunAlongChipBoundary"
4
+ import type { ChipObstacle } from "./types"
5
+
6
+ export type TracePathScore = {
7
+ chipEdgeRuns: number
8
+ traceCrossings: number
9
+ }
10
+
11
+ export const scoreSolutionTracePath = (params: {
12
+ traces: SolvedTracePath[]
13
+ outputTraces: SolvedTracePath[]
14
+ chipObstacles: ChipObstacle[]
15
+ }): TracePathScore => {
16
+ const { traces, outputTraces, chipObstacles } = params
17
+ let chipEdgeRuns = 0
18
+ let traceCrossings = 0
19
+ for (const trace of traces) {
20
+ if (doesPathRunAlongChipBoundary(trace.tracePath, chipObstacles)) {
21
+ chipEdgeRuns += 1
22
+ }
23
+ traceCrossings += countCrossingsWithOtherTraces({ trace, outputTraces })
24
+ }
25
+ return { chipEdgeRuns, traceCrossings }
26
+ }
@@ -0,0 +1,26 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import type { ChipObstacle } from "./types"
3
+ import { EPS } from "./types"
4
+
5
+ export const segmentRunsAlongRectBoundary = (
6
+ start: Point,
7
+ end: Point,
8
+ rect: ChipObstacle,
9
+ ) => {
10
+ const isVertical = Math.abs(start.x - end.x) < EPS
11
+ const isHorizontal = Math.abs(start.y - end.y) < EPS
12
+
13
+ if (isVertical) {
14
+ return (
15
+ Math.abs(start.x - rect.minX) < EPS || Math.abs(start.x - rect.maxX) < EPS
16
+ )
17
+ }
18
+
19
+ if (isHorizontal) {
20
+ return (
21
+ Math.abs(start.y - rect.minY) < EPS || Math.abs(start.y - rect.maxY) < EPS
22
+ )
23
+ }
24
+
25
+ return false
26
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.85",
4
+ "version": "0.0.87",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/assets/example50.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -0,0 +1,149 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "schematic_component_0",
5
+ "center": {
6
+ "x": -1.4,
7
+ "y": -6.6
8
+ },
9
+ "width": 1.1,
10
+ "height": 0.7789106999999991,
11
+ "pins": [
12
+ {
13
+ "pinId": "R7.1",
14
+ "x": -1.95,
15
+ "y": -6.6
16
+ },
17
+ {
18
+ "pinId": "R7.2",
19
+ "x": -0.85,
20
+ "y": -6.6
21
+ }
22
+ ],
23
+ "sectionId": "qspi"
24
+ },
25
+ {
26
+ "chipId": "schematic_component_1",
27
+ "center": {
28
+ "x": 0,
29
+ "y": -8.4
30
+ },
31
+ "width": 2.245,
32
+ "height": 1,
33
+ "pins": [
34
+ {
35
+ "pinId": "U5.1",
36
+ "x": -1.5225,
37
+ "y": -8.1
38
+ },
39
+ {
40
+ "pinId": "U5.2",
41
+ "x": -1.5225,
42
+ "y": -8.3
43
+ },
44
+ {
45
+ "pinId": "U5.3",
46
+ "x": -1.5225,
47
+ "y": -8.5
48
+ },
49
+ {
50
+ "pinId": "U5.4",
51
+ "x": -1.5225,
52
+ "y": -8.700000000000001
53
+ },
54
+ {
55
+ "pinId": "U5.5",
56
+ "x": 1.5225,
57
+ "y": -8.1
58
+ },
59
+ {
60
+ "pinId": "U5.6",
61
+ "x": 1.5225,
62
+ "y": -8.3
63
+ },
64
+ {
65
+ "pinId": "U5.7",
66
+ "x": 1.5225,
67
+ "y": -8.700000000000001
68
+ },
69
+ {
70
+ "pinId": "U5.8",
71
+ "x": 1.5225,
72
+ "y": -8.5
73
+ }
74
+ ],
75
+ "sectionId": "qspi"
76
+ }
77
+ ],
78
+ "directConnections": [],
79
+ "netConnections": [
80
+ {
81
+ "netId": "V3V3",
82
+ "pinIds": [
83
+ "R7.1",
84
+ "U5.8"
85
+ ],
86
+ "netLabelWidth": 0.6
87
+ },
88
+ {
89
+ "netId": "QSPI_CS",
90
+ "pinIds": [
91
+ "R7.2",
92
+ "U5.4"
93
+ ],
94
+ "netLabelWidth": 0.96
95
+ },
96
+ {
97
+ "netId": "QSPI_SCK",
98
+ "pinIds": [
99
+ "U5.1"
100
+ ],
101
+ "netLabelWidth": 1.08
102
+ },
103
+ {
104
+ "netId": "QSPI_DATA0",
105
+ "pinIds": [
106
+ "U5.2"
107
+ ],
108
+ "netLabelWidth": 1.32
109
+ },
110
+ {
111
+ "netId": "QSPI_DATA1",
112
+ "pinIds": [
113
+ "U5.3"
114
+ ],
115
+ "netLabelWidth": 1.32
116
+ },
117
+ {
118
+ "netId": "QSPI_DATA2",
119
+ "pinIds": [
120
+ "U5.5"
121
+ ],
122
+ "netLabelWidth": 1.32
123
+ },
124
+ {
125
+ "netId": "QSPI_DATA3",
126
+ "pinIds": [
127
+ "U5.6"
128
+ ],
129
+ "netLabelWidth": 1.32
130
+ },
131
+ {
132
+ "netId": "GND",
133
+ "pinIds": [
134
+ "U5.7"
135
+ ],
136
+ "netLabelWidth": 0.48
137
+ }
138
+ ],
139
+ "availableNetLabelOrientations": {
140
+ "V3V3": [
141
+ "y+"
142
+ ],
143
+ "GND": [
144
+ "y-"
145
+ ]
146
+ },
147
+ "maxMspPairDistance": 2.4
148
+ }
149
+
@@ -0,0 +1,190 @@
1
+ <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="100%" height="100%" fill="white" />
3
+ <g>
4
+ <circle data-type="point" data-label="R7.1
5
+ x-" data-x="-1.95" data-y="-6.6" cx="126.46276136167273" cy="214.47382063245198" r="3" fill="hsl(232, 100%, 50%, 0.8)" />
6
+ </g>
7
+ <g>
8
+ <circle data-type="point" data-label="R7.2
9
+ x+" data-x="-0.8499999999999999" data-y="-6.6" cx="232.90824261275273" cy="214.47382063245198" r="3" fill="hsl(233, 100%, 50%, 0.8)" />
10
+ </g>
11
+ <g>
12
+ <circle data-type="point" data-label="U5.1
13
+ x-" data-x="-1.5225" data-y="-8.1" cx="167.83134612061517" cy="359.6267496111974" r="3" fill="hsl(203, 100%, 50%, 0.8)" />
14
+ </g>
15
+ <g>
16
+ <circle data-type="point" data-label="U5.2
17
+ x-" data-x="-1.5225" data-y="-8.3" cx="167.83134612061517" cy="378.9804734750303" r="3" fill="hsl(204, 100%, 50%, 0.8)" />
18
+ </g>
19
+ <g>
20
+ <circle data-type="point" data-label="U5.3
21
+ x-" data-x="-1.5225" data-y="-8.5" cx="167.83134612061517" cy="398.33419733886296" r="3" fill="hsl(205, 100%, 50%, 0.8)" />
22
+ </g>
23
+ <g>
24
+ <circle data-type="point" data-label="U5.4
25
+ x-" data-x="-1.5225" data-y="-8.700000000000001" cx="167.83134612061517" cy="417.68792120269575" r="3" fill="hsl(206, 100%, 50%, 0.8)" />
26
+ </g>
27
+ <g>
28
+ <circle data-type="point" data-label="U5.5
29
+ x+" data-x="1.5225" data-y="-8.1" cx="462.4917919474684" cy="359.6267496111974" r="3" fill="hsl(207, 100%, 50%, 0.8)" />
30
+ </g>
31
+ <g>
32
+ <circle data-type="point" data-label="U5.6
33
+ x+" data-x="1.5225" data-y="-8.3" cx="462.4917919474684" cy="378.9804734750303" r="3" fill="hsl(208, 100%, 50%, 0.8)" />
34
+ </g>
35
+ <g>
36
+ <circle data-type="point" data-label="U5.7
37
+ x+" data-x="1.5225" data-y="-8.700000000000001" cx="462.4917919474684" cy="417.68792120269575" r="3" fill="hsl(209, 100%, 50%, 0.8)" />
38
+ </g>
39
+ <g>
40
+ <circle data-type="point" data-label="U5.8
41
+ x+" data-x="1.5225" data-y="-8.5" cx="462.4917919474684" cy="398.33419733886296" r="3" fill="hsl(210, 100%, 50%, 0.8)" />
42
+ </g>
43
+ <g>
44
+ <circle data-type="point" data-label="anchorPoint
45
+ orientation: y+" data-x="-2.15" data-y="-6.6" cx="107.10903749784" cy="214.47382063245198" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
46
+ </g>
47
+ <g>
48
+ <circle data-type="point" data-label="anchorPoint
49
+ orientation: x+" data-x="-0.8499999999999999" data-y="-6.6" cx="232.90824261275273" cy="214.47382063245198" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
50
+ </g>
51
+ <g>
52
+ <circle data-type="point" data-label="anchorPoint
53
+ orientation: x-" data-x="-1.5225" data-y="-8.700000000000001" cx="167.83134612061517" cy="417.68792120269575" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
54
+ </g>
55
+ <g>
56
+ <circle data-type="point" data-label="anchorPoint
57
+ orientation: x-" data-x="-1.5225" data-y="-8.1" cx="167.83134612061517" cy="359.6267496111974" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
58
+ </g>
59
+ <g>
60
+ <circle data-type="point" data-label="anchorPoint
61
+ orientation: x-" data-x="-1.5225" data-y="-8.3" cx="167.83134612061517" cy="378.9804734750303" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
62
+ </g>
63
+ <g>
64
+ <circle data-type="point" data-label="anchorPoint
65
+ orientation: x-" data-x="-1.5225" data-y="-8.5" cx="167.83134612061517" cy="398.33419733886296" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
66
+ </g>
67
+ <g>
68
+ <circle data-type="point" data-label="anchorPoint
69
+ orientation: x+" data-x="1.5225" data-y="-8.1" cx="462.4917919474684" cy="359.6267496111974" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
70
+ </g>
71
+ <g>
72
+ <circle data-type="point" data-label="anchorPoint
73
+ orientation: x+" data-x="1.5225" data-y="-8.3" cx="462.4917919474684" cy="378.9804734750303" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
74
+ </g>
75
+ <g>
76
+ <circle data-type="point" data-label="anchorPoint
77
+ orientation: y-" data-x="1.6235" data-y="-8.901" cx="472.26542249870397" cy="437.1384136858476" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
78
+ </g>
79
+ <g>
80
+ <polyline data-points="-1.95,-6.6 1.5225,-8.5" data-type="line" data-label="" points="126.46276136167273,214.47382063245198 462.4917919474684,398.33419733886296" fill="none" stroke="hsl(154, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
81
+ </g>
82
+ <g>
83
+ <polyline data-points="-0.8499999999999999,-6.6 -1.5225,-8.700000000000001" data-type="line" data-label="" points="232.90824261275273,214.47382063245198 167.83134612061517,417.68792120269575" fill="none" stroke="hsl(172, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
84
+ </g>
85
+ <g>
86
+ <polyline data-points="-1.95,-6.6 -2.15,-6.6 -2.15,-7.55 2.9435000000000002,-7.55 2.9435000000000002,-8.5 1.5225000000000002,-8.5" data-type="line" data-label="" points="126.46276136167273,214.47382063245198 107.10903749784,214.47382063245198 107.10903749784,306.40400898565747 600,306.40400898565747 600,398.33419733886296 462.4917919474684,398.33419733886296" fill="none" stroke="purple" stroke-width="1" />
87
+ </g>
88
+ <g>
89
+ <polyline data-points="1.5225,-8.700000000000001 1.6235,-8.700000000000001 1.6235,-8.901" data-type="line" data-label="" points="462.4917919474684,417.68792120269575 472.26542249870397,417.68792120269575 472.26542249870397,437.1384136858476" fill="none" stroke="purple" stroke-width="1" />
90
+ </g>
91
+ <g>
92
+ <rect data-type="rect" data-label="schematic_component_0" data-x="-1.4" data-y="-6.6" x="126.46276136167273" y="176.78676412649043" width="106.44548125108" height="75.37411301192321" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010333928571428572" />
93
+ </g>
94
+ <g>
95
+ <rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="-8.4" x="167.83134612061517" y="340.27302574736484" width="294.6604458268532" height="96.76861931916358" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010333928571428572" />
96
+ </g>
97
+ <g>
98
+ <rect data-type="rect" data-label="netId: V3V3
99
+ globalConnNetId: connectivity_net0" data-x="-2.15" data-y="-6.3" x="97.43217556592361" y="156.41264904095385" width="19.353723863832755" height="58.06117159149812" fill="#ef444466" stroke="#ef4444" stroke-width="0.010333928571428572" />
100
+ </g>
101
+ <g>
102
+ <rect data-type="rect" data-label="netId: QSPI_CS
103
+ globalConnNetId: connectivity_net1" data-x="-0.3689999999999999" data-y="-6.6" x="233.0050112320719" y="204.7969587005357" width="92.8978745463971" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
104
+ </g>
105
+ <g>
106
+ <rect data-type="rect" data-label="netId: QSPI_CS
107
+ globalConnNetId: connectivity_net1" data-x="-2.0035" data-y="-8.700000000000001" x="74.83670295489893" y="408.01105927077947" width="92.8978745463971" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
108
+ </g>
109
+ <g>
110
+ <rect data-type="rect" data-label="netId: QSPI_SCK
111
+ globalConnNetId: connectivity_net2" data-x="-2.0635" data-y="-8.1" x="63.22446863659928" y="349.9498876792811" width="104.51010886469675" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
112
+ </g>
113
+ <g>
114
+ <rect data-type="rect" data-label="netId: QSPI_DATA0
115
+ globalConnNetId: connectivity_net3" data-x="-2.1835" data-y="-8.3" x="40" y="369.3036115431139" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
116
+ </g>
117
+ <g>
118
+ <rect data-type="rect" data-label="netId: QSPI_DATA1
119
+ globalConnNetId: connectivity_net4" data-x="-2.1835" data-y="-8.5" x="40" y="388.6573354069466" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
120
+ </g>
121
+ <g>
122
+ <rect data-type="rect" data-label="netId: QSPI_DATA2
123
+ globalConnNetId: connectivity_net5" data-x="2.1835" data-y="-8.1" x="462.5885605667876" y="349.9498876792811" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
124
+ </g>
125
+ <g>
126
+ <rect data-type="rect" data-label="netId: QSPI_DATA3
127
+ globalConnNetId: connectivity_net6" data-x="2.1835" data-y="-8.3" x="462.5885605667876" y="369.3036115431139" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
128
+ </g>
129
+ <g>
130
+ <rect data-type="rect" data-label="netId: GND
131
+ globalConnNetId: connectivity_net7" data-x="1.6235" data-y="-9.141" x="462.5885605667876" y="437.1384136858476" width="19.353723863832784" height="46.44893727319857" fill="#00000066" stroke="#000000" stroke-width="0.010333928571428572" />
132
+ </g>
133
+ <g id="crosshair" style="display: none">
134
+ <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
135
+ <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>
136
+ </g>
137
+ <script>
138
+ <![CDATA[
139
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
140
+ const svg = e.currentTarget;
141
+ const rect = svg.getBoundingClientRect();
142
+ const x = e.clientX - rect.left;
143
+ const y = e.clientY - rect.top;
144
+ const crosshair = svg.getElementById('crosshair');
145
+ const h = svg.getElementById('crosshair-h');
146
+ const v = svg.getElementById('crosshair-v');
147
+ const coords = svg.getElementById('coordinates');
148
+
149
+ crosshair.style.display = 'block';
150
+ h.setAttribute('x1', '0');
151
+ h.setAttribute('x2', '640');
152
+ h.setAttribute('y1', y);
153
+ h.setAttribute('y2', y);
154
+ v.setAttribute('x1', x);
155
+ v.setAttribute('x2', x);
156
+ v.setAttribute('y1', '0');
157
+ v.setAttribute('y2', '640');
158
+
159
+ // Calculate real coordinates using inverse transformation
160
+ const matrix = {
161
+ "a": 96.76861931916363,
162
+ "c": 0,
163
+ "e": 315.1615690340418,
164
+ "b": 0,
165
+ "d": -96.76861931916363,
166
+ "f": -424.19906687402795
167
+ };
168
+ // Manually invert and apply the affine transform
169
+ // Since we only use translate and scale, we can directly compute:
170
+ // x' = (x - tx) / sx
171
+ // y' = (y - ty) / sy
172
+ const sx = matrix.a;
173
+ const sy = matrix.d;
174
+ const tx = matrix.e;
175
+ const ty = matrix.f;
176
+ const realPoint = {
177
+ x: (x - tx) / sx,
178
+ y: (y - ty) / sy // Flip y back since we used negative scale
179
+ }
180
+
181
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
182
+ coords.setAttribute('x', (x + 5).toString());
183
+ coords.setAttribute('y', (y - 5).toString());
184
+ });
185
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
186
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
187
+ });
188
+ ]]>
189
+ </script>
190
+ </svg>
@@ -0,0 +1,12 @@
1
+ import { test, expect } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "../assets/example50.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ test("example49", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
12
+ })