@tscircuit/schematic-trace-solver 0.0.35 → 0.0.36

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 CHANGED
@@ -79,6 +79,7 @@ interface InputDirectConnection {
79
79
  interface InputNetConnection {
80
80
  netId: string;
81
81
  pinIds: Array<PinId>;
82
+ netLabelWidth?: number;
82
83
  }
83
84
  interface InputProblem {
84
85
  chips: Array<InputChip>;
@@ -279,6 +280,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
279
280
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup;
280
281
  availableOrientations: Array<FacingDirection>;
281
282
  chipObstacleSpatialIndex: ChipObstacleSpatialIndex;
283
+ netLabelWidth?: number;
282
284
  netLabelPlacement: NetLabelPlacement | null;
283
285
  testedCandidates: Array<{
284
286
  center: {
@@ -306,6 +308,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
306
308
  inputTraceMap: Record<MspConnectionPairId, SolvedTracePath>;
307
309
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup;
308
310
  availableOrientations: FacingDirection[];
311
+ netLabelWidth?: number;
309
312
  });
310
313
  getConstructorParams(): ConstructorParameters<typeof SingleNetLabelPlacementSolver>[0];
311
314
  _step(): void;
package/dist/index.js CHANGED
@@ -1371,15 +1371,18 @@ var ChipObstacleSpatialIndex = class {
1371
1371
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
1372
1372
  var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
1373
1373
  var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
1374
- function getDimsForOrientation(orientation) {
1374
+ function getDimsForOrientation(params) {
1375
+ const { orientation, netLabelWidth } = params;
1376
+ const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
1375
1377
  if (orientation === "y+" || orientation === "y-") {
1376
1378
  return {
1379
+ // Rotated, so width/height swap
1377
1380
  width: NET_LABEL_HORIZONTAL_HEIGHT,
1378
- height: NET_LABEL_HORIZONTAL_WIDTH
1381
+ height: horizWidth
1379
1382
  };
1380
1383
  }
1381
1384
  return {
1382
- width: NET_LABEL_HORIZONTAL_WIDTH,
1385
+ width: horizWidth,
1383
1386
  height: NET_LABEL_HORIZONTAL_HEIGHT
1384
1387
  };
1385
1388
  }
@@ -1508,7 +1511,8 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1508
1511
  inputTraceMap,
1509
1512
  chipObstacleSpatialIndex,
1510
1513
  overlappingSameNetTraceGroup,
1511
- availableOrientations
1514
+ availableOrientations,
1515
+ netLabelWidth
1512
1516
  } = params;
1513
1517
  const pinId = overlappingSameNetTraceGroup.portOnlyPinId;
1514
1518
  if (!pinId) {
@@ -1540,7 +1544,10 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1540
1544
  const outwardOf = (o) => o === "x+" ? { x: 1, y: 0 } : o === "x-" ? { x: -1, y: 0 } : o === "y+" ? { x: 0, y: 1 } : { x: 0, y: -1 };
1541
1545
  const testedCandidates = [];
1542
1546
  for (const orientation of orientations) {
1543
- const { width: width2, height: height2 } = getDimsForOrientation(orientation);
1547
+ const { width: width2, height: height2 } = getDimsForOrientation({
1548
+ orientation,
1549
+ netLabelWidth
1550
+ });
1544
1551
  const baseCenter2 = getCenterFromAnchor(anchor, orientation, width2, height2);
1545
1552
  const outward2 = outwardOf(orientation);
1546
1553
  const offset2 = 1e-3;
@@ -1607,7 +1614,10 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1607
1614
  return { placement, testedCandidates };
1608
1615
  }
1609
1616
  const fallbackOrientation = pinFacingDirection;
1610
- const { width, height } = getDimsForOrientation(fallbackOrientation);
1617
+ const { width, height } = getDimsForOrientation({
1618
+ orientation: fallbackOrientation,
1619
+ netLabelWidth
1620
+ });
1611
1621
  const baseCenter = getCenterFromAnchor(
1612
1622
  anchor,
1613
1623
  fallbackOrientation,
@@ -1699,6 +1709,8 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1699
1709
  overlappingSameNetTraceGroup;
1700
1710
  availableOrientations;
1701
1711
  chipObstacleSpatialIndex;
1712
+ // Optional override for the width of the net label (per netId)
1713
+ netLabelWidth;
1702
1714
  netLabelPlacement = null;
1703
1715
  testedCandidates = [];
1704
1716
  constructor(params) {
@@ -1707,6 +1719,7 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1707
1719
  this.inputTraceMap = params.inputTraceMap;
1708
1720
  this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup;
1709
1721
  this.availableOrientations = params.availableOrientations;
1722
+ this.netLabelWidth = params.netLabelWidth;
1710
1723
  this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
1711
1724
  }
1712
1725
  getConstructorParams() {
@@ -1714,7 +1727,8 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1714
1727
  inputProblem: this.inputProblem,
1715
1728
  inputTraceMap: this.inputTraceMap,
1716
1729
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
1717
- availableOrientations: this.availableOrientations
1730
+ availableOrientations: this.availableOrientations,
1731
+ netLabelWidth: this.netLabelWidth
1718
1732
  };
1719
1733
  }
1720
1734
  _step() {
@@ -1728,7 +1742,8 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1728
1742
  inputTraceMap: this.inputTraceMap,
1729
1743
  chipObstacleSpatialIndex: this.chipObstacleSpatialIndex,
1730
1744
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
1731
- availableOrientations: this.availableOrientations
1745
+ availableOrientations: this.availableOrientations,
1746
+ netLabelWidth: this.netLabelWidth
1732
1747
  });
1733
1748
  this.testedCandidates.push(...res.testedCandidates);
1734
1749
  if (res.placement) {
@@ -1796,7 +1811,10 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1796
1811
  const anchors = anchorsForSegment(a, b);
1797
1812
  for (const anchor of anchors) {
1798
1813
  for (const orientation of candidateOrients) {
1799
- const { width, height } = getDimsForOrientation(orientation);
1814
+ const { width, height } = getDimsForOrientation({
1815
+ orientation,
1816
+ netLabelWidth: this.netLabelWidth
1817
+ });
1800
1818
  const center = getCenterFromAnchor(
1801
1819
  anchor,
1802
1820
  orientation,
@@ -2075,11 +2093,15 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2075
2093
  const isAlreadyFull = currOrients.length === 4 && fullOrients.every((o) => currOrients.includes(o));
2076
2094
  if (!this.triedAnyOrientationFallbackForCurrentGroup && !isAlreadyFull && this.currentGroup) {
2077
2095
  this.triedAnyOrientationFallbackForCurrentGroup = true;
2096
+ const netLabelWidth2 = this.currentGroup.netId ? this.inputProblem.netConnections.find(
2097
+ (nc) => nc.netId === this.currentGroup.netId
2098
+ )?.netLabelWidth : void 0;
2078
2099
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
2079
2100
  inputProblem: this.inputProblem,
2080
2101
  inputTraceMap: this.inputTraceMap,
2081
2102
  overlappingSameNetTraceGroup: this.currentGroup,
2082
- availableOrientations: fullOrients
2103
+ availableOrientations: fullOrients,
2104
+ netLabelWidth: netLabelWidth2
2083
2105
  });
2084
2106
  return;
2085
2107
  }
@@ -2099,11 +2121,15 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2099
2121
  const netId = nextOverlappingSameNetTraceGroup.netId ?? nextOverlappingSameNetTraceGroup.globalConnNetId;
2100
2122
  this.currentGroup = nextOverlappingSameNetTraceGroup;
2101
2123
  this.triedAnyOrientationFallbackForCurrentGroup = false;
2124
+ const netLabelWidth = this.currentGroup.netId ? this.inputProblem.netConnections.find(
2125
+ (nc) => nc.netId === this.currentGroup.netId
2126
+ )?.netLabelWidth : void 0;
2102
2127
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
2103
2128
  inputProblem: this.inputProblem,
2104
2129
  inputTraceMap: this.inputTraceMap,
2105
2130
  overlappingSameNetTraceGroup: nextOverlappingSameNetTraceGroup,
2106
- availableOrientations: this.inputProblem.availableNetLabelOrientations[netId] ?? ["x+", "x-", "y+", "y-"]
2131
+ availableOrientations: this.inputProblem.availableNetLabelOrientations[netId] ?? ["x+", "x-", "y+", "y-"],
2132
+ netLabelWidth
2107
2133
  });
2108
2134
  }
2109
2135
  visualize() {
@@ -273,11 +273,17 @@ export class NetLabelPlacementSolver extends BaseSolver {
273
273
  this.currentGroup
274
274
  ) {
275
275
  this.triedAnyOrientationFallbackForCurrentGroup = true
276
+ const netLabelWidth = this.currentGroup.netId
277
+ ? this.inputProblem.netConnections.find(
278
+ (nc) => nc.netId === this.currentGroup!.netId,
279
+ )?.netLabelWidth
280
+ : undefined
276
281
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
277
282
  inputProblem: this.inputProblem,
278
283
  inputTraceMap: this.inputTraceMap,
279
284
  overlappingSameNetTraceGroup: this.currentGroup,
280
285
  availableOrientations: fullOrients,
286
+ netLabelWidth,
281
287
  })
282
288
  return
283
289
  }
@@ -307,6 +313,12 @@ export class NetLabelPlacementSolver extends BaseSolver {
307
313
  this.currentGroup = nextOverlappingSameNetTraceGroup
308
314
  this.triedAnyOrientationFallbackForCurrentGroup = false
309
315
 
316
+ const netLabelWidth = this.currentGroup.netId
317
+ ? this.inputProblem.netConnections.find(
318
+ (nc) => nc.netId === this.currentGroup!.netId,
319
+ )?.netLabelWidth
320
+ : undefined
321
+
310
322
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
311
323
  inputProblem: this.inputProblem,
312
324
  inputTraceMap: this.inputTraceMap,
@@ -314,6 +326,7 @@ export class NetLabelPlacementSolver extends BaseSolver {
314
326
  availableOrientations: this.inputProblem.availableNetLabelOrientations[
315
327
  netId
316
328
  ] ?? ["x+", "x-", "y+", "y-"],
329
+ netLabelWidth,
317
330
  })
318
331
  }
319
332
 
@@ -58,6 +58,9 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
58
58
 
59
59
  chipObstacleSpatialIndex: ChipObstacleSpatialIndex
60
60
 
61
+ // Optional override for the width of the net label (per netId)
62
+ netLabelWidth?: number
63
+
61
64
  netLabelPlacement: NetLabelPlacement | null = null
62
65
  testedCandidates: Array<{
63
66
  center: { x: number; y: number }
@@ -75,12 +78,14 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
75
78
  inputTraceMap: Record<MspConnectionPairId, SolvedTracePath>
76
79
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup
77
80
  availableOrientations: FacingDirection[]
81
+ netLabelWidth?: number
78
82
  }) {
79
83
  super()
80
84
  this.inputProblem = params.inputProblem
81
85
  this.inputTraceMap = params.inputTraceMap
82
86
  this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup
83
87
  this.availableOrientations = params.availableOrientations
88
+ this.netLabelWidth = params.netLabelWidth
84
89
 
85
90
  this.chipObstacleSpatialIndex =
86
91
  params.inputProblem._chipObstacleSpatialIndex ??
@@ -95,6 +100,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
95
100
  inputTraceMap: this.inputTraceMap,
96
101
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
97
102
  availableOrientations: this.availableOrientations,
103
+ netLabelWidth: this.netLabelWidth,
98
104
  }
99
105
  }
100
106
 
@@ -112,6 +118,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
112
118
  chipObstacleSpatialIndex: this.chipObstacleSpatialIndex,
113
119
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
114
120
  availableOrientations: this.availableOrientations,
121
+ netLabelWidth: this.netLabelWidth,
115
122
  })
116
123
  this.testedCandidates.push(...res.testedCandidates)
117
124
  if (res.placement) {
@@ -220,7 +227,10 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
220
227
  const anchors = anchorsForSegment(a, b)
221
228
  for (const anchor of anchors) {
222
229
  for (const orientation of candidateOrients) {
223
- const { width, height } = getDimsForOrientation(orientation)
230
+ const { width, height } = getDimsForOrientation({
231
+ orientation,
232
+ netLabelWidth: this.netLabelWidth,
233
+ })
224
234
  const center = getCenterFromAnchor(
225
235
  anchor,
226
236
  orientation,
@@ -3,15 +3,25 @@ import type { FacingDirection } from "lib/utils/dir"
3
3
  export const NET_LABEL_HORIZONTAL_WIDTH = 0.45
4
4
  export const NET_LABEL_HORIZONTAL_HEIGHT = 0.2
5
5
 
6
- export function getDimsForOrientation(orientation: FacingDirection) {
6
+ export function getDimsForOrientation(params: {
7
+ orientation: FacingDirection
8
+ netLabelWidth?: number
9
+ }) {
10
+ const { orientation, netLabelWidth } = params
11
+ const horizWidth =
12
+ typeof netLabelWidth === "number"
13
+ ? netLabelWidth
14
+ : NET_LABEL_HORIZONTAL_WIDTH
15
+
7
16
  if (orientation === "y+" || orientation === "y-") {
8
17
  return {
18
+ // Rotated, so width/height swap
9
19
  width: NET_LABEL_HORIZONTAL_HEIGHT,
10
- height: NET_LABEL_HORIZONTAL_WIDTH,
20
+ height: horizWidth,
11
21
  }
12
22
  }
13
23
  return {
14
- width: NET_LABEL_HORIZONTAL_WIDTH,
24
+ width: horizWidth,
15
25
  height: NET_LABEL_HORIZONTAL_HEIGHT,
16
26
  }
17
27
  }
@@ -21,6 +21,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
21
21
  chipObstacleSpatialIndex: ChipObstacleSpatialIndex
22
22
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup
23
23
  availableOrientations: FacingDirection[]
24
+ netLabelWidth?: number
24
25
  }): {
25
26
  placement: NetLabelPlacement | null
26
27
  testedCandidates: Array<{
@@ -41,6 +42,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
41
42
  chipObstacleSpatialIndex,
42
43
  overlappingSameNetTraceGroup,
43
44
  availableOrientations,
45
+ netLabelWidth,
44
46
  } = params
45
47
 
46
48
  const pinId = overlappingSameNetTraceGroup.portOnlyPinId
@@ -98,7 +100,10 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
98
100
  }> = []
99
101
 
100
102
  for (const orientation of orientations) {
101
- const { width, height } = getDimsForOrientation(orientation)
103
+ const { width, height } = getDimsForOrientation({
104
+ orientation,
105
+ netLabelWidth,
106
+ })
102
107
  // Place label fully outside the chip: shift center slightly outward
103
108
  const baseCenter = getCenterFromAnchor(anchor, orientation, width, height)
104
109
  const outward = outwardOf(orientation)
@@ -176,7 +181,10 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
176
181
 
177
182
  // If no valid placements found, return placement using pin's facing direction
178
183
  const fallbackOrientation = pinFacingDirection
179
- const { width, height } = getDimsForOrientation(fallbackOrientation)
184
+ const { width, height } = getDimsForOrientation({
185
+ orientation: fallbackOrientation,
186
+ netLabelWidth,
187
+ })
180
188
  const baseCenter = getCenterFromAnchor(
181
189
  anchor,
182
190
  fallbackOrientation,
@@ -27,6 +27,7 @@ export interface InputDirectConnection {
27
27
  export interface InputNetConnection {
28
28
  netId: string
29
29
  pinIds: Array<PinId>
30
+ netLabelWidth?: number
30
31
  }
31
32
 
32
33
  export interface InputProblem {
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.35",
4
+ "version": "0.0.36",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -0,0 +1,235 @@
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="U3.8
5
+ x-" data-x="-1.4" data-y="0.42500000000000004" cx="220.08485425212754" cy="293.25559941049465" r="3" fill="hsl(88, 100%, 50%, 0.8)" />
6
+ </g>
7
+ <g>
8
+ <circle data-type="point" data-label="U3.4
9
+ x-" data-x="-1.4" data-y="-0.42500000000000004" cx="220.08485425212754" cy="363.20249325997025" r="3" fill="hsl(84, 100%, 50%, 0.8)" />
10
+ </g>
11
+ <g>
12
+ <circle data-type="point" data-label="U3.1
13
+ x+" data-x="1.4" data-y="0.5" cx="450.49815163863525" cy="287.08381465907036" r="3" fill="hsl(81, 100%, 50%, 0.8)" />
14
+ </g>
15
+ <g>
16
+ <circle data-type="point" data-label="U3.6
17
+ x+" data-x="1.4" data-y="0.30000000000000004" cx="450.49815163863525" cy="303.5419073295352" r="3" fill="hsl(86, 100%, 50%, 0.8)" />
18
+ </g>
19
+ <g>
20
+ <circle data-type="point" data-label="U3.5
21
+ x+" data-x="1.4" data-y="0.10000000000000009" cx="450.49815163863525" cy="320" r="3" fill="hsl(85, 100%, 50%, 0.8)" />
22
+ </g>
23
+ <g>
24
+ <circle data-type="point" data-label="U3.2
25
+ x+" data-x="1.4" data-y="-0.09999999999999998" cx="450.49815163863525" cy="336.45809267046485" r="3" fill="hsl(82, 100%, 50%, 0.8)" />
26
+ </g>
27
+ <g>
28
+ <circle data-type="point" data-label="U3.3
29
+ x+" data-x="1.4" data-y="-0.3" cx="450.49815163863525" cy="352.9161853409297" r="3" fill="hsl(83, 100%, 50%, 0.8)" />
30
+ </g>
31
+ <g>
32
+ <circle data-type="point" data-label="U3.7
33
+ x+" data-x="1.4" data-y="-0.5" cx="450.49815163863525" cy="369.37427801139455" r="3" fill="hsl(87, 100%, 50%, 0.8)" />
34
+ </g>
35
+ <g>
36
+ <circle data-type="point" data-label="C20.1
37
+ y+" data-x="-2.3148566499999994" data-y="0.5512093000000002" cx="144.8008766226725" cy="282.86977763412216" r="3" fill="hsl(140, 100%, 50%, 0.8)" />
38
+ </g>
39
+ <g>
40
+ <circle data-type="point" data-label="C20.2
41
+ y-" data-x="-2.31430995" data-y="-0.5512093000000002" cx="144.84586481898717" cy="373.58831503634275" r="3" fill="hsl(141, 100%, 50%, 0.8)" />
42
+ </g>
43
+ <g>
44
+ <circle data-type="point" data-label="R11.1
45
+ y+" data-x="1.7580660749999977" data-y="2.3025814000000002" cx="479.96357486113317" cy="138.7485560227891" r="3" fill="hsl(125, 100%, 50%, 0.8)" />
46
+ </g>
47
+ <g>
48
+ <circle data-type="point" data-label="R11.2
49
+ y-" data-x="1.757519574999999" data-y="1.2" cx="479.91860312291124" cy="229.4804903124434" r="3" fill="hsl(126, 100%, 50%, 0.8)" />
50
+ </g>
51
+ <g>
52
+ <circle data-type="point" data-label="R12.1
53
+ y-" data-x="-1.7580660749999977" data-y="-3.3025814000000002" cx="190.61943102962965" cy="600" r="3" fill="hsl(6, 100%, 50%, 0.8)" />
54
+ </g>
55
+ <g>
56
+ <circle data-type="point" data-label="R12.2
57
+ y+" data-x="-1.757519574999999" data-y="-2.2" cx="190.66440276785158" cy="509.2680657103457" r="3" fill="hsl(7, 100%, 50%, 0.8)" />
58
+ </g>
59
+ <g>
60
+ <circle data-type="point" data-label="R13.1
61
+ y-" data-x="1.7580660749999977" data-y="-3.3025814000000002" cx="479.96357486113317" cy="600" r="3" fill="hsl(247, 100%, 50%, 0.8)" />
62
+ </g>
63
+ <g>
64
+ <circle data-type="point" data-label="R13.2
65
+ y+" data-x="1.757519574999999" data-y="-2.2" cx="479.91860312291124" cy="509.2680657103457" r="3" fill="hsl(248, 100%, 50%, 0.8)" />
66
+ </g>
67
+ <g>
68
+ <circle data-type="point" data-label="" data-x="-1.8574283249999997" data-y="0.7512093000000004" cx="182.44286543740003" cy="266.4116849636573" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
69
+ </g>
70
+ <g>
71
+ <circle data-type="point" data-label="" data-x="1.5790330374999988" data-y="2.5025814000000004" cx="465.23086324988424" cy="122.29046335232425" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
72
+ </g>
73
+ <g>
74
+ <circle data-type="point" data-label="" data-x="-2.31430995" data-y="-0.7512093000000004" cx="144.84586481898717" cy="390.0464077068076" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
75
+ </g>
76
+ <g>
77
+ <circle data-type="point" data-label="" data-x="1.757519574999999" data-y="0.85" cx="479.91860312291124" cy="258.2821524857569" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
78
+ </g>
79
+ <g>
80
+ <circle data-type="point" data-label="" data-x="1.757519574999999" data-y="-2" cx="479.91860312291124" cy="492.80997303988084" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
81
+ </g>
82
+ <g>
83
+ <polyline data-points="-2.3148566499999994,0.5512093000000002 -1.4,0.42500000000000004" data-type="line" data-label="" points="144.8008766226725,282.86977763412216 220.08485425212754,293.25559941049465" fill="none" stroke="hsl(256, 100%, 50%, 0.8)" stroke-width="1" />
84
+ </g>
85
+ <g>
86
+ <polyline data-points="-2.31430995,-0.5512093000000002 -1.4,-0.42500000000000004" data-type="line" data-label="" points="144.84586481898717,373.58831503634275 220.08485425212754,363.20249325997025" fill="none" stroke="hsl(256, 100%, 50%, 0.8)" stroke-width="1" />
87
+ </g>
88
+ <g>
89
+ <polyline data-points="1.757519574999999,1.2 1.4,0.5" data-type="line" data-label="" points="479.91860312291124,229.4804903124434 450.49815163863525,287.08381465907036" fill="none" stroke="hsl(144, 100%, 50%, 0.8)" stroke-width="1" />
90
+ </g>
91
+ <g>
92
+ <polyline data-points="1.757519574999999,-2.2 -1.757519574999999,-2.2" data-type="line" data-label="" points="479.91860312291124,509.2680657103457 190.66440276785158,509.2680657103457" fill="none" stroke="hsl(144, 100%, 50%, 0.8)" stroke-width="1" />
93
+ </g>
94
+ <g>
95
+ <polyline data-points="-1.4,0.42500000000000004 1.4,-0.3" data-type="line" data-label="" points="220.08485425212754,293.25559941049465 450.49815163863525,352.9161853409297" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
96
+ </g>
97
+ <g>
98
+ <polyline data-points="-1.4,0.42500000000000004 1.4,-0.5" data-type="line" data-label="" points="220.08485425212754,293.25559941049465 450.49815163863525,369.37427801139455" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
99
+ </g>
100
+ <g>
101
+ <polyline data-points="-1.4,0.42500000000000004 -2.3148566499999994,0.5512093000000002" data-type="line" data-label="" points="220.08485425212754,293.25559941049465 144.8008766226725,282.86977763412216" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
102
+ </g>
103
+ <g>
104
+ <polyline data-points="-1.4,0.42500000000000004 1.7580660749999977,2.3025814000000002" data-type="line" data-label="" points="220.08485425212754,293.25559941049465 479.96357486113317,138.7485560227891" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
105
+ </g>
106
+ <g>
107
+ <polyline data-points="1.4,-0.3 1.4,-0.5" data-type="line" data-label="" points="450.49815163863525,352.9161853409297 450.49815163863525,369.37427801139455" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
108
+ </g>
109
+ <g>
110
+ <polyline data-points="1.4,-0.3 -2.3148566499999994,0.5512093000000002" data-type="line" data-label="" points="450.49815163863525,352.9161853409297 144.8008766226725,282.86977763412216" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
111
+ </g>
112
+ <g>
113
+ <polyline data-points="1.4,-0.3 1.7580660749999977,2.3025814000000002" data-type="line" data-label="" points="450.49815163863525,352.9161853409297 479.96357486113317,138.7485560227891" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
114
+ </g>
115
+ <g>
116
+ <polyline data-points="1.4,-0.5 -2.3148566499999994,0.5512093000000002" data-type="line" data-label="" points="450.49815163863525,369.37427801139455 144.8008766226725,282.86977763412216" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
117
+ </g>
118
+ <g>
119
+ <polyline data-points="1.4,-0.5 1.7580660749999977,2.3025814000000002" data-type="line" data-label="" points="450.49815163863525,369.37427801139455 479.96357486113317,138.7485560227891" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
120
+ </g>
121
+ <g>
122
+ <polyline data-points="-2.3148566499999994,0.5512093000000002 1.7580660749999977,2.3025814000000002" data-type="line" data-label="" points="144.8008766226725,282.86977763412216 479.96357486113317,138.7485560227891" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
123
+ </g>
124
+ <g>
125
+ <polyline data-points="-1.4,-0.42500000000000004 -2.31430995,-0.5512093000000002" data-type="line" data-label="" points="220.08485425212754,363.20249325997025 144.84586481898717,373.58831503634275" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
126
+ </g>
127
+ <g>
128
+ <polyline data-points="1.4,0.5 1.757519574999999,1.2" data-type="line" data-label="" points="450.49815163863525,287.08381465907036 479.91860312291124,229.4804903124434" fill="none" stroke="hsl(304, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
129
+ </g>
130
+ <g>
131
+ <polyline data-points="-1.4,0.42500000000000004 -1.8574283249999997,0.42500000000000004 -1.8574283249999997,0.7512093000000004 -2.3148566499999994,0.7512093000000004 -2.3148566499999994,0.5512093000000002" data-type="line" data-label="" points="220.08485425212754,293.25559941049465 182.44286543740003,293.25559941049465 182.44286543740003,266.4116849636573 144.8008766226725,266.4116849636573 144.8008766226725,282.86977763412216" fill="none" stroke="purple" stroke-width="1" />
132
+ </g>
133
+ <g>
134
+ <polyline data-points="1.4,-0.3 1.5790330374999988,-0.3 1.5790330374999988,2.5025814000000004 1.7580660749999977,2.5025814000000004 1.7580660749999977,2.3025814000000002" data-type="line" data-label="" points="450.49815163863525,352.9161853409297 465.23086324988424,352.9161853409297 465.23086324988424,122.29046335232425 479.96357486113317,122.29046335232425 479.96357486113317,138.7485560227891" fill="none" stroke="purple" stroke-width="1" />
135
+ </g>
136
+ <g>
137
+ <polyline data-points="1.4,-0.5 1.5999999999999999,-0.5 1.5999999999999999,-0.3 1.4,-0.3" data-type="line" data-label="" points="450.49815163863525,369.37427801139455 466.9562443091001,369.37427801139455 466.9562443091001,352.9161853409297 450.49815163863525,352.9161853409297" fill="none" stroke="purple" stroke-width="1" />
138
+ </g>
139
+ <g>
140
+ <polyline data-points="-2.31430995,-0.5512093000000002 -2.31430995,-0.7512093000000004 -1.857154975,-0.7512093000000004 -1.857154975,-0.42500000000000004 -1.4,-0.42500000000000004" data-type="line" data-label="" points="144.84586481898717,373.58831503634275 144.84586481898717,390.0464077068076 182.46535953555735,390.0464077068076 182.46535953555735,363.20249325997025 220.08485425212754,363.20249325997025" fill="none" stroke="purple" stroke-width="1" />
141
+ </g>
142
+ <g>
143
+ <polyline data-points="1.757519574999999,1.2 1.757519574999999,0.5 1.4,0.5" data-type="line" data-label="" points="479.91860312291124,229.4804903124434 479.91860312291124,287.08381465907036 450.49815163863525,287.08381465907036" fill="none" stroke="purple" stroke-width="1" />
144
+ </g>
145
+ <g>
146
+ <polyline data-points="1.757519574999999,-2.2 1.757519574999999,-2 -1.757519574999999,-2 -1.757519574999999,-2.2" data-type="line" data-label="" points="479.91860312291124,509.2680657103457 479.91860312291124,492.80997303988084 190.66440276785158,492.80997303988084 190.66440276785158,509.2680657103457" fill="none" stroke="purple" stroke-width="1" />
147
+ </g>
148
+ <g>
149
+ <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="220.08485425212754" y="270.6257219886055" width="230.4132973865077" height="115.2066486932539" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.012152076428571431" />
150
+ </g>
151
+ <g>
152
+ <rect data-type="rect" data-label="schematic_component_1" data-x="-2.3145833" data-y="0" x="123.05068836854284" y="282.86977763412216" width="43.545364704573984" height="90.71853740222059" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.012152076428571431" />
153
+ </g>
154
+ <g>
155
+ <rect data-type="rect" data-label="schematic_component_2" data-x="1.7577928249999983" data-y="1.7512907000000002" x="466.95624430910016" y="138.7485560227891" width="25.96968936584409" height="90.7319342896543" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.012152076428571431" />
156
+ </g>
157
+ <g>
158
+ <rect data-type="rect" data-label="schematic_component_3" data-x="-1.7577928249999983" data-y="-2.7512907" x="177.65707221581857" y="509.2680657103457" width="25.96968936584412" height="90.7319342896543" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.012152076428571431" />
159
+ </g>
160
+ <g>
161
+ <rect data-type="rect" data-label="schematic_component_4" data-x="1.7577928249999983" data-y="-2.7512907" x="466.95624430910016" y="509.2680657103457" width="25.96968936584409" height="90.7319342896543" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.012152076428571431" />
162
+ </g>
163
+ <g>
164
+ <rect data-type="rect" data-label="" data-x="-1.8574283249999997" data-y="1.2512093000000004" x="174.2138191021676" y="184.1212216113331" width="16.45809267046485" height="82.2904633523242" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.012152076428571431" />
165
+ </g>
166
+ <g>
167
+ <rect data-type="rect" data-label="" data-x="1.5790330374999988" data-y="3.0025814000000004" x="457.0018169146518" y="40.00000000000006" width="16.45809267046485" height="82.2904633523242" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.012152076428571431" />
168
+ </g>
169
+ <g>
170
+ <rect data-type="rect" data-label="" data-x="-2.31430995" data-y="-1.1512093000000005" x="136.61681848375474" y="390.0464077068076" width="16.45809267046485" height="65.83237068185935" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.012152076428571431" />
171
+ </g>
172
+ <g>
173
+ <rect data-type="rect" data-label="" data-x="1.807519574999999" data-y="0.85" x="479.91860312291124" y="250.05310615052446" width="8.229046335232397" height="16.45809267046485" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.012152076428571431" />
174
+ </g>
175
+ <g>
176
+ <rect data-type="rect" data-label="" data-x="1.982519574999999" data-y="-2" x="479.91860312291124" y="484.5809267046484" width="37.0307085085459" height="16.458092670464907" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.012152076428571431" />
177
+ </g>
178
+ <g id="crosshair" style="display: none">
179
+ <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
180
+ <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>
181
+ </g>
182
+ <script>
183
+ <![CDATA[
184
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
185
+ const svg = e.currentTarget;
186
+ const rect = svg.getBoundingClientRect();
187
+ const x = e.clientX - rect.left;
188
+ const y = e.clientY - rect.top;
189
+ const crosshair = svg.getElementById('crosshair');
190
+ const h = svg.getElementById('crosshair-h');
191
+ const v = svg.getElementById('crosshair-v');
192
+ const coords = svg.getElementById('coordinates');
193
+
194
+ crosshair.style.display = 'block';
195
+ h.setAttribute('x1', '0');
196
+ h.setAttribute('x2', '640');
197
+ h.setAttribute('y1', y);
198
+ h.setAttribute('y2', y);
199
+ v.setAttribute('x1', x);
200
+ v.setAttribute('x2', x);
201
+ v.setAttribute('y1', '0');
202
+ v.setAttribute('y2', '640');
203
+
204
+ // Calculate real coordinates using inverse transformation
205
+ const matrix = {
206
+ "a": 82.2904633523242,
207
+ "c": 0,
208
+ "e": 335.2915029453814,
209
+ "b": 0,
210
+ "d": -82.2904633523242,
211
+ "f": 328.22904633523245
212
+ };
213
+ // Manually invert and apply the affine transform
214
+ // Since we only use translate and scale, we can directly compute:
215
+ // x' = (x - tx) / sx
216
+ // y' = (y - ty) / sy
217
+ const sx = matrix.a;
218
+ const sy = matrix.d;
219
+ const tx = matrix.e;
220
+ const ty = matrix.f;
221
+ const realPoint = {
222
+ x: (x - tx) / sx,
223
+ y: (y - ty) / sy // Flip y back since we used negative scale
224
+ }
225
+
226
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
227
+ coords.setAttribute('x', (x + 5).toString());
228
+ coords.setAttribute('y', (y - 5).toString());
229
+ });
230
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
231
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
232
+ });
233
+ ]]>
234
+ </script>
235
+ </svg>
@@ -0,0 +1,190 @@
1
+ import { expect } from "bun:test"
2
+ import { test } from "bun:test"
3
+ import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"
4
+ import "tests/fixtures/matcher"
5
+
6
+ const inputProblem = {
7
+ chips: [
8
+ {
9
+ chipId: "schematic_component_0",
10
+ center: {
11
+ x: 0,
12
+ y: 0,
13
+ },
14
+ width: 2,
15
+ height: 1.4,
16
+ pins: [
17
+ {
18
+ pinId: "U3.8",
19
+ x: -1.4,
20
+ y: 0.42500000000000004,
21
+ },
22
+ {
23
+ pinId: "U3.4",
24
+ x: -1.4,
25
+ y: -0.42500000000000004,
26
+ },
27
+ {
28
+ pinId: "U3.1",
29
+ x: 1.4,
30
+ y: 0.5,
31
+ },
32
+ {
33
+ pinId: "U3.6",
34
+ x: 1.4,
35
+ y: 0.30000000000000004,
36
+ },
37
+ {
38
+ pinId: "U3.5",
39
+ x: 1.4,
40
+ y: 0.10000000000000009,
41
+ },
42
+ {
43
+ pinId: "U3.2",
44
+ x: 1.4,
45
+ y: -0.09999999999999998,
46
+ },
47
+ {
48
+ pinId: "U3.3",
49
+ x: 1.4,
50
+ y: -0.3,
51
+ },
52
+ {
53
+ pinId: "U3.7",
54
+ x: 1.4,
55
+ y: -0.5,
56
+ },
57
+ ],
58
+ },
59
+ {
60
+ chipId: "schematic_component_1",
61
+ center: {
62
+ x: -2.3145833,
63
+ y: 0,
64
+ },
65
+ width: 0.5291665999999999,
66
+ height: 1.0583333000000001,
67
+ pins: [
68
+ {
69
+ pinId: "C20.1",
70
+ x: -2.3148566499999994,
71
+ y: 0.5512093000000002,
72
+ },
73
+ {
74
+ pinId: "C20.2",
75
+ x: -2.31430995,
76
+ y: -0.5512093000000002,
77
+ },
78
+ ],
79
+ },
80
+ {
81
+ chipId: "schematic_component_2",
82
+ center: {
83
+ x: 1.7577928249999983,
84
+ y: 1.7512907000000002,
85
+ },
86
+ width: 0.3155856499999966,
87
+ height: 1.0583332999999997,
88
+ pins: [
89
+ {
90
+ pinId: "R11.1",
91
+ x: 1.7580660749999977,
92
+ y: 2.3025814000000002,
93
+ },
94
+ {
95
+ pinId: "R11.2",
96
+ x: 1.757519574999999,
97
+ y: 1.2,
98
+ },
99
+ ],
100
+ },
101
+ {
102
+ chipId: "schematic_component_3",
103
+ center: {
104
+ x: -1.7577928249999983,
105
+ y: -2.7512907000000002,
106
+ },
107
+ width: 0.3155856499999966,
108
+ height: 1.0583332999999997,
109
+ pins: [
110
+ {
111
+ pinId: "R12.1",
112
+ x: -1.7580660749999977,
113
+ y: -3.3025814000000002,
114
+ },
115
+ {
116
+ pinId: "R12.2",
117
+ x: -1.757519574999999,
118
+ y: -2.2,
119
+ },
120
+ ],
121
+ },
122
+ {
123
+ chipId: "schematic_component_4",
124
+ center: {
125
+ x: 1.7577928249999983,
126
+ y: -2.7512907000000002,
127
+ },
128
+ width: 0.3155856499999966,
129
+ height: 1.0583332999999997,
130
+ pins: [
131
+ {
132
+ pinId: "R13.1",
133
+ x: 1.7580660749999977,
134
+ y: -3.3025814000000002,
135
+ },
136
+ {
137
+ pinId: "R13.2",
138
+ x: 1.757519574999999,
139
+ y: -2.2,
140
+ },
141
+ ],
142
+ },
143
+ ],
144
+ directConnections: [
145
+ {
146
+ pinIds: ["C20.1", "U3.8"],
147
+ netId: "capacitor.C20 > port.pin1 to .U3 > .VDD",
148
+ },
149
+ {
150
+ pinIds: ["C20.2", "U3.4"],
151
+ netId: "capacitor.C20 > port.pin2 to .U3 > .GND",
152
+ },
153
+ {
154
+ pinIds: ["R11.2", "U3.1"],
155
+ netId: "resistor.R11 > port.pin2 to .U3 > .N_CS",
156
+ },
157
+ {
158
+ pinIds: ["R13.2", "R12.2"],
159
+ netId: "resistor.R11 > port.pin1 to resistor.R12 > port.pin1",
160
+ },
161
+ ],
162
+ netConnections: [
163
+ {
164
+ netId: "V3_3",
165
+ pinIds: ["U3.8", "U3.3", "U3.7", "C20.1", "R11.1"],
166
+ netLabelWidth: 1,
167
+ },
168
+ {
169
+ netId: "GND",
170
+ pinIds: ["U3.4", "C20.2"],
171
+ netLabelWidth: 0.8,
172
+ },
173
+ {
174
+ netId: "FLASH_N_CS",
175
+ pinIds: ["U3.1", "R11.2"],
176
+ netLabelWidth: 0.1,
177
+ },
178
+ ],
179
+ availableNetLabelOrientations: {
180
+ V3_3: ["y+"],
181
+ GND: ["y-"],
182
+ },
183
+ maxMspPairDistance: 5,
184
+ } as InputProblem
185
+
186
+ test("example20", () => {
187
+ const solver = new SchematicTracePipelineSolver(inputProblem)
188
+ solver.solve()
189
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
190
+ })