@tscircuit/schematic-trace-solver 0.0.201 → 0.0.202

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
@@ -1149,6 +1149,8 @@ type Candidate = {
1149
1149
  status: CandidateStatus | null;
1150
1150
  };
1151
1151
  interface NetLabelNetLabelCollisionSolverParams {
1152
+ /** Move temporary port-only inline labels before moving their neighbors. */
1153
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions?: boolean;
1152
1154
  /** Use the final horizontal tag envelope after inline conversion. */
1153
1155
  useRenderedLabelBounds?: boolean;
1154
1156
  /** Already placed labels that are obstacles, but must not be moved. */
@@ -1181,6 +1183,10 @@ declare class NetLabelNetLabelCollisionSolver extends BaseSolver {
1181
1183
  };
1182
1184
  private labelBounds;
1183
1185
  private collisionKey;
1186
+ private isSingleTerminalInlineEligible;
1187
+ private hasSatisfiedSingleOrientation;
1188
+ private isAlignedWithHostTrace;
1189
+ private isProtectedAgainstTemporaryInlineCollision;
1184
1190
  private findNextCollidingPair;
1185
1191
  private netLabelWidthOf;
1186
1192
  private netLabelHeightOf;
package/dist/index.js CHANGED
@@ -12776,7 +12776,8 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
12776
12776
  traces: this.traces,
12777
12777
  netLabelPlacements: this.netLabelPlacements,
12778
12778
  fixedNetLabelPlacements: this.params.fixedNetLabelPlacements,
12779
- useRenderedLabelBounds: this.params.useRenderedLabelBounds
12779
+ useRenderedLabelBounds: this.params.useRenderedLabelBounds,
12780
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions
12780
12781
  };
12781
12782
  }
12782
12783
  getOutput() {
@@ -12790,6 +12791,36 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
12790
12791
  collisionKey(a, b) {
12791
12792
  return [a.globalConnNetId, b.globalConnNetId].sort().join("::");
12792
12793
  }
12794
+ isSingleTerminalInlineEligible(label) {
12795
+ if (!label.netId) return false;
12796
+ return [
12797
+ ...this.inputProblem.netConnections,
12798
+ ...this.inputProblem.directConnections
12799
+ ].some(
12800
+ (connection) => connection.allowInlineNetLabel && connection.pinIds.length === 1 && connection.netId === label.netId && label.pinIds.every((pinId) => connection.pinIds.includes(pinId))
12801
+ );
12802
+ }
12803
+ hasSatisfiedSingleOrientation(label) {
12804
+ if (!label.netId) return false;
12805
+ const orientations = this.inputProblem.availableNetLabelOrientations[label.netId];
12806
+ return orientations?.length === 1 && orientations[0] === label.orientation;
12807
+ }
12808
+ isAlignedWithHostTrace(label) {
12809
+ const orientationAxis = label.orientation.startsWith("x") ? "x" : "y";
12810
+ return label.mspConnectionPairIds.some((mspPairId) => {
12811
+ const trace = this.traceMap[mspPairId];
12812
+ return trace?.tracePath.slice(1).some((end, index) => {
12813
+ const start = trace.tracePath[index];
12814
+ if (!tracePathContainsPoint([start, end], label.anchorPoint))
12815
+ return false;
12816
+ const segmentAxis = Math.abs(start.x - end.x) < SEGMENT_PARALLEL_EPS ? "y" : "x";
12817
+ return segmentAxis === orientationAxis;
12818
+ });
12819
+ });
12820
+ }
12821
+ isProtectedAgainstTemporaryInlineCollision(label) {
12822
+ return !this.isSingleTerminalInlineEligible(label) && this.hasSatisfiedSingleOrientation(label) && this.isAlignedWithHostTrace(label);
12823
+ }
12793
12824
  findNextCollidingPair() {
12794
12825
  const labels = [
12795
12826
  ...this.outputNetLabelPlacements,
@@ -12962,6 +12993,18 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
12962
12993
  this.labelsToTry = [pair[1], pair[0]].filter(
12963
12994
  (label) => !(this.params.fixedNetLabelPlacements ?? []).includes(label)
12964
12995
  );
12996
+ if (this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions && pair.some((label) => this.isSingleTerminalInlineEligible(label)) && pair.some(
12997
+ (label) => this.isProtectedAgainstTemporaryInlineCollision(label)
12998
+ )) {
12999
+ this.labelsToTry = this.labelsToTry.filter(
13000
+ (label) => this.isSingleTerminalInlineEligible(label) || !this.isProtectedAgainstTemporaryInlineCollision(label)
13001
+ );
13002
+ }
13003
+ if (this.labelsToTry.length === 0) {
13004
+ this.skippedCollisionKeys.add(this.collisionKey(pair[0], pair[1]));
13005
+ this.clearActiveSearch();
13006
+ return;
13007
+ }
12965
13008
  this.beginSearchForLabel(this.labelsToTry.shift());
12966
13009
  return;
12967
13010
  }
@@ -15628,6 +15671,11 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15628
15671
  minY: label.center.y - label.height / 2,
15629
15672
  maxY: label.center.y + label.height / 2
15630
15673
  }));
15674
+ const pendingReplacementBounds = output.netLabelPlacements.filter(
15675
+ (label) => pendingInlinePlacements.some(
15676
+ (placement) => placement.globalConnNetId === label.globalConnNetId && placement.pinIds.length === label.pinIds.length && placement.pinIds.every((pinId) => label.pinIds.includes(pinId))
15677
+ )
15678
+ ).map(getAnchoredNetLabelRenderedBounds);
15631
15679
  const pinPositions = [
15632
15680
  ...inputProblem.chips.flatMap((chip) => chip.pins),
15633
15681
  ...output.traces.flatMap((trace) => trace.pins)
@@ -15639,13 +15687,26 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15639
15687
  const axis = Math.abs(a.x - b.x) < EPS16 ? "x" : "y";
15640
15688
  const along = axis === "x" ? "y" : "x";
15641
15689
  if (Math.abs(a[axis] - b[axis]) > EPS16 || same(a, b)) continue;
15642
- const blocking = [
15690
+ const candidateObstacles = [
15643
15691
  ...output.netLabelPlacements.filter((label) => label.globalConnNetId !== trace.globalConnNetId).map(getAnchoredNetLabelRenderedBounds),
15644
15692
  ...inlineBounds,
15645
15693
  ...terminalBounds.filter(
15646
15694
  ({ label }) => !trace.pinIds.some((pinId) => label.pinIds.includes(pinId))
15647
15695
  ).map(({ bounds }) => bounds)
15648
- ].filter((bounds) => segmentIntersectsRect(a, b, bounds));
15696
+ ];
15697
+ const attachedLabelBounds = output.netLabelPlacements.filter(
15698
+ (label) => label.globalConnNetId === trace.globalConnNetId && label.netId != null && inputProblem.availableNetLabelOrientations[label.netId]?.length === 1 && inputProblem.availableNetLabelOrientations[label.netId]?.[0] === label.orientation && (axis === "x" ? label.orientation === "y+" || label.orientation === "y-" : label.orientation === "x+" || label.orientation === "x-") && tracePathContainsPoint([a, b], label.anchorPoint)
15699
+ ).map(getAnchoredNetLabelRenderedBounds);
15700
+ const blocking = [
15701
+ ...candidateObstacles.filter(
15702
+ (bounds) => segmentIntersectsRect(a, b, bounds)
15703
+ ),
15704
+ ...pendingReplacementBounds.filter(
15705
+ (bounds) => attachedLabelBounds.some(
15706
+ (labelBounds) => boundsOverlap(labelBounds, bounds)
15707
+ )
15708
+ )
15709
+ ];
15649
15710
  if (!blocking.length) continue;
15650
15711
  let low = Math.min(a[along], b[along]);
15651
15712
  let high = Math.max(a[along], b[along]);
@@ -17559,7 +17620,8 @@ var InlineNetLabelSolver = class _InlineNetLabelSolver extends BaseSolver {
17559
17620
  netLabelPlacements: retainedNetLabelPlacements,
17560
17621
  fixedNetLabelPlacements: fixedLabels,
17561
17622
  netLabelConnectorTraceIds: this.netLabelConnectorTraceIds,
17562
- useRenderedLabelBounds: activeInlinePlacements.length > 0
17623
+ useRenderedLabelBounds: activeInlinePlacements.length > 0,
17624
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true
17563
17625
  });
17564
17626
  if (activeInlinePlacements.length > 0) labelCollisionSolver.solve();
17565
17627
  const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
@@ -19010,7 +19072,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
19010
19072
  {
19011
19073
  inputProblem: instance.inputProblem,
19012
19074
  traces: simplificationOutput.traces,
19013
- netLabelPlacements: simplificationOutput.netLabelPlacements
19075
+ netLabelPlacements: simplificationOutput.netLabelPlacements,
19076
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true
19014
19077
  }
19015
19078
  ];
19016
19079
  }
@@ -1195,6 +1195,7 @@ export class InlineNetLabelSolver extends BaseSolver {
1195
1195
  fixedNetLabelPlacements: fixedLabels,
1196
1196
  netLabelConnectorTraceIds: this.netLabelConnectorTraceIds,
1197
1197
  useRenderedLabelBounds: activeInlinePlacements.length > 0,
1198
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true,
1198
1199
  })
1199
1200
  if (activeInlinePlacements.length > 0) labelCollisionSolver.solve()
1200
1201
  const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
@@ -75,6 +75,16 @@ export function* getLocalTraceLabelShifts(
75
75
  minY: label.center.y - label.height / 2,
76
76
  maxY: label.center.y + label.height / 2,
77
77
  }))
78
+ const pendingReplacementBounds = output.netLabelPlacements
79
+ .filter((label) =>
80
+ pendingInlinePlacements.some(
81
+ (placement) =>
82
+ placement.globalConnNetId === label.globalConnNetId &&
83
+ placement.pinIds.length === label.pinIds.length &&
84
+ placement.pinIds.every((pinId) => label.pinIds.includes(pinId)),
85
+ ),
86
+ )
87
+ .map(getAnchoredNetLabelRenderedBounds)
78
88
  const pinPositions = [
79
89
  ...inputProblem.chips.flatMap((chip) => chip.pins),
80
90
  ...output.traces.flatMap((trace) => trace.pins),
@@ -87,7 +97,7 @@ export function* getLocalTraceLabelShifts(
87
97
  const axis = Math.abs(a.x - b.x) < EPS ? "x" : "y"
88
98
  const along = axis === "x" ? "y" : "x"
89
99
  if (Math.abs(a[axis] - b[axis]) > EPS || same(a, b)) continue
90
- const blocking = [
100
+ const candidateObstacles = [
91
101
  ...output.netLabelPlacements
92
102
  .filter((label) => label.globalConnNetId !== trace.globalConnNetId)
93
103
  .map(getAnchoredNetLabelRenderedBounds),
@@ -98,7 +108,32 @@ export function* getLocalTraceLabelShifts(
98
108
  !trace.pinIds.some((pinId) => label.pinIds.includes(pinId)),
99
109
  )
100
110
  .map(({ bounds }) => bounds),
101
- ].filter((bounds) => segmentIntersectsRect(a, b, bounds))
111
+ ]
112
+ const attachedLabelBounds = output.netLabelPlacements
113
+ .filter(
114
+ (label) =>
115
+ label.globalConnNetId === trace.globalConnNetId &&
116
+ label.netId != null &&
117
+ inputProblem.availableNetLabelOrientations[label.netId]?.length ===
118
+ 1 &&
119
+ inputProblem.availableNetLabelOrientations[label.netId]?.[0] ===
120
+ label.orientation &&
121
+ (axis === "x"
122
+ ? label.orientation === "y+" || label.orientation === "y-"
123
+ : label.orientation === "x+" || label.orientation === "x-") &&
124
+ tracePathContainsPoint([a, b], label.anchorPoint),
125
+ )
126
+ .map(getAnchoredNetLabelRenderedBounds)
127
+ const blocking = [
128
+ ...candidateObstacles.filter((bounds) =>
129
+ segmentIntersectsRect(a, b, bounds),
130
+ ),
131
+ ...pendingReplacementBounds.filter((bounds) =>
132
+ attachedLabelBounds.some((labelBounds) =>
133
+ boundsOverlap(labelBounds, bounds),
134
+ ),
135
+ ),
136
+ ]
102
137
  if (!blocking.length) continue
103
138
  // Cleanup can split one rail across several trace records, including a
104
139
  // branch that starts at a junction instead of at its pin. Shift the
@@ -93,6 +93,8 @@ function sampleAnchorsAlongSegment(
93
93
  }
94
94
 
95
95
  export interface NetLabelNetLabelCollisionSolverParams {
96
+ /** Move temporary port-only inline labels before moving their neighbors. */
97
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions?: boolean
96
98
  /** Use the final horizontal tag envelope after inline conversion. */
97
99
  useRenderedLabelBounds?: boolean
98
100
  /** Already placed labels that are obstacles, but must not be moved. */
@@ -148,6 +150,8 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
148
150
  netLabelPlacements: this.netLabelPlacements,
149
151
  fixedNetLabelPlacements: this.params.fixedNetLabelPlacements,
150
152
  useRenderedLabelBounds: this.params.useRenderedLabelBounds,
153
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions:
154
+ this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions,
151
155
  }
152
156
  }
153
157
 
@@ -168,6 +172,50 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
168
172
  return [a.globalConnNetId, b.globalConnNetId].sort().join("::")
169
173
  }
170
174
 
175
+ private isSingleTerminalInlineEligible(label: NetLabelPlacement) {
176
+ if (!label.netId) return false
177
+ return [
178
+ ...this.inputProblem.netConnections,
179
+ ...this.inputProblem.directConnections,
180
+ ].some(
181
+ (connection) =>
182
+ connection.allowInlineNetLabel &&
183
+ connection.pinIds.length === 1 &&
184
+ connection.netId === label.netId &&
185
+ label.pinIds.every((pinId) => connection.pinIds.includes(pinId)),
186
+ )
187
+ }
188
+
189
+ private hasSatisfiedSingleOrientation(label: NetLabelPlacement) {
190
+ if (!label.netId) return false
191
+ const orientations =
192
+ this.inputProblem.availableNetLabelOrientations[label.netId]
193
+ return orientations?.length === 1 && orientations[0] === label.orientation
194
+ }
195
+
196
+ private isAlignedWithHostTrace(label: NetLabelPlacement) {
197
+ const orientationAxis = label.orientation.startsWith("x") ? "x" : "y"
198
+ return label.mspConnectionPairIds.some((mspPairId) => {
199
+ const trace = this.traceMap[mspPairId]
200
+ return trace?.tracePath.slice(1).some((end, index) => {
201
+ const start = trace.tracePath[index]!
202
+ if (!tracePathContainsPoint([start, end], label.anchorPoint))
203
+ return false
204
+ const segmentAxis =
205
+ Math.abs(start.x - end.x) < SEGMENT_PARALLEL_EPS ? "y" : "x"
206
+ return segmentAxis === orientationAxis
207
+ })
208
+ })
209
+ }
210
+
211
+ private isProtectedAgainstTemporaryInlineCollision(label: NetLabelPlacement) {
212
+ return (
213
+ !this.isSingleTerminalInlineEligible(label) &&
214
+ this.hasSatisfiedSingleOrientation(label) &&
215
+ this.isAlignedWithHostTrace(label)
216
+ )
217
+ }
218
+
171
219
  private findNextCollidingPair():
172
220
  | [NetLabelPlacement, NetLabelPlacement]
173
221
  | null {
@@ -394,6 +442,24 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
394
442
  this.labelsToTry = [pair[1], pair[0]].filter(
395
443
  (label) => !(this.params.fixedNetLabelPlacements ?? []).includes(label),
396
444
  )
445
+ if (
446
+ this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions &&
447
+ pair.some((label) => this.isSingleTerminalInlineEligible(label)) &&
448
+ pair.some((label) =>
449
+ this.isProtectedAgainstTemporaryInlineCollision(label),
450
+ )
451
+ ) {
452
+ this.labelsToTry = this.labelsToTry.filter(
453
+ (label) =>
454
+ this.isSingleTerminalInlineEligible(label) ||
455
+ !this.isProtectedAgainstTemporaryInlineCollision(label),
456
+ )
457
+ }
458
+ if (this.labelsToTry.length === 0) {
459
+ this.skippedCollisionKeys.add(this.collisionKey(pair[0], pair[1]))
460
+ this.clearActiveSearch()
461
+ return
462
+ }
397
463
  this.beginSearchForLabel(this.labelsToTry.shift()!)
398
464
  return
399
465
  }
@@ -559,6 +559,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
559
559
  inputProblem: instance.inputProblem,
560
560
  traces: simplificationOutput.traces,
561
561
  netLabelPlacements: simplificationOutput.netLabelPlacements,
562
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true,
562
563
  },
563
564
  ]
564
565
  },
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
6
  },
7
7
  "main": "dist/index.js",
8
- "version": "0.0.201",
8
+ "version": "0.0.202",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",
@@ -1,20 +1,20 @@
1
1
  <svg width="1200" height="2016" viewBox="0 0 1200 2016" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Solver debug visualization on top and Circuit JSON schematic on the bottom">
2
2
  <g transform="translate(0, 0) scale(1.875, 1.875) translate(0, 0)">
3
- <rect width="100%" height="100%" fill="white"/><g><polyline data-points="-8.690000000000001,4.1 -9.35,-3.9" data-type="line" data-label="" points="296.875,67.5 255.6250000000001,567.5" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-8.690000000000001,4.1 -9.751000000000001,4.1 -9.751000000000001,-3.9 -9.35,-3.9" data-type="line" data-label="" points="296.875,67.5 230.5625,67.5 230.5625,567.5 255.6250000000001,567.5" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-8.979363636363638,4.1 -8.979363636363638,4.1198999999999995" data-type="line" data-label="" points="278.78977272727263,67.5 278.78977272727263,66.25625000000002" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-8.690000000000001,3.9 -9.530000000000001,3.9" data-type="line" data-label="" points="296.875,79.99999999999997 244.375,79.99999999999997" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-9.35,-4.1 -10.03,-4.1" data-type="line" data-label="" points="255.6250000000001,580 213.1250000000001,580" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="-7.690000000000001" data-y="4" x="296.875" y="61.24999999999996" width="125.00000000000006" height="25.00000000000003" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="-8.35" data-y="-4" x="255.62500000000014" y="561.25" width="124.99999999999994" height="25" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="D2FC-F-7N(20M)" data-x="-7.450000000000001" data-y="3.67" x="321.875" y="88.74999999999999" width="105" height="11.249999999999972" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="SW_LEFT" data-x="-7.870000000000001" data-y="4.3149999999999995" x="318.125" y="46.25" width="60" height="15.625" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="D2FC-F-7N(20M)" data-x="-8.11" data-y="-4.33" x="280.6250000000001" y="588.75" width="105" height="11.25" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="SW_RIGHT" data-x="-8.469999999999999" data-y="-3.6849999999999996" x="276.87500000000017" y="546.25" width="67.49999999999989" height="15.625" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="netId: GND
4
- globalConnNetId: connectivity_net0" data-x="-8.979363636363638" data-y="4.329999999999999" x="263.78977272727263" y="40" width="30" height="26.25" fill="#00000066" stroke="#000000" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="INLINE netId: LEFT_SW
3
+ <rect width="100%" height="100%" fill="white"/><g><polyline data-points="-8.690000000000001,4.1 -9.35,-3.9" data-type="line" data-label="" points="314.62753950338595,61.489841986455986 272.9119638826186,567.1331828442438" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-8.690000000000001,4.1 -10.359999999999998,4.1 -10.359999999999998,-3.9 -9.35,-3.9" data-type="line" data-label="" points="314.62753950338595,61.489841986455986 209.07449209932292,61.489841986455986 209.07449209932292,567.1331828442438 272.9119638826186,567.1331828442438" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-8.690000000000001,3.9 -9.530000000000001,3.9" data-type="line" data-label="" points="314.62753950338595,74.13092550790068 261.53498871331817,74.13092550790068" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-9.35,-4.1 -10.03,-4.1" data-type="line" data-label="" points="272.9119638826186,579.7742663656885 229.9322799097066,579.7742663656885" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="-7.690000000000001" data-y="4" x="314.62753950338595" y="55.16930022573361" width="126.41083521444699" height="25.282167042889427" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="-8.35" data-y="-4" x="272.9119638826186" y="560.8126410835214" width="126.41083521444693" height="25.282167042889455" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="D2FC-F-7N(20M)" data-x="-7.450000000000001" data-y="3.67" x="339.9097065462754" y="82.97968397291196" width="106.18510158013538" height="11.37697516930021" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="SW_LEFT" data-x="-7.870000000000001" data-y="4.3149999999999995" x="336.11738148984193" y="40.00000000000003" width="60.6772009029346" height="15.801354401805895" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="D2FC-F-7N(20M)" data-x="-8.11" data-y="-4.33" x="298.1941309255079" y="588.6230248306997" width="106.1851015801355" height="11.37697516930018" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="SW_RIGHT" data-x="-8.469999999999999" data-y="-3.6849999999999996" x="294.40180586907456" y="545.6433408577878" width="68.26185101580126" height="15.801354401805838" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="netId: GND
4
+ globalConnNetId: connectivity_net0" data-x="-10.359999999999998" data-y="-4.11" x="193.90519187358927" y="567.133182844244" width="30.3386004514673" height="26.54627539503383" fill="#00000066" stroke="#000000" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="INLINE netId: LEFT_SW
5
5
  axis: x
6
- side: y+" data-x="-9.110000000000001" data-y="4.01" x="250.625" y="69.37499999999997" width="40" height="7.5" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.015999999999999997"/></g><g><rect data-type="rect" data-label="INLINE netId: RIGHT
6
+ side: y+" data-x="-9.110000000000001" data-y="4.01" x="267.85553047404056" y="63.386004514672706" width="40.451467268623105" height="7.584650112866797" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.01582142857142857"/></g><g><rect data-type="rect" data-label="INLINE netId: RIGHT
7
7
  axis: x
8
- side: y+" data-x="-9.69" data-y="-3.9899999999999998" x="219.3750000000001" y="569.375" width="30" height="7.5" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.015999999999999997"/></g><text data-type="text" data-label="LEFT_SW" data-x="-8.790000000000001" data-y="4.01" x="290.625" y="73.12499999999997" fill="green" font-size="7.500000000000001" font-family="sans-serif" text-anchor="end" dominant-baseline="central">LEFT_SW</text><text data-type="text" data-label="RIGHT" data-x="-9.45" data-y="-3.9899999999999998" x="249.3750000000001" y="573.125" fill="green" font-size="7.500000000000001" font-family="sans-serif" text-anchor="end" dominant-baseline="central">RIGHT</text><g><circle data-type="point" data-label="1
9
- x-" data-x="-8.690000000000001" data-y="4.1" cx="296.875" cy="67.5" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="2
10
- x-" data-x="-8.690000000000001" data-y="3.9" cx="296.875" cy="79.99999999999997" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="3
11
- x+" data-x="-6.69" data-y="4" cx="421.87500000000006" cy="73.74999999999997" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="1
12
- x-" data-x="-9.35" data-y="-3.9" cx="255.6250000000001" cy="567.5" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="2
13
- x-" data-x="-9.35" data-y="-4.1" cx="255.6250000000001" cy="580" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="3
14
- x+" data-x="-7.35" data-y="-4" cx="380.62500000000006" cy="573.75" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
15
- orientation: y+" data-x="-8.979363636363638" data-y="4.1198999999999995" cx="278.78977272727263" cy="66.25625000000002" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="inline anchor
16
- LEFT_SW" data-x="-9.110000000000001" data-y="3.9" cx="270.625" cy="79.99999999999997" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
17
- RIGHT" data-x="-9.69" data-y="-4.1" cx="234.3750000000001" cy="580" r="3" fill="green"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
8
+ side: y+" data-x="-9.69" data-y="-3.9899999999999998" x="236.25282167042894" y="569.0293453724605" width="30.3386004514673" height="7.584650112866825" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.01582142857142857"/></g><text data-type="text" data-label="LEFT_SW" data-x="-8.790000000000001" data-y="4.01" x="308.3069977426636" y="67.1783295711061" fill="green" font-size="7.584650112866817" font-family="sans-serif" text-anchor="end" dominant-baseline="central">LEFT_SW</text><text data-type="text" data-label="RIGHT" data-x="-9.45" data-y="-3.9899999999999998" x="266.59142212189624" y="572.8216704288939" fill="green" font-size="7.584650112866817" font-family="sans-serif" text-anchor="end" dominant-baseline="central">RIGHT</text><g><circle data-type="point" data-label="1
9
+ x-" data-x="-8.690000000000001" data-y="4.1" cx="314.62753950338595" cy="61.489841986455986" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="2
10
+ x-" data-x="-8.690000000000001" data-y="3.9" cx="314.62753950338595" cy="74.13092550790068" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="3
11
+ x+" data-x="-6.69" data-y="4" cx="441.03837471783294" cy="67.81038374717832" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="1
12
+ x-" data-x="-9.35" data-y="-3.9" cx="272.9119638826186" cy="567.1331828442438" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="2
13
+ x-" data-x="-9.35" data-y="-4.1" cx="272.9119638826186" cy="579.7742663656885" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="3
14
+ x+" data-x="-7.35" data-y="-4" cx="399.3227990970655" cy="573.4537246049662" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
15
+ orientation: y-" data-x="-10.359999999999998" data-y="-3.9" cx="209.07449209932292" cy="567.1331828442438" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="inline anchor
16
+ LEFT_SW" data-x="-9.110000000000001" data-y="3.9" cx="288.0812641083521" cy="74.13092550790068" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
17
+ RIGHT" data-x="-9.69" data-y="-4.1" cx="251.4221218961626" cy="579.7742663656885" r="3" fill="green"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
18
18
  document.currentScript.parentElement.addEventListener('mousemove', (e) => {
19
19
  const svg = e.currentTarget;
20
20
  const rect = svg.getBoundingClientRect();
@@ -36,7 +36,7 @@ RIGHT" data-x="-9.69" data-y="-4.1" cx="234.3750000000001" cy="580" r="3" fill="
36
36
  v.setAttribute('y2', '640');
37
37
 
38
38
  // Calculate real coordinates using inverse transformation
39
- const matrix = {"a":62.50000000000001,"c":0,"e":840.0000000000001,"b":0,"d":-62.50000000000001,"f":323.75};
39
+ const matrix = {"a":63.20541760722348,"c":0,"e":863.8826185101581,"b":0,"d":-63.20541760722348,"f":320.63205417607224};
40
40
  // Manually invert and apply the affine transform
41
41
  // Since we only use translate and scale, we can directly compute:
42
42
  // x' = (x - tx) / sx
@@ -71,13 +71,6 @@ RIGHT" data-x="-9.69" data-y="-4.1" cx="234.3750000000001" cy="580" r="3" fill="
71
71
  .port-label { fill: rgb(0, 100, 100); }
72
72
  .component-name { fill: rgb(0, 100, 100); }
73
73
 
74
- </style><rect class="boundary" x="0" y="0" width="1200" height="800"/><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"><path d="M 555.3177571657768 73.81291826693001 L 469.89819023462815 73.81291826693001 L 469.89819023462815 717.88128344053 L 502.18211703895497 717.88128344053" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.881367303472px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 555.3177571657768 73.81291826693001 L 469.89819023462815 73.81291826693001 L 469.89819023462815 717.88128344053 L 502.18211703895497 717.88128344053" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.610170912934px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_1__stack1"><path d="M 532.0215116390998 73.81291826693001 L 532.0215116390998 72.21079820856067" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.881367303472px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 532.0215116390998 73.81291826693001 L 532.0215116390998 72.21079820856067" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.610170912934px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_2__stack1"><path d="M 555.3177571657768 89.91462739627002 L 487.6905788225488 89.91462739627002" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.881367303472px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 555.3177571657768 89.91462739627002 L 487.6905788225488 89.91462739627002" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.610170912934px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_3__stack1"><path d="M 502.18211703895497 733.98299256987 L 447.436305999199 733.98299256987" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.881367303472px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 502.18211703895497 733.98299256987 L 447.436305999199 733.98299256987" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.610170912934px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"/><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_1__stack1"/><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_2__stack1"/><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_3__stack1"/><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_0__stack1"><rect class="component chip sch-component-body" x="587.5211754244567" y="65.76206370225998" width="96.61025477604016" height="32.20341825868002" stroke-width="1.610170912934px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="587.5211754244567" y="65.76206370225998" width="96.61025477604016" height="32.20341825868002" fill="transparent"/><line class="component-pin sch-component-pin sch-port-line" x1="587.5211754244567" y1="73.81291826693001" x2="555.3177571657768" y2="73.81291826693001" stroke-width="1.610170912934px"/><g class="sch-port" data-schematic-port-id="source_port_0_0__stack1"><rect x="553.7075862528428" y="72.20274735399602" width="3.220341825868" height="3.220341825868" opacity="0"/></g><text class="pin-number sch-pin-number" x="571.4194662951168" y="72.20274735399607" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.076281847005px" transform="">1</text><line class="component-pin sch-component-pin sch-port-line" x1="587.5211754244567" y1="89.91462739627002" x2="555.3177571657768" y2="89.91462739627002" stroke-width="1.610170912934px"/><g class="sch-port" data-schematic-port-id="source_port_0_1__stack1"><rect x="553.7075862528428" y="88.30445648333603" width="3.220341825868" height="3.220341825868" opacity="0"/></g><text class="pin-number sch-pin-number" x="571.4194662951168" y="88.30445648333597" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.076281847005px" transform="">2</text><line class="component-pin sch-component-pin sch-port-line" x1="684.1314302004969" y1="81.86377283159999" x2="714.7246775462429" y2="81.86377283159999" stroke-width="1.610170912934px"/><g class="sch-port" data-schematic-port-id="source_port_0_2__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="716.3348484591769" cy="81.86377283159999" r="1.610170912934" stroke-width="1.610170912934px"/><rect x="714.7246775462429" y="80.25360191866599" width="3.220341825868" height="3.220341825868" opacity="0"/></g><text class="pin-number sch-pin-number" x="700.2331393298368" y="80.25360191866605" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.076281847005px" transform="">3</text></g><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_1__stack1"><rect class="component chip sch-component-body" x="534.385535297635" y="709.83042887586" width="96.61025477603994" height="32.20341825868002" stroke-width="1.610170912934px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="534.385535297635" y="709.83042887586" width="96.61025477603994" height="32.20341825868002" fill="transparent"/><line class="component-pin sch-component-pin sch-port-line" x1="534.385535297635" y1="717.88128344053" x2="502.18211703895497" y2="717.88128344053" stroke-width="1.610170912934px"/><g class="sch-port" data-schematic-port-id="source_port_1_0__stack1"><rect x="500.571946126021" y="716.271112527596" width="3.220341825868" height="3.220341825868" opacity="0"/></g><text class="pin-number sch-pin-number" x="518.2838261682949" y="716.271112527596" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.076281847005px" transform="">1</text><line class="component-pin sch-component-pin sch-port-line" x1="534.385535297635" y1="733.98299256987" x2="502.18211703895497" y2="733.98299256987" stroke-width="1.610170912934px"/><g class="sch-port" data-schematic-port-id="source_port_1_1__stack1"><rect x="500.571946126021" y="732.372821656936" width="3.220341825868" height="3.220341825868" opacity="0"/></g><text class="pin-number sch-pin-number" x="518.2838261682949" y="732.3728216569359" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.076281847005px" transform="">2</text><line class="component-pin sch-component-pin sch-port-line" x1="630.9957900736749" y1="725.9321380051999" x2="661.589037419421" y2="725.9321380051999" stroke-width="1.610170912934px"/><g class="sch-port" data-schematic-port-id="source_port_1_2__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="663.1992083323549" cy="725.9321380051999" r="1.610170912934" stroke-width="1.610170912934px"/><rect x="661.589037419421" y="724.3219670922659" width="3.220341825868" height="3.220341825868" opacity="0"/></g><text class="pin-number sch-pin-number" x="647.097499203015" y="724.321967092266" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.076281847005px" transform="">3</text></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_0__stack1"><circle cx="555.3177571657768" cy="73.81291826693001" r="3.220341825868" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_1__stack1"><circle cx="555.3177571657768" cy="89.91462739627002" r="3.220341825868" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_2__stack1"><circle cx="716.3348484591769" cy="81.86377283159999" r="3.220341825868" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_0__stack1"><circle cx="502.18211703895497" cy="717.88128344053" r="3.220341825868" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_1__stack1"><circle cx="502.18211703895497" cy="733.98299256987" r="3.220341825868" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_2__stack1"><circle cx="663.1992083323549" cy="725.9321380051999" r="3.220341825868" fill="red" opacity="0"/></g><path class="net-label sch-net-label" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" d="
75
- M 532.0215116390998,72.21079820856067
76
- L 523.9706570744298,67.86333674363887
77
- L 523.9706570744298,40.254272823197226
78
- L 540.0723662037698,40.254272823197226
79
- L 540.0723662037698,67.86333674363888
80
- Z
81
- " fill="rgba(255, 255, 255, 0.6)" stroke="rgb(132, 0, 0)" stroke-width="1.610170912934px"/><text class="net-label-text sch-net-label-text" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" x="532.0215116390998" y="64.96502910035767" fill="rgb(132, 0, 0)" text-anchor="start" dominant-baseline="central" font-family="sans-serif" font-variant-numeric="tabular-nums" font-size="14.491538216406px" transform="rotate(-90 532.0215116390998 64.96502910035767)">XX</text><text class="sch-text" x="655.1483537676849" y="108.43159289501102" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.491538216405978px" transform="rotate(0, 655.1483537676849, 108.43159289501102)">D2FC-F-7N(20M)</text><text class="sch-text" x="621.3347645960708" y="56.503580952889536" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.491538216406px" transform="rotate(0, 621.3347645960708, 56.503580952889536)">SW_LEFT</text><text class="sch-text" x="602.012713640863" y="752.499958068611" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.491538216405978px" transform="rotate(0, 602.012713640863, 752.499958068611)">D2FC-F-7N(20M)</text><text class="sch-text" x="573.029637208051" y="700.5719461264895" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.491538216406px" transform="rotate(0, 573.029637208051, 700.5719461264895)">SW_RIGHT</text><text class="sch-text" x="521.5041679941628" y="81.05868737513299" fill="#047857" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="9.661025477604px" transform="rotate(0, 521.5041679941628, 81.05868737513299)">XXXXXXX</text><text class="sch-text" x="474.80921151907694" y="725.127052548733" fill="#047857" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="9.661025477604px" transform="rotate(0, 474.80921151907694, 725.127052548733)">XXXXX</text><rect class="schematic-box sch-box" x="587.5211754244568" y="101.18582378680799" width="135.25435668645605" height="14.491538216406013" stroke-width="1.610170912934px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.881367303472 6.440683651736"/><rect class="schematic-box sch-box" x="582.6906626856548" y="46.440012747052094" width="77.288203820832" height="20.12713641167494" stroke-width="1.610170912934px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.881367303472 6.440683651736"/><rect class="schematic-box sch-box" x="534.385535297635" y="745.254188960408" width="135.25435668645594" height="14.491538216405957" stroke-width="1.610170912934px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.881367303472 6.440683651736"/><rect class="schematic-box sch-box" x="529.5550225588331" y="690.508377920652" width="86.94922929843597" height="20.127136411674883" stroke-width="1.610170912934px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.881367303472 6.440683651736"/>
74
+ </style><rect class="boundary" x="0" y="0" width="1200" height="800"/><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"><path d="M 570.3853955376389 68.15415821511004 L 434.88843813401627 68.15415821511004 L 434.88843813401627 717.24137931031 L 516.8356997972851 717.24137931031" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.981744421904px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 570.3853955376389 68.15415821511004 L 434.88843813401627 68.15415821511004 L 434.88843813401627 717.24137931031 L 516.8356997972851 717.24137931031" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.622718052738px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_1__stack1"><path d="M 570.3853955376389 84.38133874249002 L 502.231237322643 84.38133874249002" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.981744421904px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 570.3853955376389 84.38133874249002 L 502.231237322643 84.38133874249002" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.622718052738px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_2__stack1"><path d="M 516.8356997972851 733.46855983769 L 461.6632860041931 733.46855983769" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="12.981744421904px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 516.8356997972851 733.46855983769 L 461.6632860041931 733.46855983769" stroke="rgb(0, 150, 0)" fill="none" stroke-width="1.622718052738px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"/><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_1__stack1"/><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_2__stack1"/><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_0__stack1"><rect class="component chip sch-component-body" x="602.8397565923989" y="60.04056795141997" width="97.36308316428017" height="32.45436105476006" stroke-width="1.622718052738px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="602.8397565923989" y="60.04056795141997" width="97.36308316428017" height="32.45436105476006" fill="transparent"/><line class="component-pin sch-component-pin sch-port-line" x1="602.8397565923989" y1="68.15415821511004" x2="570.3853955376389" y2="68.15415821511004" stroke-width="1.622718052738px"/><g class="sch-port" data-schematic-port-id="source_port_0_0__stack1"><rect x="568.7626774849009" y="66.53144016237205" width="3.245436105476" height="3.245436105476" opacity="0"/></g><text class="pin-number sch-pin-number" x="586.612576065019" y="66.53144016237206" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.170385395535px" transform="">1</text><line class="component-pin sch-component-pin sch-port-line" x1="602.8397565923989" y1="84.38133874249002" x2="570.3853955376389" y2="84.38133874249002" stroke-width="1.622718052738px"/><g class="sch-port" data-schematic-port-id="source_port_0_1__stack1"><rect x="568.7626774849009" y="82.75862068975202" width="3.245436105476" height="3.245436105476" opacity="0"/></g><text class="pin-number sch-pin-number" x="586.612576065019" y="82.75862068975204" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.170385395535px" transform="">2</text><line class="component-pin sch-component-pin sch-port-line" x1="700.202839756679" y1="76.2677484788" x2="731.0344827587011" y2="76.2677484788" stroke-width="1.622718052738px"/><g class="sch-port" data-schematic-port-id="source_port_0_2__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="732.657200811439" cy="76.2677484788" r="1.622718052738" stroke-width="1.622718052738px"/><rect x="731.034482758701" y="74.64503042606201" width="3.245436105476" height="3.245436105476" opacity="0"/></g><text class="pin-number sch-pin-number" x="716.4300202840591" y="74.64503042606202" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.170385395535px" transform="">3</text></g><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_1__stack1"><rect class="component chip sch-component-body" x="549.2900608520451" y="709.1277890466199" width="97.36308316427994" height="32.45436105476006" stroke-width="1.622718052738px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="549.2900608520451" y="709.1277890466199" width="97.36308316427994" height="32.45436105476006" fill="transparent"/><line class="component-pin sch-component-pin sch-port-line" x1="549.2900608520451" y1="717.24137931031" x2="516.8356997972851" y2="717.24137931031" stroke-width="1.622718052738px"/><g class="sch-port" data-schematic-port-id="source_port_1_0__stack1"><rect x="515.212981744547" y="715.618661257572" width="3.245436105476" height="3.245436105476" opacity="0"/></g><text class="pin-number sch-pin-number" x="533.062880324665" y="715.6186612575721" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.170385395535px" transform="">1</text><line class="component-pin sch-component-pin sch-port-line" x1="549.2900608520451" y1="733.46855983769" x2="516.8356997972851" y2="733.46855983769" stroke-width="1.622718052738px"/><g class="sch-port" data-schematic-port-id="source_port_1_1__stack1"><rect x="515.212981744547" y="731.845841784952" width="3.245436105476" height="3.245436105476" opacity="0"/></g><text class="pin-number sch-pin-number" x="533.062880324665" y="731.845841784952" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.170385395535px" transform="">2</text><line class="component-pin sch-component-pin sch-port-line" x1="646.6531440163251" y1="725.354969574" x2="677.4847870183471" y2="725.354969574" stroke-width="1.622718052738px"/><g class="sch-port" data-schematic-port-id="source_port_1_2__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="679.1075050710851" cy="725.354969574" r="1.622718052738" stroke-width="1.622718052738px"/><rect x="677.4847870183471" y="723.732251521262" width="3.245436105476" height="3.245436105476" opacity="0"/></g><text class="pin-number sch-pin-number" x="662.8803245437051" y="723.732251521262" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="12.170385395535px" transform="">3</text></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_0__stack1"><circle cx="570.3853955376389" cy="68.15415821511004" r="3.245436105476" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_1__stack1"><circle cx="570.3853955376389" cy="84.38133874249002" r="3.245436105476" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_2__stack1"><circle cx="732.657200811439" cy="76.2677484788" r="3.245436105476" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_0__stack1"><circle cx="516.8356997972851" cy="717.24137931031" r="3.245436105476" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_1__stack1"><circle cx="516.8356997972851" cy="733.46855983769" r="3.245436105476" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_2__stack1"><circle cx="679.1075050710851" cy="725.354969574" r="3.245436105476" fill="red" opacity="0"/></g><rect class="component-overlay sch-component-overlay sch-net-label-overlay" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" x="425.1521298175883" y="717.24137931031" width="19.47261663285599" height="14.604462474642105" fill="transparent"/><path class="sch-net-label-symbol-path" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" d="M 425.1521298175883 731.8458417849521 L 444.62474645044426 731.8458417849521" stroke="rgb(132, 0, 0)" fill="none" stroke-width="1.622718052738px" stroke-linecap="round"/><path class="sch-net-label-symbol-path" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" d="M 434.88843813401627 731.8458417849521 L 434.88843813401627 717.24137931031" stroke="rgb(132, 0, 0)" fill="none" stroke-width="1.622718052738px" stroke-linecap="round"/><text class="sch-net-label-symbol-text" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" x="434.88843813401627" y="735.9026369167971" fill="rgb(15, 15, 15)" font-family="sans-serif" text-anchor="middle" dominant-baseline="hanging" font-size="14.604462474641998px">XX</text><text class="sch-text" x="670.993914807395" y="103.04259634897699" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.604462474641977px" transform="rotate(0, 670.993914807395, 103.04259634897699)">D2FC-F-7N(20M)</text><text class="sch-text" x="636.916835699897" y="50.709939148176545" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.604462474641998px" transform="rotate(0, 636.916835699897, 50.709939148176545)">SW_LEFT</text><text class="sch-text" x="617.4442190670411" y="752.129817444177" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.604462474641977px" transform="rotate(0, 617.4442190670411, 752.129817444177)">D2FC-F-7N(20M)</text><text class="sch-text" x="588.2352941177571" y="699.7971602433765" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="14.604462474641998px" transform="rotate(0, 588.2352941177571, 699.7971602433765)">SW_RIGHT</text><text class="sch-text" x="536.308316430141" y="75.45638945243104" fill="#047857" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="9.736308316428px" transform="rotate(0, 536.308316430141, 75.45638945243104)">XXXXXXX</text><text class="sch-text" x="489.24949290073914" y="724.543610547631" fill="#047857" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="9.736308316428px" transform="rotate(0, 489.24949290073914, 724.543610547631)">XXXXX</text><rect class="schematic-box sch-box" x="602.839756592399" y="95.74036511165605" width="136.30831642999203" height="14.604462474641934" stroke-width="1.622718052738px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.981744421904 6.490872210952"/><rect class="schematic-box sch-box" x="597.971602434185" y="40.5679513185641" width="77.89046653142395" height="20.28397565922495" stroke-width="1.622718052738px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.981744421904 6.490872210952"/><rect class="schematic-box sch-box" x="549.2900608520451" y="744.827586206856" width="136.30831642999192" height="14.604462474641878" stroke-width="1.622718052738px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.981744421904 6.490872210952"/><rect class="schematic-box sch-box" x="544.4219066938313" y="689.6551724137639" width="87.62677484785195" height="20.283975659225007" stroke-width="1.622718052738px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="12.981744421904 6.490872210952"/>
82
75
  </g>
83
76
  </svg>
@@ -0,0 +1,94 @@
1
+ <svg width="1200" height="2016" viewBox="0 0 1200 2016" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Solver debug visualization on top and Circuit JSON schematic on the bottom">
2
+ <g transform="translate(0, 0) scale(1.875, 1.875) translate(0, 0)">
3
+ <rect width="100%" height="100%" fill="white"/><g><polyline data-points="-14.05,6 -14.25,6 -14.25,5.72 -10.399999999999999,5.72 -10.399999999999999,5.92" data-type="line" data-label="" points="69.52029520295173,203.3948339483395 49.840098400983834,203.3948339483395 49.840098400983834,230.9471094710948 428.6838868388685,230.9471094710948 428.6838868388685,211.26691266912678" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-9.6,6 -8.659,6 -8.659,3.180000000000001 -10.4,3.180000000000001 -10.4,3.3800000000000003" data-type="line" data-label="" points="507.4046740467404,203.3948339483395 599.9999999999999,203.3948339483395 599.9999999999999,480.88560885608854 428.68388683886826,480.88560885608854 428.68388683886826,461.2054120541206" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="J1" data-x="-13" data-y="6" x="69.52029520295173" y="183.71463714637144" width="206.64206642066438" height="39.36039360393613" fill="hsl(183, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010162499999999998"/></g><g><rect data-type="rect" data-label="F1" data-x="-10.17" data-y="6.115" x="395.2275522755226" y="172.89052890528905" width="112.1771217712178" height="38.37638376383768" fill="hsl(59, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010162499999999998"/></g><g><rect data-type="rect" data-label="RV1" data-x="-10.195" data-y="3.615" x="390.3075030750306" y="414.9569495694958" width="117.09717097170983" height="46.24846248462478" fill="hsl(157, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010162499999999998"/></g><g><rect data-type="rect" data-label="netId: LINE
4
+ globalConnNetId: connectivity_net0" data-x="-14.25" data-y="6.225" x="39.999999999999886" y="159.11439114391163" width="19.680196801967895" height="44.280442804427935" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010162499999999998"/></g><g><rect data-type="rect" data-label="netId: LINE_FUSED
5
+ globalConnNetId: connectivity_net1" data-x="-9.1295" data-y="6.225" x="543.8622386223863" y="159.11439114391163" width="19.68019680196801" height="44.280442804427935" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010162499999999998"/></g><g><rect data-type="rect" data-label="netId: NEUTRAL
6
+ globalConnNetId: connectivity_net2" data-x="-9.179" data-y="3.5" x="507.50307503075015" y="439.55719557195584" width="82.65682656826573" height="19.680196801968066" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010162499999999998"/></g><g><circle data-type="point" data-label="J1.1
7
+ x-" data-x="-14.05" data-y="6" cx="69.52029520295173" cy="203.3948339483395" r="3" fill="hsl(218, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F1.1
8
+ y-" data-x="-10.4" data-y="5.92" cx="428.68388683886826" cy="211.26691266912678" r="3" fill="hsl(214, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="F1.2
9
+ x+" data-x="-9.6" data-y="6" cx="507.4046740467404" cy="203.3948339483395" r="3" fill="hsl(215, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="RV1.1
10
+ y-" data-x="-10.4" data-y="3.3800000000000003" cx="428.68388683886826" cy="461.2054120541206" r="3" fill="hsl(72, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="RV1.2
11
+ x+" data-x="-9.6" data-y="3.5" cx="507.4046740467404" cy="449.39729397293985" r="3" fill="hsl(73, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
12
+ orientation: y+" data-x="-14.25" data-y="6" cx="49.840098400983834" cy="203.3948339483395" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
13
+ orientation: y+" data-x="-9.1295" data-y="6" cx="553.7023370233702" cy="203.3948339483395" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
14
+ orientation: x+" data-x="-9.6" data-y="3.5" cx="507.4046740467404" cy="449.39729397293985" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
15
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
16
+ const svg = e.currentTarget;
17
+ const rect = svg.getBoundingClientRect();
18
+ const x = e.clientX - rect.left;
19
+ const y = e.clientY - rect.top;
20
+ const crosshair = svg.getElementById('crosshair');
21
+ const h = svg.getElementById('crosshair-h');
22
+ const v = svg.getElementById('crosshair-v');
23
+ const coords = svg.getElementById('coordinates');
24
+
25
+ crosshair.style.display = 'block';
26
+ h.setAttribute('x1', '0');
27
+ h.setAttribute('x2', '640');
28
+ h.setAttribute('y1', y);
29
+ h.setAttribute('y2', y);
30
+ v.setAttribute('x1', x);
31
+ v.setAttribute('x2', x);
32
+ v.setAttribute('y1', '0');
33
+ v.setAttribute('y2', '640');
34
+
35
+ // Calculate real coordinates using inverse transformation
36
+ const matrix = {"a":98.40098400984012,"c":0,"e":1452.0541205412055,"b":0,"d":-98.40098400984012,"f":793.8007380073802};
37
+ // Manually invert and apply the affine transform
38
+ // Since we only use translate and scale, we can directly compute:
39
+ // x' = (x - tx) / sx
40
+ // y' = (y - ty) / sy
41
+ const sx = matrix.a;
42
+ const sy = matrix.d;
43
+ const tx = matrix.e;
44
+ const ty = matrix.f;
45
+ const realPoint = {
46
+ x: (x - tx) / sx,
47
+ y: (y - ty) / sy // Flip y back since we used negative scale
48
+ }
49
+
50
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
51
+ coords.setAttribute('x', (x + 5).toString());
52
+ coords.setAttribute('y', (y - 5).toString());
53
+ });
54
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
55
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
56
+ });
57
+ ]]></script>
58
+ </g>
59
+ <g transform="translate(0, 1216)">
60
+ <style>
61
+ .boundary { fill: rgb(245, 241, 237); }
62
+ .schematic-boundary { fill: none; stroke: #fff; }
63
+ .component { fill: none; stroke: rgb(132, 0, 0); }
64
+ .chip { fill: rgb(255, 255, 194); stroke: rgb(132, 0, 0); }
65
+ .component-pin { fill: none; stroke: rgb(132, 0, 0); }
66
+ .text { font-family: sans-serif; fill: rgb(0, 150, 0); }
67
+ .pin-number { fill: rgb(169, 0, 0); }
68
+ .port-label { fill: rgb(0, 100, 100); }
69
+ .component-name { fill: rgb(0, 100, 100); }
70
+
71
+ </style><rect class="boundary" x="0" y="0" width="1200" height="800"/><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"><path d="M 142.41210502838976 179.8783563267002 L 106.80907877114987 179.8783563267002 L 106.80907877114987 229.72259308683624 L 792.1673342230201 229.72259308683624 L 792.1673342230201 194.11956682959612" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="28.482421005792px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 142.41210502838976 179.8783563267002 L 106.80907877114987 179.8783563267002 L 106.80907877114987 229.72259308683624 L 792.1673342230201 229.72259308683624 L 792.1673342230201 194.11956682959612" stroke="rgb(0, 150, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_1__stack1"><path d="M 934.5794392519799 179.8783563267002 L 1102.091677792294 179.8783563267002 L 1102.091677792294 681.881026553784 L 792.1673342230199 681.881026553784 L 792.1673342230199 646.2780002965441" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="28.482421005792px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 934.5794392519799 179.8783563267002 L 1102.091677792294 179.8783563267002 L 1102.091677792294 681.881026553784 L 792.1673342230199 681.881026553784 L 792.1673342230199 646.2780002965441" stroke="rgb(0, 150, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"/><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_1__stack1"/><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_0__stack1"><rect class="component chip sch-component-body" x="213.61815754287" y="144.2753300694601" width="231.41967067205996" height="71.20605251448" stroke-width="3.560302625724px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="213.61815754287" y="144.2753300694601" width="231.41967067205996" height="71.20605251448" fill="transparent"/><text class="sch-text" x="329.3279928789002" y="179.8783563267002" fill="#0f172a" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="23.73535083816px" transform="rotate(0, 329.3279928789002, 179.8783563267002)">J1</text><line class="component-pin sch-component-pin sch-port-line" x1="213.61815754287" y1="179.8783563267002" x2="142.41210502838976" y2="179.8783563267002" stroke-width="3.560302625724px"/><g class="sch-port" data-schematic-port-id="source_port_0_0__stack1"><rect x="138.85180240266575" y="176.3180537009762" width="7.120605251448" height="7.120605251448" opacity="0"/></g><text class="pin-number sch-pin-number" x="178.01513128562965" y="176.31805370097618" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="26.70226969293px" transform="">1</text></g><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_1__stack1"><rect class="component-overlay sch-component-overlay" x="791.4570450036748" y="162.32560986228535" width="143.12239424830545" height="36.45570419532308" fill="transparent"/><path class="sch-component-symbol-path" d="M 791.4570450036748 179.87835632670016 L 934.5794392519803 179.87835632670016" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><path class="sch-component-symbol-path" d="M 863.0182421278275 162.32560986228535 L 899.4739463231506 162.32560986228535 L 899.4739463231506 198.78131405760843 L 827.9127491989979 198.78131405760843 L 827.9127491989979 162.32560986228535 L 863.0182421278275 162.32560986228535" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><text class="sch-component-name sch-component-text" x="861.668030861334" y="152.8741309968312" stroke="rgb(245, 241, 237)" stroke-width="3.560302625724px" paint-order="stroke" fill="rgb(15, 15, 15)" font-family="sans-serif" text-anchor="middle" dominant-baseline="ideographic" font-size="32.042723631516px">F1</text><text class="sch-component-text" x="863.0182421278275" y="212.28342672254288" fill="rgb(15, 15, 15)" font-family="sans-serif" text-anchor="middle" dominant-baseline="hanging" font-size="32.042723631516px"></text></g><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_2__stack1"><rect class="component-overlay sch-component-overlay" x="790.5802827469805" y="594.2496975087281" width="143.9991565049993" height="62.66629959013858" fill="transparent"/><path class="sch-component-symbol-path" d="M 790.5802827469805 626.2495100653946 L 827.9133973964248 626.2495100653946" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><path class="sch-component-symbol-path" d="M 898.57965012573 626.2495100653946 L 934.5794392519798 624.9161845422002" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><path class="sch-component-symbol-path" d="M 898.57965012573 656.9159970988667 L 827.9133973964248 594.2496975087281" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><path class="sch-component-symbol-path" d="M 913.2462308808688 656.9159970988667 L 898.57965012573 656.9159970988667" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><path class="sch-component-symbol-path" d="M 862.5798609994802 607.5829527406725 L 898.57965012573 607.5829527406725 L 898.57965012573 643.5827418669223 L 827.9133973964248 643.5827418669223 L 827.9133973964248 607.5829527406725 L 862.5798609994802 607.5829527406725" stroke="rgb(132, 0, 0)" fill="none" stroke-width="3.560302625724px" stroke-linecap="round"/><text class="sch-component-name sch-component-text" x="854.5799078603135" y="592.9163719855336" stroke="rgb(245, 241, 237)" stroke-width="3.560302625724px" paint-order="stroke" fill="rgb(15, 15, 15)" font-family="sans-serif" text-anchor="start" dominant-baseline="middle" font-size="32.042723631516px">RV1</text><text class="sch-component-text" x="863.9131865226746" y="659.5826481452556" fill="rgb(15, 15, 15)" font-family="sans-serif" text-anchor="end" dominant-baseline="middle" font-size="32.042723631516px"></text><circle class="component-pin sch-component-pin" cx="934.5794392519798" cy="624.9161845422002" r="3.560302625724px" stroke-width="3.560302625724px" fill="none" stroke="rgb(132, 0, 0)"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_0__stack1"><circle cx="142.41210502838976" cy="179.8783563267002" r="7.120605251448" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_0__stack1"><circle cx="814.9541566805533" cy="192.399806644122" r="7.120605251448" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_1__stack1"><circle cx="957.3662617095133" cy="178.15859614122587" r="7.120605251448" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_2_0__stack1"><circle cx="816.2959268650682" cy="645.8311745068766" r="7.120605251448" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_2_1__stack1"><circle cx="958.7080318940282" cy="624.4693587525326" r="7.120605251448" fill="red" opacity="0"/></g><path class="net-label sch-net-label" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" d="
72
+ M 106.80907877114987,179.8783563267002
73
+ L 89.00756564252987,170.2655392372454
74
+ L 89.00756564252987,109.2182168814979
75
+ L 124.61059189976987,109.2182168814979
76
+ L 124.61059189976987,170.2655392372454
77
+ Z
78
+ " fill="rgba(255, 255, 255, 0.6)" stroke="rgb(132, 0, 0)" stroke-width="3.560302625724px"/><text class="net-label-text sch-net-label-text" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_0__stack1" x="106.80907877114987" y="163.85699451094223" fill="rgb(132, 0, 0)" text-anchor="start" dominant-baseline="central" font-family="sans-serif" font-variant-numeric="tabular-nums" font-size="32.042723631516px" transform="rotate(-90 106.80907877114987 163.85699451094223)">XX</text><path class="net-label sch-net-label" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_1__stack1" d="
79
+ M 1018.335558522137,179.8783563267002
80
+ L 1000.5340453935171,170.2655392372454
81
+ L 1000.5340453935171,109.2182168814979
82
+ L 1036.137071650757,109.2182168814979
83
+ L 1036.137071650757,170.2655392372454
84
+ Z
85
+ " fill="rgba(255, 255, 255, 0.6)" stroke="rgb(132, 0, 0)" stroke-width="3.560302625724px"/><text class="net-label-text sch-net-label-text" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_1__stack1" x="1018.335558522137" y="163.85699451094223" fill="rgb(132, 0, 0)" text-anchor="start" dominant-baseline="central" font-family="sans-serif" font-variant-numeric="tabular-nums" font-size="32.042723631516px" transform="rotate(-90 1018.335558522137 163.85699451094223)">XX</text><path class="net-label sch-net-label" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_2__stack1" d="
86
+ M 934.5794392519799,624.9161845422001
87
+ L 944.1922563414347,607.1146714135801
88
+ L 1108.607031597369,607.1146714135801
89
+ L 1108.607031597369,642.71769767082
90
+ L 944.1922563414347,642.71769767082
91
+ Z
92
+ " fill="rgba(255, 255, 255, 0.6)" stroke="rgb(132, 0, 0)" stroke-width="3.560302625724px"/><text class="net-label-text sch-net-label-text" data-circuit-json-type="schematic_net_label" data-schematic-net-label-id="schematic_net_label_2__stack1" x="950.600801067738" y="624.9161845422001" fill="rgb(132, 0, 0)" text-anchor="start" dominant-baseline="central" font-family="sans-serif" font-variant-numeric="tabular-nums" font-size="32.042723631516px" transform="">NEUTRAL</text>
93
+ </g>
94
+ </svg>
@@ -0,0 +1,82 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "J1",
5
+ "center": { "x": -13, "y": 6 },
6
+ "width": 1.3,
7
+ "height": 0.4,
8
+ "pins": [
9
+ {
10
+ "pinId": "J1.1",
11
+ "x": -14.05,
12
+ "y": 6,
13
+ "_facingDirection": "x-"
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "chipId": "F1",
19
+ "symbolName": "fuse_horz",
20
+ "center": { "x": -10.17, "y": 6.115 },
21
+ "width": 0.74,
22
+ "height": 0.39,
23
+ "pins": [
24
+ {
25
+ "pinId": "F1.1",
26
+ "x": -10.4,
27
+ "y": 6,
28
+ "_facingDirection": "x-"
29
+ },
30
+ {
31
+ "pinId": "F1.2",
32
+ "x": -9.6,
33
+ "y": 6,
34
+ "_facingDirection": "x+"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "chipId": "RV1",
40
+ "symbolName": "varistor_horz",
41
+ "center": { "x": -10.195, "y": 3.615 },
42
+ "width": 0.79,
43
+ "height": 0.47,
44
+ "pins": [
45
+ {
46
+ "pinId": "RV1.1",
47
+ "x": -10.4,
48
+ "y": 3.5,
49
+ "_facingDirection": "x-"
50
+ },
51
+ {
52
+ "pinId": "RV1.2",
53
+ "x": -9.6,
54
+ "y": 3.5,
55
+ "_facingDirection": "x+"
56
+ }
57
+ ]
58
+ }
59
+ ],
60
+ "directConnections": [
61
+ {
62
+ "netId": "LINE",
63
+ "pinIds": ["J1.1", "F1.1"]
64
+ },
65
+ {
66
+ "netId": "LINE_FUSED",
67
+ "pinIds": ["F1.2", "RV1.1"]
68
+ }
69
+ ],
70
+ "netConnections": [
71
+ {
72
+ "netId": "NEUTRAL",
73
+ "netLabelText": "NEUTRAL",
74
+ "netLabelWidth": 0.84,
75
+ "pinIds": ["RV1.2"]
76
+ }
77
+ ],
78
+ "availableNetLabelOrientations": {
79
+ "NEUTRAL": ["x+"]
80
+ },
81
+ "maxMspPairDistance": 100
82
+ }
@@ -1,4 +1,5 @@
1
1
  import { expect, test } from "bun:test"
2
+ import { getOutputLabelCollisions } from "lib/solvers/InlineNetLabelSolver/getOutputLabelCollisions"
2
3
  import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
4
  import "tests/fixtures/matcher"
4
5
  import inputProblem from "./assets/repro-mouse-switch-ground-unnecessary-jog.input.json"
@@ -11,5 +12,19 @@ test("repro unnecessary jog in mouse switch ground trunk", () => {
11
12
 
12
13
  solver.solve()
13
14
 
15
+ const inlineOutput = solver.inlineNetLabelSolver!.getOutput()
16
+ const groundLabel = inlineOutput.netLabelPlacements.find(
17
+ (label) => label.netId === "GND",
18
+ )
19
+ expect(groundLabel?.orientation).toBe("y-")
20
+ expect(
21
+ inlineOutput.inlineNetLabelPlacements.some(
22
+ (label) => label.netId === "RIGHT",
23
+ ),
24
+ ).toBe(true)
25
+ expect(
26
+ inlineOutput.netLabelPlacements.some((label) => label.netId === "RIGHT"),
27
+ ).toBe(false)
28
+ expect(getOutputLabelCollisions(inlineOutput)).toEqual([])
14
29
  expect(solver).toMatchSolverSnapshot(import.meta.path)
15
30
  })
@@ -0,0 +1,21 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+ import "tests/fixtures/matcher"
5
+ import inputProblemJson from "./assets/repro-trace-endpoints-stop-at-component-boundary.input.json"
6
+
7
+ const inputProblem: InputProblem = JSON.parse(JSON.stringify(inputProblemJson))
8
+
9
+ // F1 and RV1 have ports inside their text-expanded component bounds. The
10
+ // routed traces currently stop at the lower bounds instead of reaching the
11
+ // original left-facing port coordinates.
12
+ test("trace endpoints stop at component boundaries before reaching target ports", () => {
13
+ const solver = new SchematicTracePipelineSolver(inputProblem, {
14
+ hideRatsNet: true,
15
+ })
16
+
17
+ solver.solve()
18
+
19
+ expect(solver.schematicTraceLinesSolver!.solvedTracePaths).toHaveLength(2)
20
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
21
+ })