@tscircuit/schematic-trace-solver 0.0.111 → 0.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1130,6 +1130,17 @@ var calculateElbowForPins = ({
1130
1130
  },
1131
1131
  { overshoot }
1132
1132
  );
1133
+ var pinsFaceEachOther = ({
1134
+ pin1,
1135
+ pin2
1136
+ }) => {
1137
+ const xDistance = pin2.x - pin1.x;
1138
+ const yDistance = pin2.y - pin1.y;
1139
+ if (Math.abs(xDistance) >= Math.abs(yDistance)) {
1140
+ return xDistance > 0 ? pin1._facingDirection === "x+" && pin2._facingDirection === "x-" : pin1._facingDirection === "x-" && pin2._facingDirection === "x+";
1141
+ }
1142
+ return yDistance > 0 ? pin1._facingDirection === "y+" && pin2._facingDirection === "y-" : pin1._facingDirection === "y-" && pin2._facingDirection === "y+";
1143
+ };
1133
1144
  var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1134
1145
  pins;
1135
1146
  connectionPair;
@@ -1178,7 +1189,9 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1178
1189
  pin2,
1179
1190
  overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4))
1180
1191
  });
1181
- const shouldUseAdaptiveElbow = findFirstCollision(defaultElbow, this.obstacles) !== null && findFirstCollision(adaptiveElbow, this.obstacles) === null;
1192
+ const adaptiveElbowIsShorter = this.pathLength(adaptiveElbow) < this.pathLength(defaultElbow);
1193
+ const defaultElbowBacktracks = this.pathLength(defaultElbow) > routingDistance + 1e-9;
1194
+ const shouldUseAdaptiveElbow = findFirstCollision(adaptiveElbow, this.obstacles) === null && (pinsFaceEachOther({ pin1, pin2 }) && defaultElbowBacktracks && adaptiveElbowIsShorter || findFirstCollision(defaultElbow, this.obstacles) !== null);
1182
1195
  this.baseElbow = defaultElbow;
1183
1196
  if (shouldUseAdaptiveElbow) {
1184
1197
  this.baseElbow = adaptiveElbow;
@@ -58,6 +58,25 @@ const calculateElbowForPins = ({
58
58
  { overshoot },
59
59
  )
60
60
 
61
+ const pinsFaceEachOther = ({
62
+ pin1,
63
+ pin2,
64
+ }: {
65
+ pin1: MspConnectionPair["pins"][number]
66
+ pin2: MspConnectionPair["pins"][number]
67
+ }) => {
68
+ const xDistance = pin2.x - pin1.x
69
+ const yDistance = pin2.y - pin1.y
70
+ if (Math.abs(xDistance) >= Math.abs(yDistance)) {
71
+ return xDistance > 0
72
+ ? pin1._facingDirection === "x+" && pin2._facingDirection === "x-"
73
+ : pin1._facingDirection === "x-" && pin2._facingDirection === "x+"
74
+ }
75
+ return yDistance > 0
76
+ ? pin1._facingDirection === "y+" && pin2._facingDirection === "y-"
77
+ : pin1._facingDirection === "y-" && pin2._facingDirection === "y+"
78
+ }
79
+
61
80
  export class SchematicTraceSingleLineSolver2 extends BaseSolver {
62
81
  pins: MspConnectionPair["pins"]
63
82
  connectionPair?: MspConnectionPair
@@ -130,9 +149,16 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver {
130
149
  pin2,
131
150
  overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4)),
132
151
  })
152
+ const adaptiveElbowIsShorter =
153
+ this.pathLength(adaptiveElbow) < this.pathLength(defaultElbow)
154
+ const defaultElbowBacktracks =
155
+ this.pathLength(defaultElbow) > routingDistance + 1e-9
133
156
  const shouldUseAdaptiveElbow =
134
- findFirstCollision(defaultElbow, this.obstacles) !== null &&
135
- findFirstCollision(adaptiveElbow, this.obstacles) === null
157
+ findFirstCollision(adaptiveElbow, this.obstacles) === null &&
158
+ ((pinsFaceEachOther({ pin1, pin2 }) &&
159
+ defaultElbowBacktracks &&
160
+ adaptiveElbowIsShorter) ||
161
+ findFirstCollision(defaultElbow, this.obstacles) !== null)
136
162
 
137
163
  // Build initial elbow path
138
164
  this.baseElbow = defaultElbow
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.111",
4
+ "version": "0.0.112",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -0,0 +1,63 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
3
+ import type { InputChip, InputProblem } from "lib/types/InputProblem"
4
+
5
+ const getOrthogonalPathLength = (points: Array<{ x: number; y: number }>) =>
6
+ points.slice(0, -1).reduce((length, point, pointIndex) => {
7
+ const nextPoint = points[pointIndex + 1]!
8
+ return (
9
+ length + Math.abs(nextPoint.x - point.x) + Math.abs(nextPoint.y - point.y)
10
+ )
11
+ }, 0)
12
+
13
+ test("repro47 uses a minimal route between nearby opposing pins", async () => {
14
+ const diode: InputChip = {
15
+ chipId: "D2",
16
+ center: { x: 3.2, y: 0.095 },
17
+ width: 0.4,
18
+ height: 0.2,
19
+ pins: [{ pinId: "D2.cathode", x: 3.4, y: 0.095 }],
20
+ }
21
+ const pushButton: InputChip = {
22
+ chipId: "SW2",
23
+ center: { x: 3.81, y: 0.045 },
24
+ width: 0.4,
25
+ height: 0.2,
26
+ pins: [{ pinId: "SW2.pin1", x: 3.61, y: 0.045 }],
27
+ }
28
+ const pins: ConstructorParameters<
29
+ typeof SchematicTraceSingleLineSolver2
30
+ >[0]["pins"] = [
31
+ {
32
+ pinId: "D2.cathode",
33
+ chipId: diode.chipId,
34
+ x: 3.4,
35
+ y: 0.095,
36
+ _facingDirection: "x+" as const,
37
+ },
38
+ {
39
+ pinId: "SW2.pin1",
40
+ chipId: pushButton.chipId,
41
+ x: 3.61,
42
+ y: 0.045,
43
+ _facingDirection: "x-" as const,
44
+ },
45
+ ]
46
+ const inputProblem: InputProblem = {
47
+ chips: [diode, pushButton],
48
+ directConnections: [{ pinIds: [pins[0].pinId, pins[1].pinId] }],
49
+ netConnections: [],
50
+ availableNetLabelOrientations: {},
51
+ }
52
+ const solver = new SchematicTraceSingleLineSolver2({
53
+ pins,
54
+ chipMap: { [diode.chipId]: diode, [pushButton.chipId]: pushButton },
55
+ inputProblem,
56
+ })
57
+
58
+ solver.solve()
59
+
60
+ expect(solver.solved).toBe(true)
61
+ expect(getOrthogonalPathLength(solver.solvedTracePath!)).toBeCloseTo(0.26)
62
+ await expect(solver).toMatchSolverSnapshot(import.meta.path)
63
+ })
@@ -0,0 +1,46 @@
1
+ <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="3.4,0.095 3.61,0.045" data-type="line" data-label="" points="261.78217821782187,306.13861386138615 378.21782178217813,333.86138613861385" fill="none" stroke="hsl(112, 100%, 50%, 0.1)" stroke-width="1px"/></g><g><polyline data-points="3.4,0.095 3.465,0.095 3.505,0.095 3.505,0.045 3.545,0.045 3.61,0.045" data-type="line" data-label="" points="261.78217821782187,306.13861386138615 297.82178217821775,306.13861386138615 320,306.13861386138615 320,333.86138613861385 342.17821782178225,333.86138613861385 378.21782178217813,333.86138613861385" fill="none" stroke="red" stroke-width="1px" stroke-dasharray="4 4"/></g><g><polyline data-points="3.4,0.095 3.61,0.045" data-type="line" data-label="" points="261.78217821782187,306.13861386138615 378.21782178217813,333.86138613861385" fill="none" stroke="blue" stroke-width="1px" stroke-dasharray="5 5"/></g><g><polyline data-points="3.4,0.095 3.465,0.095 3.505,0.095 3.505,0.045 3.545,0.045 3.61,0.045" data-type="line" data-label="" points="261.78217821782187,306.13861386138615 297.82178217821775,306.13861386138615 320,306.13861386138615 320,333.86138613861385 342.17821782178225,333.86138613861385 378.21782178217813,333.86138613861385" fill="none" stroke="green" stroke-width="1px"/></g><g><rect data-type="rect" data-label="D2" data-x="3.2" data-y="0.095" x="40" y="250.69306930693068" width="221.7821782178221" height="110.89108910891093" fill="hsl(358, 100%, 50%, 0.1)" stroke="black" stroke-width="0.0018035714285714283"/></g><g><rect data-type="rect" data-label="SW2" data-x="3.81" data-y="0.045" x="378.21782178217825" y="278.41584158415844" width="221.78217821782187" height="110.89108910891088" fill="hsl(70, 100%, 50%, 0.1)" stroke="black" stroke-width="0.0018035714285714283"/></g><g><circle data-type="point" data-label="D2.cathode
2
+ x+" data-x="3.4" data-y="0.095" cx="261.78217821782187" cy="306.13861386138615" r="3" fill="hsl(22, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="SW2.pin1
3
+ x-" data-x="3.61" data-y="0.045" cx="378.21782178217813" cy="333.86138613861385" r="3" fill="hsl(12, 100%, 50%, 0.8)"/></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[
4
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
5
+ const svg = e.currentTarget;
6
+ const rect = svg.getBoundingClientRect();
7
+ const x = e.clientX - rect.left;
8
+ const y = e.clientY - rect.top;
9
+ const crosshair = svg.getElementById('crosshair');
10
+ const h = svg.getElementById('crosshair-h');
11
+ const v = svg.getElementById('crosshair-v');
12
+ const coords = svg.getElementById('coordinates');
13
+
14
+ crosshair.style.display = 'block';
15
+ h.setAttribute('x1', '0');
16
+ h.setAttribute('x2', '640');
17
+ h.setAttribute('y1', y);
18
+ h.setAttribute('y2', y);
19
+ v.setAttribute('x1', x);
20
+ v.setAttribute('x2', x);
21
+ v.setAttribute('y1', '0');
22
+ v.setAttribute('y2', '640');
23
+
24
+ // Calculate real coordinates using inverse transformation
25
+ const matrix = {"a":554.4554455445545,"c":0,"e":-1623.3663366336636,"b":0,"d":-554.4554455445545,"f":358.81188118811883};
26
+ // Manually invert and apply the affine transform
27
+ // Since we only use translate and scale, we can directly compute:
28
+ // x' = (x - tx) / sx
29
+ // y' = (y - ty) / sy
30
+ const sx = matrix.a;
31
+ const sy = matrix.d;
32
+ const tx = matrix.e;
33
+ const ty = matrix.f;
34
+ const realPoint = {
35
+ x: (x - tx) / sx,
36
+ y: (y - ty) / sy // Flip y back since we used negative scale
37
+ }
38
+
39
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
40
+ coords.setAttribute('x', (x + 5).toString());
41
+ coords.setAttribute('y', (y - 5).toString());
42
+ });
43
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
44
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
45
+ });
46
+ ]]></script></svg>