@tscircuit/schematic-trace-solver 0.0.60 → 0.0.61

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
@@ -648,7 +648,7 @@ type CandidateLabel = {
648
648
  height: number;
649
649
  };
650
650
  type CandidateStatus$1 = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
651
- type CandidatePhase = "rotate" | "shift";
651
+ type CandidatePhase = "rotate" | "shift" | "lateral-shift";
652
652
  type EvaluatedCandidate = CandidateLabel & {
653
653
  status: CandidateStatus$1;
654
654
  selected: boolean;
@@ -688,12 +688,23 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
688
688
  private findValidRotatedCandidate;
689
689
  private getAvailableOrientations;
690
690
  private findValidShiftedCandidate;
691
+ /**
692
+ * When all candidates fail for the current (unshifted) position, try
693
+ * shifting the label anchor laterally — x for y-orientations, y for
694
+ * x-orientations — and re-attempting the required orientation.
695
+ *
696
+ * Offsets are tried in alternating sign order:
697
+ * -1·step, +1·step, -2·step, +2·step, …
698
+ * so the nearest escape routes are tested first.
699
+ */
700
+ private findValidLateralShiftedCandidate;
691
701
  private findValidCandidateInShiftColumn;
692
702
  private evaluateCandidate;
693
703
  private getSearchStartAnchor;
694
704
  private getWickOffsetAnchor;
695
705
  private getSideOffsetAnchor;
696
706
  private getSearchDistanceLimit;
707
+ private getLateralColumnMaxDistance;
697
708
  private createCandidate;
698
709
  private getNetLabelWidth;
699
710
  private getCandidateStatus;
package/dist/index.js CHANGED
@@ -5873,11 +5873,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5873
5873
  this.addConnectorTrace(label, candidate, labelIndex);
5874
5874
  }
5875
5875
  addConnectorTrace(label, candidate, labelIndex) {
5876
- const tracePath = getConnectorTracePath(
5877
- label.anchorPoint,
5878
- candidate.anchorPoint,
5879
- candidate.orientation
5880
- );
5876
+ let tracePath;
5877
+ if (candidate.phase === "lateral-shift") {
5878
+ const orientDir = dir(candidate.orientation);
5879
+ const kickedSource = {
5880
+ x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
5881
+ y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP
5882
+ };
5883
+ tracePath = simplifyOrthogonalPath([
5884
+ label.anchorPoint,
5885
+ ...getConnectorTracePath(
5886
+ kickedSource,
5887
+ candidate.anchorPoint,
5888
+ candidate.orientation
5889
+ )
5890
+ ]);
5891
+ } else {
5892
+ tracePath = getConnectorTracePath(
5893
+ label.anchorPoint,
5894
+ candidate.anchorPoint,
5895
+ candidate.orientation
5896
+ );
5897
+ }
5881
5898
  if (tracePath.length < 2) return;
5882
5899
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
5883
5900
  const connectorTrace = {
@@ -5910,7 +5927,18 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5910
5927
  labelIndex,
5911
5928
  orientations
5912
5929
  );
5913
- return rotatedCandidate ?? this.findValidShiftedCandidate(label, orientations[0], labelIndex);
5930
+ if (rotatedCandidate) return rotatedCandidate;
5931
+ const shiftedCandidate = this.findValidShiftedCandidate(
5932
+ label,
5933
+ orientations[0],
5934
+ labelIndex
5935
+ );
5936
+ if (shiftedCandidate) return shiftedCandidate;
5937
+ return this.findValidLateralShiftedCandidate(
5938
+ label,
5939
+ orientations[0],
5940
+ labelIndex
5941
+ );
5914
5942
  }
5915
5943
  findValidRotatedCandidate(label, labelIndex, orientations) {
5916
5944
  for (const orientation of orientations) {
@@ -5964,6 +5992,50 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5964
5992
  }
5965
5993
  return null;
5966
5994
  }
5995
+ /**
5996
+ * When all candidates fail for the current (unshifted) position, try
5997
+ * shifting the label anchor laterally — x for y-orientations, y for
5998
+ * x-orientations — and re-attempting the required orientation.
5999
+ *
6000
+ * Offsets are tried in alternating sign order:
6001
+ * -1·step, +1·step, -2·step, +2·step, …
6002
+ * so the nearest escape routes are tested first.
6003
+ */
6004
+ findValidLateralShiftedCandidate(label, orientation, labelIndex) {
6005
+ const direction = dir(orientation);
6006
+ const initialBaseAnchor = this.getSearchStartAnchor(label, orientation);
6007
+ const lateralDir = {
6008
+ x: isYOrientation(orientation) ? 1 : 0,
6009
+ y: isXOrientation(orientation) ? 1 : 0
6010
+ };
6011
+ const maxSteps = Math.ceil(this.maxSearchDistance / LABEL_SEARCH_STEP);
6012
+ for (let step = 1; step <= maxSteps; step++) {
6013
+ for (const sign of [-1, 1]) {
6014
+ const lateralOffset = sign * step * LABEL_SEARCH_STEP;
6015
+ const baseAnchor = {
6016
+ x: initialBaseAnchor.x + lateralDir.x * lateralOffset,
6017
+ y: initialBaseAnchor.y + lateralDir.y * lateralOffset
6018
+ };
6019
+ const maxSearchDistance = this.getLateralColumnMaxDistance(
6020
+ label,
6021
+ orientation,
6022
+ baseAnchor
6023
+ );
6024
+ const candidate = this.findValidCandidateInShiftColumn({
6025
+ label,
6026
+ labelIndex,
6027
+ orientation,
6028
+ direction,
6029
+ baseAnchor,
6030
+ maxSearchDistance,
6031
+ outwardDistance: lateralOffset,
6032
+ phase: "lateral-shift"
6033
+ });
6034
+ if (candidate) return candidate;
6035
+ }
6036
+ }
6037
+ return null;
6038
+ }
5967
6039
  findValidCandidateInShiftColumn(params) {
5968
6040
  const {
5969
6041
  label,
@@ -5972,7 +6044,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5972
6044
  direction,
5973
6045
  baseAnchor,
5974
6046
  maxSearchDistance,
5975
- outwardDistance
6047
+ outwardDistance,
6048
+ phase = "shift"
5976
6049
  } = params;
5977
6050
  for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS7; distance3 += LABEL_SEARCH_STEP) {
5978
6051
  const anchorPoint = {
@@ -5984,7 +6057,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5984
6057
  candidate,
5985
6058
  label,
5986
6059
  labelIndex,
5987
- "shift",
6060
+ phase,
5988
6061
  distance3,
5989
6062
  outwardDistance
5990
6063
  );
@@ -6065,6 +6138,21 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6065
6138
  const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
6066
6139
  return Math.min(this.maxSearchDistance, labelLength * 2);
6067
6140
  }
6141
+ getLateralColumnMaxDistance(label, orientation, baseAnchor) {
6142
+ const chipId = label.pinIds.map((pid) => this.pinMap[pid]?.chipId).find(Boolean);
6143
+ const chip = chipId ? this.chipObstacleSpatialIndex.chips.find((c) => c.chipId === chipId) : null;
6144
+ if (chip) {
6145
+ if (orientation === "y-")
6146
+ return Math.max(0, baseAnchor.y - chip.bounds.minY);
6147
+ if (orientation === "y+")
6148
+ return Math.max(0, chip.bounds.maxY - baseAnchor.y);
6149
+ if (orientation === "x-")
6150
+ return Math.max(0, baseAnchor.x - chip.bounds.minX);
6151
+ if (orientation === "x+")
6152
+ return Math.max(0, chip.bounds.maxX - baseAnchor.x);
6153
+ }
6154
+ return this.getSearchDistanceLimit(label, orientation);
6155
+ }
6068
6156
  createCandidate(label, anchorPoint, orientation) {
6069
6157
  const { width, height } = getDimsForOrientation({
6070
6158
  orientation,
@@ -6111,6 +6199,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6111
6199
  if (tracePathCrossesAnyTrace(connectorTrace, this.traceMap)) {
6112
6200
  return "trace-collision";
6113
6201
  }
6202
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
6203
+ if (tracePathCrossesAnyBounds(connectorTrace, chip.bounds)) {
6204
+ return "chip-collision";
6205
+ }
6206
+ }
6114
6207
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
6115
6208
  if (i === labelIndex) continue;
6116
6209
  const otherLabel = this.outputNetLabelPlacements[i];
@@ -20,6 +20,7 @@ import {
20
20
  isYOrientation,
21
21
  rangesOverlap,
22
22
  rectsOverlap,
23
+ simplifyOrthogonalPath,
23
24
  traceCrossesBoundsInterior,
24
25
  tracePathCrossesAnyBounds,
25
26
  tracePathCrossesAnyTrace,
@@ -127,7 +128,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
127
128
 
128
129
  private applyCandidate(
129
130
  label: NetLabelPlacement,
130
- candidate: CandidateLabel,
131
+ candidate: EvaluatedCandidate,
131
132
  labelIndex: number,
132
133
  ) {
133
134
  this.outputNetLabelPlacements[labelIndex] = {
@@ -139,14 +140,33 @@ export class AvailableNetOrientationSolver extends BaseSolver {
139
140
 
140
141
  private addConnectorTrace(
141
142
  label: NetLabelPlacement,
142
- candidate: CandidateLabel,
143
+ candidate: EvaluatedCandidate,
143
144
  labelIndex: number,
144
145
  ) {
145
- const tracePath = getConnectorTracePath(
146
- label.anchorPoint,
147
- candidate.anchorPoint,
148
- candidate.orientation,
149
- )
146
+ let tracePath: Point[]
147
+
148
+ if (candidate.phase === "lateral-shift") {
149
+ const orientDir = dir(candidate.orientation)
150
+ const kickedSource = {
151
+ x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
152
+ y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP,
153
+ }
154
+ tracePath = simplifyOrthogonalPath([
155
+ label.anchorPoint,
156
+ ...getConnectorTracePath(
157
+ kickedSource,
158
+ candidate.anchorPoint,
159
+ candidate.orientation,
160
+ ),
161
+ ])
162
+ } else {
163
+ tracePath = getConnectorTracePath(
164
+ label.anchorPoint,
165
+ candidate.anchorPoint,
166
+ candidate.orientation,
167
+ )
168
+ }
169
+
150
170
  if (tracePath.length < 2) return
151
171
 
152
172
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`
@@ -185,10 +205,19 @@ export class AvailableNetOrientationSolver extends BaseSolver {
185
205
  labelIndex,
186
206
  orientations,
187
207
  )
208
+ if (rotatedCandidate) return rotatedCandidate
209
+
210
+ const shiftedCandidate = this.findValidShiftedCandidate(
211
+ label,
212
+ orientations[0]!,
213
+ labelIndex,
214
+ )
215
+ if (shiftedCandidate) return shiftedCandidate
188
216
 
189
- return (
190
- rotatedCandidate ??
191
- this.findValidShiftedCandidate(label, orientations[0]!, labelIndex)
217
+ return this.findValidLateralShiftedCandidate(
218
+ label,
219
+ orientations[0]!,
220
+ labelIndex,
192
221
  )
193
222
  }
194
223
 
@@ -266,6 +295,63 @@ export class AvailableNetOrientationSolver extends BaseSolver {
266
295
  return null
267
296
  }
268
297
 
298
+ /**
299
+ * When all candidates fail for the current (unshifted) position, try
300
+ * shifting the label anchor laterally — x for y-orientations, y for
301
+ * x-orientations — and re-attempting the required orientation.
302
+ *
303
+ * Offsets are tried in alternating sign order:
304
+ * -1·step, +1·step, -2·step, +2·step, …
305
+ * so the nearest escape routes are tested first.
306
+ */
307
+ private findValidLateralShiftedCandidate(
308
+ label: NetLabelPlacement,
309
+ orientation: FacingDirection,
310
+ labelIndex: number,
311
+ ): EvaluatedCandidate | null {
312
+ const direction = dir(orientation)
313
+ const initialBaseAnchor = this.getSearchStartAnchor(label, orientation)
314
+
315
+ // Lateral axis: perpendicular to the orientation direction
316
+ const lateralDir: Point = {
317
+ x: isYOrientation(orientation) ? 1 : 0,
318
+ y: isXOrientation(orientation) ? 1 : 0,
319
+ }
320
+
321
+ const maxSteps = Math.ceil(this.maxSearchDistance / LABEL_SEARCH_STEP)
322
+
323
+ for (let step = 1; step <= maxSteps; step++) {
324
+ for (const sign of [-1, 1]) {
325
+ const lateralOffset = sign * step * LABEL_SEARCH_STEP
326
+ const baseAnchor = {
327
+ x: initialBaseAnchor.x + lateralDir.x * lateralOffset,
328
+ y: initialBaseAnchor.y + lateralDir.y * lateralOffset,
329
+ }
330
+
331
+ const maxSearchDistance = this.getLateralColumnMaxDistance(
332
+ label,
333
+ orientation,
334
+ baseAnchor,
335
+ )
336
+
337
+ const candidate = this.findValidCandidateInShiftColumn({
338
+ label,
339
+ labelIndex,
340
+ orientation,
341
+ direction,
342
+ baseAnchor,
343
+ maxSearchDistance,
344
+ outwardDistance: lateralOffset,
345
+ phase: "lateral-shift",
346
+ })
347
+
348
+ if (candidate) return candidate
349
+ }
350
+ }
351
+
352
+ return null
353
+ }
354
+
269
355
  private findValidCandidateInShiftColumn(params: {
270
356
  label: NetLabelPlacement
271
357
  labelIndex: number
@@ -274,6 +360,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
274
360
  baseAnchor: Point
275
361
  maxSearchDistance: number
276
362
  outwardDistance: number
363
+ phase?: CandidatePhase
277
364
  }) {
278
365
  const {
279
366
  label,
@@ -283,6 +370,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
283
370
  baseAnchor,
284
371
  maxSearchDistance,
285
372
  outwardDistance,
373
+ phase = "shift",
286
374
  } = params
287
375
 
288
376
  for (
@@ -299,7 +387,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
299
387
  candidate,
300
388
  label,
301
389
  labelIndex,
302
- "shift",
390
+ phase,
303
391
  distance,
304
392
  outwardDistance,
305
393
  )
@@ -412,6 +500,32 @@ export class AvailableNetOrientationSolver extends BaseSolver {
412
500
  return Math.min(this.maxSearchDistance, labelLength * 2)
413
501
  }
414
502
 
503
+ private getLateralColumnMaxDistance(
504
+ label: NetLabelPlacement,
505
+ orientation: FacingDirection,
506
+ baseAnchor: Point,
507
+ ) {
508
+ const chipId = label.pinIds
509
+ .map((pid) => this.pinMap[pid]?.chipId)
510
+ .find(Boolean)
511
+ const chip = chipId
512
+ ? this.chipObstacleSpatialIndex.chips.find((c) => c.chipId === chipId)
513
+ : null
514
+
515
+ if (chip) {
516
+ if (orientation === "y-")
517
+ return Math.max(0, baseAnchor.y - chip.bounds.minY)
518
+ if (orientation === "y+")
519
+ return Math.max(0, chip.bounds.maxY - baseAnchor.y)
520
+ if (orientation === "x-")
521
+ return Math.max(0, baseAnchor.x - chip.bounds.minX)
522
+ if (orientation === "x+")
523
+ return Math.max(0, chip.bounds.maxX - baseAnchor.x)
524
+ }
525
+
526
+ return this.getSearchDistanceLimit(label, orientation)
527
+ }
528
+
415
529
  private createCandidate(
416
530
  label: NetLabelPlacement,
417
531
  anchorPoint: Point,
@@ -474,6 +588,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
474
588
  return "trace-collision"
475
589
  }
476
590
 
591
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
592
+ if (tracePathCrossesAnyBounds(connectorTrace, chip.bounds)) {
593
+ return "chip-collision"
594
+ }
595
+ }
596
+
477
597
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
478
598
  if (i === labelIndex) continue
479
599
  const otherLabel = this.outputNetLabelPlacements[i]!
@@ -168,7 +168,7 @@ export const getConnectorTracePath = (
168
168
  : [source, { x: source.x, y: target.y }, target],
169
169
  )
170
170
 
171
- const simplifyOrthogonalPath = (path: Point[]) => {
171
+ export const simplifyOrthogonalPath = (path: Point[]) => {
172
172
  const deduped = path.filter(
173
173
  (point, index) => index === 0 || !pointsEqual(point, path[index - 1]!),
174
174
  )
@@ -33,7 +33,7 @@ export type CandidateStatus =
33
33
  | "trace-collision"
34
34
  | "netlabel-collision"
35
35
 
36
- export type CandidatePhase = "rotate" | "shift"
36
+ export type CandidatePhase = "rotate" | "shift" | "lateral-shift"
37
37
 
38
38
  export type EvaluatedCandidate = CandidateLabel & {
39
39
  status: CandidateStatus
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.60",
4
+ "version": "0.0.61",
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/example37.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/assets/example38.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -0,0 +1,173 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "schematic_component_0",
5
+ "center": {
6
+ "x": -3.69,
7
+ "y": 1.7
8
+ },
9
+ "width": 1.1,
10
+ "height": 0.388910699999999,
11
+ "pins": [
12
+ {
13
+ "pinId": "R1.1",
14
+ "x": -4.24,
15
+ "y": 1.7
16
+ },
17
+ {
18
+ "pinId": "R1.2",
19
+ "x": -3.14,
20
+ "y": 1.7
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "chipId": "schematic_component_1",
26
+ "center": {
27
+ "x": -6.19,
28
+ "y": 0.3
29
+ },
30
+ "width": 1.1,
31
+ "height": 0.84,
32
+ "pins": [
33
+ {
34
+ "pinId": "C1.1",
35
+ "x": -6.74,
36
+ "y": 0.3
37
+ },
38
+ {
39
+ "pinId": "C1.2",
40
+ "x": -5.640000000000001,
41
+ "y": 0.3
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "chipId": "schematic_component_2",
47
+ "center": {
48
+ "x": 0,
49
+ "y": 0
50
+ },
51
+ "width": 0.4,
52
+ "height": 1,
53
+ "pins": [
54
+ {
55
+ "pinId": "U1.1",
56
+ "x": -0.6000000000000001,
57
+ "y": 0.30000000000000004
58
+ },
59
+ {
60
+ "pinId": "U1.2",
61
+ "x": -0.6000000000000001,
62
+ "y": 0.10000000000000003
63
+ },
64
+ {
65
+ "pinId": "U1.3",
66
+ "x": -0.6000000000000001,
67
+ "y": -0.09999999999999998
68
+ },
69
+ {
70
+ "pinId": "U1.4",
71
+ "x": -0.6000000000000001,
72
+ "y": -0.30000000000000004
73
+ },
74
+ {
75
+ "pinId": "U1.5",
76
+ "x": 0.6000000000000001,
77
+ "y": -0.30000000000000004
78
+ },
79
+ {
80
+ "pinId": "U1.6",
81
+ "x": 0.6000000000000001,
82
+ "y": -0.10000000000000003
83
+ },
84
+ {
85
+ "pinId": "U1.7",
86
+ "x": 0.6000000000000001,
87
+ "y": 0.09999999999999998
88
+ },
89
+ {
90
+ "pinId": "U1.8",
91
+ "x": 0.6000000000000001,
92
+ "y": 0.30000000000000004
93
+ }
94
+ ]
95
+ },
96
+ {
97
+ "chipId": "schematic_component_3",
98
+ "center": {
99
+ "x": -2.92,
100
+ "y": -0.4
101
+ },
102
+ "width": 0.4,
103
+ "height": 1,
104
+ "pins": [
105
+ {
106
+ "pinId": "U2.1",
107
+ "x": -3.52,
108
+ "y": -0.09999999999999998
109
+ },
110
+ {
111
+ "pinId": "U2.2",
112
+ "x": -3.52,
113
+ "y": -0.3
114
+ },
115
+ {
116
+ "pinId": "U2.3",
117
+ "x": -3.52,
118
+ "y": -0.5
119
+ },
120
+ {
121
+ "pinId": "U2.4",
122
+ "x": -3.52,
123
+ "y": -0.7000000000000001
124
+ },
125
+ {
126
+ "pinId": "U2.5",
127
+ "x": -2.32,
128
+ "y": -0.7000000000000001
129
+ },
130
+ {
131
+ "pinId": "U2.6",
132
+ "x": -2.32,
133
+ "y": -0.5
134
+ },
135
+ {
136
+ "pinId": "U2.7",
137
+ "x": -2.32,
138
+ "y": -0.30000000000000004
139
+ },
140
+ {
141
+ "pinId": "U2.8",
142
+ "x": -2.32,
143
+ "y": -0.09999999999999998
144
+ }
145
+ ]
146
+ }
147
+ ],
148
+ "directConnections": [
149
+ {
150
+ "pinIds": ["C1.1", "R1.1"],
151
+ "netId": ".C1 > .pin1 to R1.pin1"
152
+ },
153
+ {
154
+ "pinIds": ["U1.1", "C1.2"],
155
+ "netId": ".U1 > .pin1 to C1.pin2"
156
+ },
157
+ {
158
+ "pinIds": ["U1.2", "R1.2"],
159
+ "netId": ".U1 > .pin2 to R1.pin2"
160
+ },
161
+ {
162
+ "pinIds": ["U2.1", "U1.3"],
163
+ "netId": ".U2 > .pin1 to U1.pin3"
164
+ },
165
+ {
166
+ "pinIds": ["U2.2", "U1.4"],
167
+ "netId": ".U2 > .pin2 to U1.pin4"
168
+ }
169
+ ],
170
+ "netConnections": [],
171
+ "availableNetLabelOrientations": {},
172
+ "maxMspPairDistance": 2.4
173
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "schematic_component_0",
5
+ "center": {
6
+ "x": 0,
7
+ "y": 0
8
+ },
9
+ "width": 0.2,
10
+ "height": 0.325,
11
+ "pins": [
12
+ {
13
+ "pinId": "TP1.1",
14
+ "x": -1.2246467991473533e-17,
15
+ "y": 0.2
16
+ }
17
+ ]
18
+ }
19
+ ],
20
+ "directConnections": [],
21
+ "netConnections": [
22
+ {
23
+ "netId": "GND",
24
+ "pinIds": ["TP1.1"],
25
+ "netLabelWidth": 0.48
26
+ }
27
+ ],
28
+ "availableNetLabelOrientations": {
29
+ "GND": ["y-"]
30
+ },
31
+ "maxMspPairDistance": 2.4
32
+ }
@@ -2,39 +2,38 @@
2
2
  <rect width="100%" height="100%" fill="white" />
3
3
  <g>
4
4
  <circle data-type="point" data-label="Q1.1
5
- y+" data-x="0.30397715550000004" data-y="0.5800832909999993" cx="402.5477478715151" cy="162.47313199841204" r="3" fill="hsl(315, 100%, 50%, 0.8)" />
5
+ y+" data-x="0.30397715550000004" data-y="0.5800832909999993" cx="386.41696544795997" cy="192.03773223990865" r="3" fill="hsl(315, 100%, 50%, 0.8)" />
6
6
  </g>
7
7
  <g>
8
8
  <circle data-type="point" data-label="Q1.2
9
- y-" data-x="0.31067575550000137" data-y="-0.5800832909999993" cx="404.36681333050564" cy="477.52686800158796" r="3" fill="hsl(316, 100%, 50%, 0.8)" />
9
+ y-" data-x="0.31067575550000137" data-y="-0.5800832909999993" cx="388.6751471623335" cy="583.1443755831586" r="3" fill="hsl(316, 100%, 50%, 0.8)" />
10
10
  </g>
11
11
  <g>
12
12
  <circle data-type="point" data-label="Q1.3
13
- x-" data-x="-0.4467558855000001" data-y="-0.10250625000000019" cx="198.67939570751895" cy="347.8364999709806" r="3" fill="hsl(317, 100%, 50%, 0.8)" />
13
+ x-" data-x="-0.4467558855000001" data-y="-0.10250625000000019" cx="133.33548191977735" cy="422.1471909191105" r="3" fill="hsl(317, 100%, 50%, 0.8)" />
14
14
  </g>
15
15
  <g>
16
- <circle data-type="point" data-label="anchorPoint
17
- orientation: y+" data-x="0.30397715550000004" data-y="0.5800832909999993" cx="402.5477478715151" cy="162.47313199841204" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
16
+ <circle data-type="point" data-label="" data-x="0.30397715550000004" data-y="0.5800832909999993" cx="386.41696544795997" cy="192.03773223990865" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
18
17
  </g>
19
18
  <g>
20
- <circle data-type="point" data-label="anchorPoint
21
- orientation: y-" data-x="0.31067575550000137" data-y="-0.5800832909999993" cx="404.36681333050564" cy="477.52686800158796" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
19
+ <circle data-type="point" data-label="" data-x="0.5606757555000014" data-y="-0.5290832909999993" cx="472.95326924654" cy="565.9516386779806" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
22
20
  </g>
23
21
  <g>
24
- <polyline data-points="0.30397715550000004,0.5800832909999993 0.31067575550000137,-0.5800832909999993" data-type="line" data-label="" points="402.5477478715151,162.47313199841204 404.36681333050564,477.52686800158796" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
22
+ <polyline data-points="0.30397715550000004,0.5800832909999993 0.31067575550000137,-0.5800832909999993" data-type="line" data-label="" points="386.41696544795997,192.03773223990865 388.6751471623335,583.1443755831586" fill="none" stroke="hsl(73, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
25
23
  </g>
26
24
  <g>
27
- <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="198.67939570751895" y="162.47313199841204" width="242.6412085849621" height="315.0537360031759" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.003682440324999998" />
25
+ <polyline data-points="0.31067575550000137,-0.5800832909999993 0.31067575550000137,-0.6300832909999994 0.5606757555000014,-0.6300832909999994 0.5606757555000014,-0.5290832909999993" data-type="line" data-label="" points="388.6751471623335,583.1443755831586 388.6751471623335,600 472.95326924654,600 472.95326924654,565.9516386779806" fill="none" stroke="purple" stroke-width="1" />
26
+ </g>
27
+ <g>
28
+ <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="133.33548191977735" y="192.03773223990865" width="301.2139764800545" height="391.10664334324997" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0029663688964285694" />
28
29
  </g>
29
30
  <g>
30
31
  <rect data-type="rect" data-label="netId: V3_3
31
- globalConnNetId: connectivity_net0
32
- available orientations: y+" data-x="0.30397715550000004" data-y="0.8060832909999993" x="375.3918427720889" y="40" width="54.311810198852356" height="122.20157294741779" fill="#ef444466" stroke="#ef4444" stroke-width="0.003682440324999998" />
32
+ globalConnNetId: connectivity_net0" data-x="0.30397715550000004" data-y="0.8060832909999993" x="352.7057166142773" y="40" width="67.42249766736523" height="151.70061975157182" fill="#ef444466" stroke="#ef4444" stroke-width="0.0029663688964285694" />
33
33
  </g>
34
34
  <g>
35
35
  <rect data-type="rect" data-label="netId: V3_3
36
- globalConnNetId: connectivity_net0
37
- available orientations: y+" data-x="0.31067575550000137" data-y="-0.8060832909999993" x="377.2109082310795" y="477.7984270525822" width="54.3118101988523" height="122.20157294741779" fill="#ef444466" stroke="#ef4444" stroke-width="0.003682440324999998" />
36
+ globalConnNetId: connectivity_net0" data-x="0.5606757555000014" data-y="-0.3040832909999993" x="439.2420204128574" y="414.25101892640873" width="67.42249766736518" height="151.70061975157182" fill="#ef444466" stroke="#ef4444" stroke-width="0.0029663688964285694" />
38
37
  </g>
39
38
  <g id="crosshair" style="display: none">
40
39
  <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
@@ -64,12 +63,12 @@ available orientations: y+" data-x="0.31067575550000137" data-y="-0.806083290999
64
63
 
65
64
  // Calculate real coordinates using inverse transformation
66
65
  const matrix = {
67
- "a": 271.55905099426167,
66
+ "a": 337.1124883368262,
68
67
  "c": 0,
69
- "e": 320,
68
+ "e": 283.9424701598046,
70
69
  "b": 0,
71
- "d": -271.55905099426167,
72
- "f": 320
70
+ "d": -337.1124883368262,
71
+ "f": 387.59105391153366
73
72
  };
74
73
  // Manually invert and apply the affine transform
75
74
  // Since we only use translate and scale, we can directly compute:
@@ -0,0 +1,320 @@
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="R1.1
5
+ x-" data-x="-4.24" data-y="1.7" cx="240.53050397877985" cy="230.66942944297082" r="3" fill="hsl(226, 100%, 50%, 0.8)" />
6
+ </g>
7
+ <g>
8
+ <circle data-type="point" data-label="R1.2
9
+ x+" data-x="-3.1399999999999997" data-y="1.7" cx="322.2281167108754" cy="230.66942944297082" r="3" fill="hsl(227, 100%, 50%, 0.8)" />
10
+ </g>
11
+ <g>
12
+ <circle data-type="point" data-label="C1.1
13
+ x-" data-x="-6.74" data-y="0.3" cx="54.85411140583557" cy="334.6482092838196" r="3" fill="hsl(121, 100%, 50%, 0.8)" />
14
+ </g>
15
+ <g>
16
+ <circle data-type="point" data-label="C1.2
17
+ x+" data-x="-5.640000000000001" data-y="0.3" cx="136.55172413793105" cy="334.6482092838196" r="3" fill="hsl(122, 100%, 50%, 0.8)" />
18
+ </g>
19
+ <g>
20
+ <circle data-type="point" data-label="U1.1
21
+ x-" data-x="-0.6000000000000001" data-y="0.30000000000000004" cx="510.8753315649867" cy="334.6482092838196" r="3" fill="hsl(319, 100%, 50%, 0.8)" />
22
+ </g>
23
+ <g>
24
+ <circle data-type="point" data-label="U1.2
25
+ x-" data-x="-0.6000000000000001" data-y="0.10000000000000003" cx="510.8753315649867" cy="349.50232068965516" r="3" fill="hsl(320, 100%, 50%, 0.8)" />
26
+ </g>
27
+ <g>
28
+ <circle data-type="point" data-label="U1.3
29
+ x-" data-x="-0.6000000000000001" data-y="-0.09999999999999998" cx="510.8753315649867" cy="364.3564320954907" r="3" fill="hsl(321, 100%, 50%, 0.8)" />
30
+ </g>
31
+ <g>
32
+ <circle data-type="point" data-label="U1.4
33
+ x-" data-x="-0.6000000000000001" data-y="-0.30000000000000004" cx="510.8753315649867" cy="379.21054350132624" r="3" fill="hsl(322, 100%, 50%, 0.8)" />
34
+ </g>
35
+ <g>
36
+ <circle data-type="point" data-label="U1.5
37
+ x+" data-x="0.6000000000000001" data-y="-0.30000000000000004" cx="600" cy="379.21054350132624" r="3" fill="hsl(323, 100%, 50%, 0.8)" />
38
+ </g>
39
+ <g>
40
+ <circle data-type="point" data-label="U1.6
41
+ x+" data-x="0.6000000000000001" data-y="-0.10000000000000003" cx="600" cy="364.3564320954907" r="3" fill="hsl(324, 100%, 50%, 0.8)" />
42
+ </g>
43
+ <g>
44
+ <circle data-type="point" data-label="U1.7
45
+ x+" data-x="0.6000000000000001" data-y="0.09999999999999998" cx="600" cy="349.50232068965516" r="3" fill="hsl(325, 100%, 50%, 0.8)" />
46
+ </g>
47
+ <g>
48
+ <circle data-type="point" data-label="U1.8
49
+ x+" data-x="0.6000000000000001" data-y="0.30000000000000004" cx="600" cy="334.6482092838196" r="3" fill="hsl(326, 100%, 50%, 0.8)" />
50
+ </g>
51
+ <g>
52
+ <circle data-type="point" data-label="U2.1
53
+ x-" data-x="-3.52" data-y="-0.09999999999999998" cx="294.0053050397878" cy="364.3564320954907" r="3" fill="hsl(200, 100%, 50%, 0.8)" />
54
+ </g>
55
+ <g>
56
+ <circle data-type="point" data-label="U2.2
57
+ x-" data-x="-3.52" data-y="-0.3" cx="294.0053050397878" cy="379.21054350132624" r="3" fill="hsl(201, 100%, 50%, 0.8)" />
58
+ </g>
59
+ <g>
60
+ <circle data-type="point" data-label="U2.3
61
+ x-" data-x="-3.52" data-y="-0.5" cx="294.0053050397878" cy="394.06465490716175" r="3" fill="hsl(202, 100%, 50%, 0.8)" />
62
+ </g>
63
+ <g>
64
+ <circle data-type="point" data-label="U2.4
65
+ x-" data-x="-3.52" data-y="-0.7000000000000001" cx="294.0053050397878" cy="408.9187663129973" r="3" fill="hsl(203, 100%, 50%, 0.8)" />
66
+ </g>
67
+ <g>
68
+ <circle data-type="point" data-label="U2.5
69
+ x+" data-x="-2.32" data-y="-0.7000000000000001" cx="383.1299734748011" cy="408.9187663129973" r="3" fill="hsl(204, 100%, 50%, 0.8)" />
70
+ </g>
71
+ <g>
72
+ <circle data-type="point" data-label="U2.6
73
+ x+" data-x="-2.32" data-y="-0.5" cx="383.1299734748011" cy="394.06465490716175" r="3" fill="hsl(205, 100%, 50%, 0.8)" />
74
+ </g>
75
+ <g>
76
+ <circle data-type="point" data-label="U2.7
77
+ x+" data-x="-2.32" data-y="-0.30000000000000004" cx="383.1299734748011" cy="379.21054350132624" r="3" fill="hsl(206, 100%, 50%, 0.8)" />
78
+ </g>
79
+ <g>
80
+ <circle data-type="point" data-label="U2.8
81
+ x+" data-x="-2.32" data-y="-0.09999999999999998" cx="383.1299734748011" cy="364.3564320954907" r="3" fill="hsl(207, 100%, 50%, 0.8)" />
82
+ </g>
83
+ <g>
84
+ <circle data-type="point" data-label="anchor
85
+ orientation: y+" data-x="-0.6000000000000001" data-y="0.30000000000000004" cx="510.8753315649867" cy="334.6482092838196" r="3" fill="red" />
86
+ </g>
87
+ <g>
88
+ <circle data-type="point" data-label="anchor
89
+ orientation: y-" data-x="-0.6000000000000001" data-y="0.30000000000000004" cx="510.8753315649867" cy="334.6482092838196" r="3" fill="red" />
90
+ </g>
91
+ <g>
92
+ <circle data-type="point" data-label="anchor
93
+ orientation: y+" data-x="-0.6500000000000001" data-y="0.30000000000000004" cx="507.16180371352783" cy="334.6482092838196" r="3" fill="red" />
94
+ </g>
95
+ <g>
96
+ <circle data-type="point" data-label="anchor
97
+ orientation: y-" data-x="-0.6500000000000001" data-y="0.30000000000000004" cx="507.16180371352783" cy="334.6482092838196" r="3" fill="red" />
98
+ </g>
99
+ <g>
100
+ <circle data-type="point" data-label="anchor
101
+ orientation: y+" data-x="-0.7000000000000001" data-y="0.30000000000000004" cx="503.44827586206895" cy="334.6482092838196" r="3" fill="red" />
102
+ </g>
103
+ <g>
104
+ <circle data-type="point" data-label="anchor
105
+ orientation: y-" data-x="-0.7000000000000001" data-y="0.30000000000000004" cx="503.44827586206895" cy="334.6482092838196" r="3" fill="red" />
106
+ </g>
107
+ <g>
108
+ <circle data-type="point" data-label="anchor
109
+ orientation: x+" data-x="-0.7000000000000001" data-y="0.30000000000000004" cx="503.44827586206895" cy="334.6482092838196" r="3" fill="red" />
110
+ </g>
111
+ <g>
112
+ <circle data-type="point" data-label="anchor
113
+ orientation: x-" data-x="-0.7000000000000001" data-y="0.30000000000000004" cx="503.44827586206895" cy="334.6482092838196" r="3" fill="orange" />
114
+ </g>
115
+ <g>
116
+ <circle data-type="point" data-label="anchor
117
+ orientation: x+" data-x="-0.7000000000000001" data-y="0.25000000000000006" cx="503.44827586206895" cy="338.3617371352785" r="3" fill="red" />
118
+ </g>
119
+ <g>
120
+ <circle data-type="point" data-label="anchor
121
+ orientation: x-" data-x="-0.7000000000000001" data-y="0.25000000000000006" cx="503.44827586206895" cy="338.3617371352785" r="3" fill="orange" />
122
+ </g>
123
+ <g>
124
+ <circle data-type="point" data-label="anchor
125
+ orientation: x+" data-x="-0.7000000000000001" data-y="0.20000000000000004" cx="503.44827586206895" cy="342.07526498673735" r="3" fill="red" />
126
+ </g>
127
+ <g>
128
+ <circle data-type="point" data-label="anchor
129
+ orientation: x-" data-x="-0.7000000000000001" data-y="0.20000000000000004" cx="503.44827586206895" cy="342.07526498673735" r="3" fill="orange" />
130
+ </g>
131
+ <g>
132
+ <circle data-type="point" data-label="anchor
133
+ orientation: y+" data-x="-0.7000000000000001" data-y="0.20000000000000004" cx="503.44827586206895" cy="342.07526498673735" r="3" fill="red" />
134
+ </g>
135
+ <g>
136
+ <circle data-type="point" data-label="anchor
137
+ orientation: y-" data-x="-0.7000000000000001" data-y="0.20000000000000004" cx="503.44827586206895" cy="342.07526498673735" r="3" fill="red" />
138
+ </g>
139
+ <g>
140
+ <circle data-type="point" data-label="anchor
141
+ orientation: y+" data-x="-3.1700000000000004" data-y="0.2" cx="320" cy="342.07526498673735" r="3" fill="orange" />
142
+ </g>
143
+ <g>
144
+ <circle data-type="point" data-label="anchor
145
+ orientation: y-" data-x="-3.1700000000000004" data-y="0.2" cx="320" cy="342.07526498673735" r="3" fill="red" />
146
+ </g>
147
+ <g>
148
+ <circle data-type="point" data-label="anchor
149
+ orientation: y+" data-x="-5.640000000000001" data-y="0.19999999999999998" cx="136.55172413793105" cy="342.07526498673735" r="3" fill="red" />
150
+ </g>
151
+ <g>
152
+ <circle data-type="point" data-label="anchor
153
+ orientation: y-" data-x="-5.640000000000001" data-y="0.19999999999999998" cx="136.55172413793105" cy="342.07526498673735" r="3" fill="red" />
154
+ </g>
155
+ <g>
156
+ <polyline data-points="-6.74,0.3 -4.24,1.7" data-type="line" data-label="" points="54.85411140583557,334.6482092838196 240.53050397877985,230.66942944297082" fill="none" stroke="hsl(24, 100%, 50%, 0.8)" stroke-width="1" />
157
+ </g>
158
+ <g>
159
+ <polyline data-points="-0.6000000000000001,0.30000000000000004 -5.640000000000001,0.3" data-type="line" data-label="" points="510.8753315649867,334.6482092838196 136.55172413793105,334.6482092838196" fill="none" stroke="hsl(8, 100%, 50%, 0.8)" stroke-width="1" />
160
+ </g>
161
+ <g>
162
+ <polyline data-points="-0.6000000000000001,0.10000000000000003 -3.1399999999999997,1.7" data-type="line" data-label="" points="510.8753315649867,349.50232068965516 322.2281167108754,230.66942944297082" fill="none" stroke="hsl(8, 100%, 50%, 0.8)" stroke-width="1" />
163
+ </g>
164
+ <g>
165
+ <polyline data-points="-3.52,-0.09999999999999998 -0.6000000000000001,-0.09999999999999998" data-type="line" data-label="" points="294.0053050397878,364.3564320954907 510.8753315649867,364.3564320954907" fill="none" stroke="hsl(152, 100%, 50%, 0.8)" stroke-width="1" />
166
+ </g>
167
+ <g>
168
+ <polyline data-points="-3.52,-0.3 -0.6000000000000001,-0.30000000000000004" data-type="line" data-label="" points="294.0053050397878,379.21054350132624 510.8753315649867,379.21054350132624" fill="none" stroke="hsl(152, 100%, 50%, 0.8)" stroke-width="1" />
169
+ </g>
170
+ <g>
171
+ <polyline data-points="-6.74,0.3 -6.94,0.3 -6.94,1.7 -4.24,1.7" data-type="line" data-label="" points="54.85411140583557,334.6482092838196 40,334.6482092838196 40,230.66942944297082 240.53050397877985,230.66942944297082" fill="none" stroke="black" stroke-width="1" />
172
+ </g>
173
+ <g>
174
+ <polyline data-points="-0.6000000000000001,0.30000000000000004 -0.7000000000000001,0.30000000000000004 -0.7000000000000001,0.20000000000000004 -5.640000000000001,0.19999999999999998" data-type="line" data-label="" points="510.8753315649867,334.6482092838196 503.44827586206895,334.6482092838196 503.44827586206895,342.07526498673735 136.55172413793105,342.07526498673735" fill="none" stroke="black" stroke-width="1" />
175
+ </g>
176
+ <g>
177
+ <polyline data-points="-3.52,-0.09999999999999998 -3.72,-0.09999999999999998 -3.72,0.4 -0.8000000000000007,0.4 -0.8000000000000007,-0.09999999999999998 -0.6000000000000005,-0.09999999999999998" data-type="line" data-label="" points="294.0053050397878,364.3564320954907 279.1511936339523,364.3564320954907 279.1511936339523,327.22115358090184 496.02122015915114,327.22115358090184 496.02122015915114,364.3564320954907 510.8753315649867,364.3564320954907" fill="none" stroke="black" stroke-width="1" />
178
+ </g>
179
+ <g>
180
+ <rect data-type="rect" data-label="schematic_component_0" data-x="-3.69" data-y="1.7" x="240.53050397877985" y="216.22712228116714" width="81.69761273209554" height="28.884614323607337" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.013464285714285717" />
181
+ </g>
182
+ <g>
183
+ <rect data-type="rect" data-label="schematic_component_1" data-x="-6.19" data-y="0.3" x="54.85411140583557" y="303.45457533156497" width="81.69761273209548" height="62.38726790450926" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.013464285714285717" />
184
+ </g>
185
+ <g>
186
+ <rect data-type="rect" data-label="schematic_component_2" data-x="0" data-y="0" x="510.8753315649867" y="319.7940978779841" width="89.12466843501329" height="74.27055702917767" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.013464285714285717" />
187
+ </g>
188
+ <g>
189
+ <rect data-type="rect" data-label="schematic_component_3" data-x="-2.92" data-y="-0.4" x="294.0053050397878" y="349.50232068965516" width="89.12466843501329" height="74.27055702917772" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.013464285714285717" />
190
+ </g>
191
+ <g>
192
+ <rect data-type="rect" data-label="status: chip-collision
193
+ orientation: y+" data-x="-0.6000000000000001" data-y="0.5251" x="503.44827586206895" y="301.2190315649867" width="14.85411140583551" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
194
+ </g>
195
+ <g>
196
+ <rect data-type="rect" data-label="status: chip-collision
197
+ orientation: y-" data-x="-0.6000000000000001" data-y="0.07490000000000005" x="503.44827586206895" y="334.6556363395225" width="14.85411140583551" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
198
+ </g>
199
+ <g>
200
+ <rect data-type="rect" data-label="status: chip-collision
201
+ orientation: y+" data-x="-0.6500000000000001" data-y="0.5251" x="499.7347480106101" y="301.2190315649867" width="14.854111405835567" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
202
+ </g>
203
+ <g>
204
+ <rect data-type="rect" data-label="status: chip-collision
205
+ orientation: y-" data-x="-0.6500000000000001" data-y="0.07490000000000005" x="499.7347480106101" y="334.6556363395225" width="14.854111405835567" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
206
+ </g>
207
+ <g>
208
+ <rect data-type="rect" data-label="status: chip-collision
209
+ orientation: y+" data-x="-0.7000000000000001" data-y="0.5251" x="496.0212201591512" y="301.2190315649867" width="14.85411140583551" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
210
+ </g>
211
+ <g>
212
+ <rect data-type="rect" data-label="status: chip-collision
213
+ orientation: y-" data-x="-0.7000000000000001" data-y="0.07490000000000005" x="496.0212201591512" y="334.6556363395225" width="14.85411140583551" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
214
+ </g>
215
+ <g>
216
+ <rect data-type="rect" data-label="status: chip-collision
217
+ orientation: x+" data-x="-0.4749000000000001" data-y="0.30000000000000004" x="503.4557029177719" y="327.22115358090184" width="33.421750663129956" height="14.85411140583551" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
218
+ </g>
219
+ <g>
220
+ <rect data-type="rect" data-label="status: trace-collision
221
+ orientation: x-" data-x="-0.9251" data-y="0.30000000000000004" x="470.0190981432361" y="327.22115358090184" width="33.421750663129956" height="14.85411140583551" fill="rgba(220, 140, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
222
+ </g>
223
+ <g>
224
+ <rect data-type="rect" data-label="status: chip-collision
225
+ orientation: x+" data-x="-0.4749000000000001" data-y="0.25000000000000006" x="503.4557029177719" y="330.9346814323607" width="33.421750663129956" height="14.854111405835567" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
226
+ </g>
227
+ <g>
228
+ <rect data-type="rect" data-label="status: trace-collision
229
+ orientation: x-" data-x="-0.9251" data-y="0.25000000000000006" x="470.0190981432361" y="330.9346814323607" width="33.421750663129956" height="14.854111405835567" fill="rgba(220, 140, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
230
+ </g>
231
+ <g>
232
+ <rect data-type="rect" data-label="status: chip-collision
233
+ orientation: x+" data-x="-0.4749000000000001" data-y="0.20000000000000004" x="503.4557029177719" y="334.6482092838196" width="33.421750663129956" height="14.854111405835567" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
234
+ </g>
235
+ <g>
236
+ <rect data-type="rect" data-label="status: trace-collision
237
+ orientation: x-" data-x="-0.9251" data-y="0.20000000000000004" x="470.0190981432361" y="334.6482092838196" width="33.421750663129956" height="14.854111405835567" fill="rgba(220, 140, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
238
+ </g>
239
+ <g>
240
+ <rect data-type="rect" data-label="status: chip-collision
241
+ orientation: y+" data-x="-0.7000000000000001" data-y="0.42510000000000003" x="496.0212201591512" y="308.6460872679045" width="14.85411140583551" height="33.421750663129956" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
242
+ </g>
243
+ <g>
244
+ <rect data-type="rect" data-label="status: chip-collision
245
+ orientation: y-" data-x="-0.7000000000000001" data-y="-0.02509999999999997" x="496.0212201591512" y="342.08269204244027" width="14.85411140583551" height="33.42175066313001" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
246
+ </g>
247
+ <g>
248
+ <rect data-type="rect" data-label="status: trace-collision
249
+ orientation: y+" data-x="-3.1700000000000004" data-y="0.42510000000000003" x="312.57294429708224" y="308.6460872679045" width="14.85411140583551" height="33.421750663129956" fill="rgba(220, 140, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
250
+ </g>
251
+ <g>
252
+ <rect data-type="rect" data-label="status: chip-collision
253
+ orientation: y-" data-x="-3.1700000000000004" data-y="-0.025099999999999983" x="312.57294429708224" y="342.08269204244027" width="14.85411140583551" height="33.42175066313001" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
254
+ </g>
255
+ <g>
256
+ <rect data-type="rect" data-label="status: chip-collision
257
+ orientation: y+" data-x="-5.640000000000001" data-y="0.4251" x="129.1246684350133" y="308.6460872679045" width="14.85411140583551" height="33.42175066313001" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
258
+ </g>
259
+ <g>
260
+ <rect data-type="rect" data-label="status: chip-collision
261
+ orientation: y-" data-x="-5.640000000000001" data-y="-0.025100000000000025" x="129.1246684350133" y="342.08269204244027" width="14.85411140583551" height="33.42175066313001" fill="rgba(220, 0, 0, 0.25)" stroke="black" stroke-width="0.013464285714285717" />
262
+ </g>
263
+ <g id="crosshair" style="display: none">
264
+ <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
265
+ <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>
266
+ </g>
267
+ <script>
268
+ <![CDATA[
269
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
270
+ const svg = e.currentTarget;
271
+ const rect = svg.getBoundingClientRect();
272
+ const x = e.clientX - rect.left;
273
+ const y = e.clientY - rect.top;
274
+ const crosshair = svg.getElementById('crosshair');
275
+ const h = svg.getElementById('crosshair-h');
276
+ const v = svg.getElementById('crosshair-v');
277
+ const coords = svg.getElementById('coordinates');
278
+
279
+ crosshair.style.display = 'block';
280
+ h.setAttribute('x1', '0');
281
+ h.setAttribute('x2', '640');
282
+ h.setAttribute('y1', y);
283
+ h.setAttribute('y2', y);
284
+ v.setAttribute('x1', x);
285
+ v.setAttribute('x2', x);
286
+ v.setAttribute('y1', '0');
287
+ v.setAttribute('y2', '640');
288
+
289
+ // Calculate real coordinates using inverse transformation
290
+ const matrix = {
291
+ "a": 74.27055702917771,
292
+ "c": 0,
293
+ "e": 555.4376657824934,
294
+ "b": 0,
295
+ "d": -74.27055702917771,
296
+ "f": 356.9293763925729
297
+ };
298
+ // Manually invert and apply the affine transform
299
+ // Since we only use translate and scale, we can directly compute:
300
+ // x' = (x - tx) / sx
301
+ // y' = (y - ty) / sy
302
+ const sx = matrix.a;
303
+ const sy = matrix.d;
304
+ const tx = matrix.e;
305
+ const ty = matrix.f;
306
+ const realPoint = {
307
+ x: (x - tx) / sx,
308
+ y: (y - ty) / sy // Flip y back since we used negative scale
309
+ }
310
+
311
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
312
+ coords.setAttribute('x', (x + 5).toString());
313
+ coords.setAttribute('y', (y - 5).toString());
314
+ });
315
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
316
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
317
+ });
318
+ ]]>
319
+ </script>
320
+ </svg>
@@ -0,0 +1,77 @@
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="TP1.1
5
+ y+" data-x="-1.2246467991473533e-17" data-y="0.2" cx="440.48192771084337" cy="88.19277108433738" r="3" fill="hsl(128, 100%, 50%, 0.8)" />
6
+ </g>
7
+ <g>
8
+ <circle data-type="point" data-label="" data-x="-0.25" data-y="0.14900000000000002" cx="199.5180722891566" cy="137.34939759036146" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
9
+ </g>
10
+ <g>
11
+ <polyline data-points="-1.2246467991473533e-17,0.2 -1.2246467991473533e-17,0.25 -0.25,0.25 -0.25,0.14900000000000002" data-type="line" data-label="" points="440.48192771084337,88.19277108433738 440.48192771084337,40.00000000000003 199.5180722891566,40.00000000000003 199.5180722891566,137.34939759036146" fill="none" stroke="purple" stroke-width="1" />
12
+ </g>
13
+ <g>
14
+ <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="344.09638554216866" y="88.19277108433738" width="192.77108433734935" height="385.5421686746988" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0010375" />
15
+ </g>
16
+ <g>
17
+ <rect data-type="rect" data-label="netId: GND
18
+ globalConnNetId: connectivity_net0" data-x="-0.25" data-y="-0.09099999999999997" x="103.13253012048193" y="137.34939759036146" width="192.77108433734935" height="462.6506024096385" fill="#00000066" stroke="#000000" stroke-width="0.0010375" />
19
+ </g>
20
+ <g id="crosshair" style="display: none">
21
+ <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
22
+ <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>
23
+ </g>
24
+ <script>
25
+ <![CDATA[
26
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
27
+ const svg = e.currentTarget;
28
+ const rect = svg.getBoundingClientRect();
29
+ const x = e.clientX - rect.left;
30
+ const y = e.clientY - rect.top;
31
+ const crosshair = svg.getElementById('crosshair');
32
+ const h = svg.getElementById('crosshair-h');
33
+ const v = svg.getElementById('crosshair-v');
34
+ const coords = svg.getElementById('coordinates');
35
+
36
+ crosshair.style.display = 'block';
37
+ h.setAttribute('x1', '0');
38
+ h.setAttribute('x2', '640');
39
+ h.setAttribute('y1', y);
40
+ h.setAttribute('y2', y);
41
+ v.setAttribute('x1', x);
42
+ v.setAttribute('x2', x);
43
+ v.setAttribute('y1', '0');
44
+ v.setAttribute('y2', '640');
45
+
46
+ // Calculate real coordinates using inverse transformation
47
+ const matrix = {
48
+ "a": 963.855421686747,
49
+ "c": 0,
50
+ "e": 440.48192771084337,
51
+ "b": 0,
52
+ "d": -963.855421686747,
53
+ "f": 280.9638554216868
54
+ };
55
+ // Manually invert and apply the affine transform
56
+ // Since we only use translate and scale, we can directly compute:
57
+ // x' = (x - tx) / sx
58
+ // y' = (y - ty) / sy
59
+ const sx = matrix.a;
60
+ const sy = matrix.d;
61
+ const tx = matrix.e;
62
+ const ty = matrix.f;
63
+ const realPoint = {
64
+ x: (x - tx) / sx,
65
+ y: (y - ty) / sy // Flip y back since we used negative scale
66
+ }
67
+
68
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
69
+ coords.setAttribute('x', (x + 5).toString());
70
+ coords.setAttribute('y', (y - 5).toString());
71
+ });
72
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
73
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
74
+ });
75
+ ]]>
76
+ </script>
77
+ </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/example37.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ test("example37", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
12
+ })
@@ -0,0 +1,12 @@
1
+ import { test, expect } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "../assets/example38.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ test("example38", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
12
+ })