@tscircuit/schematic-trace-solver 0.0.98 → 0.0.99
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 +1 -0
- package/dist/index.js +310 -220
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +19 -9
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +30 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts +84 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +18 -18
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +7 -7
- package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +5 -7
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +6 -12
- package/tests/examples/__snapshots__/example09.snap.svg +9 -13
- package/tests/examples/__snapshots__/example21.snap.svg +30 -32
- package/tests/examples/__snapshots__/example29.snap.svg +13 -19
- package/tests/repros/__snapshots__/repro129-host-custom-symbol-passives.snap.svg +60 -0
- package/tests/repros/assets/repro129-host-custom-symbol-passives.input.json +94 -0
- package/tests/repros/repro129-host-custom-symbol-passives.test.ts +16 -0
- package/tests/solvers/MspConnectionPairSolver/msp-connection-pair-solver-direct-connection-distance.test.ts +46 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/generate-endpoint-collision-detours.test.ts +38 -0
package/dist/index.js
CHANGED
|
@@ -76,80 +76,39 @@ var BaseSolver = class {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
// lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
|
|
79
|
-
import "
|
|
79
|
+
import { distance } from "@tscircuit/math-utils";
|
|
80
80
|
|
|
81
|
-
// lib/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
directConn.netId ? [directConn.netId, ...directConn.pinIds] : directConn.pinIds
|
|
88
|
-
]);
|
|
89
|
-
}
|
|
90
|
-
const netConnMap = new ConnectivityMap(directConnMap.netMap);
|
|
91
|
-
for (const netConn of inputProblem.netConnections) {
|
|
92
|
-
netConnMap.addConnections([[netConn.netId, ...netConn.pinIds]]);
|
|
93
|
-
}
|
|
94
|
-
return { directConnMap, netConnMap };
|
|
81
|
+
// lib/utils/getColorFromString.ts
|
|
82
|
+
var getColorFromString = (string, alpha = 1) => {
|
|
83
|
+
const hash = string.split("").reduce((acc, char) => {
|
|
84
|
+
return acc * 31 + char.charCodeAt(0);
|
|
85
|
+
}, 0);
|
|
86
|
+
return `hsl(${hash % 360}, 100%, 50%, ${alpha})`;
|
|
95
87
|
};
|
|
96
88
|
|
|
97
|
-
// lib/
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{
|
|
103
|
-
const seen = /* @__PURE__ */ new Set();
|
|
104
|
-
for (const p of pins) {
|
|
105
|
-
if (seen.has(p.pinId)) {
|
|
106
|
-
throw new Error(`Duplicate pinId detected: "${p.pinId}"`);
|
|
107
|
-
}
|
|
108
|
-
seen.add(p.pinId);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
const manhattan = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
112
|
-
const inTree = new Array(n).fill(false);
|
|
113
|
-
const bestDist = new Array(n).fill(Number.POSITIVE_INFINITY);
|
|
114
|
-
const parent = new Array(n).fill(-1);
|
|
115
|
-
let startIndex = 0;
|
|
116
|
-
for (let i = 1; i < n; i++) {
|
|
117
|
-
if (pins[i].pinId < pins[startIndex].pinId) startIndex = i;
|
|
89
|
+
// lib/utils/arePinsInDifferentSchematicSections.ts
|
|
90
|
+
var getSectionNameForPin = (sectionByChipId, sectionByPinId, pin) => {
|
|
91
|
+
if (pin.chipId) {
|
|
92
|
+
const chipSection = sectionByChipId.get(pin.chipId);
|
|
93
|
+
if (chipSection) return chipSection;
|
|
118
94
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
best = d;
|
|
130
|
-
bestId = pins[i].pinId;
|
|
131
|
-
u = i;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
inTree[u] = true;
|
|
136
|
-
if (parent[u] !== -1) {
|
|
137
|
-
edges.push([pins[u].pinId, pins[parent[u]].pinId]);
|
|
138
|
-
}
|
|
139
|
-
for (let v = 0; v < n; v++) {
|
|
140
|
-
if (!inTree[v]) {
|
|
141
|
-
const d0 = manhattan(pins[u], pins[v]);
|
|
142
|
-
const isForbidden = opts?.forbidEdge?.(pins[u], pins[v]) ?? false;
|
|
143
|
-
const d = d0 > maxDistance || isForbidden ? Number.POSITIVE_INFINITY : d0;
|
|
144
|
-
if (d < bestDist[v] || d === bestDist[v] && pins[u].pinId < pins[parent[v]]?.pinId) {
|
|
145
|
-
bestDist[v] = d;
|
|
146
|
-
parent[v] = u;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
95
|
+
return sectionByPinId.get(pin.pinId);
|
|
96
|
+
};
|
|
97
|
+
var arePinsInDifferentSchematicSections = (inputProblem, p1, p2) => {
|
|
98
|
+
const sectionByChipId = /* @__PURE__ */ new Map();
|
|
99
|
+
const sectionByPinId = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const chip of inputProblem.chips) {
|
|
101
|
+
if (!chip.sectionId) continue;
|
|
102
|
+
sectionByChipId.set(chip.chipId, chip.sectionId);
|
|
103
|
+
for (const pin of chip.pins) {
|
|
104
|
+
sectionByPinId.set(pin.pinId, chip.sectionId);
|
|
149
105
|
}
|
|
150
106
|
}
|
|
151
|
-
return
|
|
152
|
-
|
|
107
|
+
if (sectionByChipId.size === 0) return false;
|
|
108
|
+
const s1 = getSectionNameForPin(sectionByChipId, sectionByPinId, p1);
|
|
109
|
+
const s2 = getSectionNameForPin(sectionByChipId, sectionByPinId, p2);
|
|
110
|
+
return s1 !== s2;
|
|
111
|
+
};
|
|
153
112
|
|
|
154
113
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts
|
|
155
114
|
var getPinDirection = (pin, chip) => {
|
|
@@ -181,6 +140,101 @@ var getPinDirection = (pin, chip) => {
|
|
|
181
140
|
return "x-";
|
|
182
141
|
};
|
|
183
142
|
|
|
143
|
+
// lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts
|
|
144
|
+
var visualizeInputProblem = (inputProblem, opts = {}) => {
|
|
145
|
+
const {
|
|
146
|
+
connectionAlpha = 0.8,
|
|
147
|
+
chipAlpha = 0.8,
|
|
148
|
+
hideRatsNet = inputProblem._hideRatsNet ?? false
|
|
149
|
+
} = opts;
|
|
150
|
+
const graphics = {
|
|
151
|
+
lines: [],
|
|
152
|
+
points: [],
|
|
153
|
+
rects: []
|
|
154
|
+
};
|
|
155
|
+
const pinIdMap = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const chip of inputProblem.chips) {
|
|
157
|
+
for (const pin of chip.pins) {
|
|
158
|
+
pinIdMap.set(pin.pinId, pin);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const chip of inputProblem.chips) {
|
|
162
|
+
graphics.rects.push({
|
|
163
|
+
label: chip.chipId,
|
|
164
|
+
center: chip.center,
|
|
165
|
+
width: chip.width,
|
|
166
|
+
height: chip.height,
|
|
167
|
+
fill: getColorFromString(chip.chipId, chipAlpha)
|
|
168
|
+
});
|
|
169
|
+
for (const pin of chip.pins) {
|
|
170
|
+
graphics.points.push({
|
|
171
|
+
label: `${pin.pinId}
|
|
172
|
+
${pin._facingDirection ?? getPinDirection(pin, chip)}`,
|
|
173
|
+
x: pin.x,
|
|
174
|
+
y: pin.y,
|
|
175
|
+
color: getColorFromString(pin.pinId, 0.8)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
180
|
+
graphics.rects.push({
|
|
181
|
+
label: textBox.text ?? "schematic_text",
|
|
182
|
+
center: textBox.center,
|
|
183
|
+
width: textBox.width,
|
|
184
|
+
height: textBox.height,
|
|
185
|
+
fill: "rgba(160, 0, 220, 0.14)",
|
|
186
|
+
strokeColor: "rgba(160, 0, 220, 0.9)"
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (!hideRatsNet) {
|
|
190
|
+
for (const directConn of inputProblem.directConnections) {
|
|
191
|
+
const [pinId1, pinId2] = directConn.pinIds;
|
|
192
|
+
const pin1 = pinIdMap.get(pinId1);
|
|
193
|
+
const pin2 = pinIdMap.get(pinId2);
|
|
194
|
+
if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
graphics.lines.push({
|
|
198
|
+
points: [
|
|
199
|
+
{
|
|
200
|
+
x: pin1.x,
|
|
201
|
+
y: pin1.y
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
x: pin2.x,
|
|
205
|
+
y: pin2.y
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
strokeColor: getColorFromString(
|
|
209
|
+
directConn.netId ?? `${pinId1}-${pinId2}`,
|
|
210
|
+
connectionAlpha
|
|
211
|
+
)
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
for (const netConn of inputProblem.netConnections) {
|
|
215
|
+
const pins = netConn.pinIds.map((pinId) => pinIdMap.get(pinId));
|
|
216
|
+
for (let i = 0; i < pins.length - 1; i++) {
|
|
217
|
+
for (let j = i + 1; j < pins.length; j++) {
|
|
218
|
+
const pin1 = pins[i];
|
|
219
|
+
const pin2 = pins[j];
|
|
220
|
+
if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
graphics.lines.push({
|
|
224
|
+
points: [
|
|
225
|
+
{ x: pin1.x, y: pin1.y },
|
|
226
|
+
{ x: pin2.x, y: pin2.y }
|
|
227
|
+
],
|
|
228
|
+
strokeColor: getColorFromString(netConn.netId, connectionAlpha),
|
|
229
|
+
strokeDash: "4 2"
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return graphics;
|
|
236
|
+
};
|
|
237
|
+
|
|
184
238
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts
|
|
185
239
|
var getRestrictedCenterLines = (params) => {
|
|
186
240
|
const { pins, inputProblem, pinIdMap, chipMap } = params;
|
|
@@ -293,134 +347,81 @@ var doesPairCrossRestrictedCenterLines = (params) => {
|
|
|
293
347
|
return hvCrosses && vhCrosses;
|
|
294
348
|
};
|
|
295
349
|
|
|
296
|
-
// lib/
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
// lib/utils/arePinsInDifferentSchematicSections.ts
|
|
305
|
-
var getSectionNameForPin = (sectionByChipId, sectionByPinId, pin) => {
|
|
306
|
-
if (pin.chipId) {
|
|
307
|
-
const chipSection = sectionByChipId.get(pin.chipId);
|
|
308
|
-
if (chipSection) return chipSection;
|
|
350
|
+
// lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem.ts
|
|
351
|
+
import { ConnectivityMap } from "connectivity-map";
|
|
352
|
+
var getConnectivityMapsFromInputProblem = (inputProblem) => {
|
|
353
|
+
const directConnMap = new ConnectivityMap({});
|
|
354
|
+
for (const directConn of inputProblem.directConnections) {
|
|
355
|
+
directConnMap.addConnections([
|
|
356
|
+
directConn.netId ? [directConn.netId, ...directConn.pinIds] : directConn.pinIds
|
|
357
|
+
]);
|
|
309
358
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const sectionByChipId = /* @__PURE__ */ new Map();
|
|
314
|
-
const sectionByPinId = /* @__PURE__ */ new Map();
|
|
315
|
-
for (const chip of inputProblem.chips) {
|
|
316
|
-
if (!chip.sectionId) continue;
|
|
317
|
-
sectionByChipId.set(chip.chipId, chip.sectionId);
|
|
318
|
-
for (const pin of chip.pins) {
|
|
319
|
-
sectionByPinId.set(pin.pinId, chip.sectionId);
|
|
320
|
-
}
|
|
359
|
+
const netConnMap = new ConnectivityMap(directConnMap.netMap);
|
|
360
|
+
for (const netConn of inputProblem.netConnections) {
|
|
361
|
+
netConnMap.addConnections([[netConn.netId, ...netConn.pinIds]]);
|
|
321
362
|
}
|
|
322
|
-
|
|
323
|
-
const s1 = getSectionNameForPin(sectionByChipId, sectionByPinId, p1);
|
|
324
|
-
const s2 = getSectionNameForPin(sectionByChipId, sectionByPinId, p2);
|
|
325
|
-
return s1 !== s2;
|
|
363
|
+
return { directConnMap, netConnMap };
|
|
326
364
|
};
|
|
327
365
|
|
|
328
|
-
// lib/solvers/
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const pinIdMap = /* @__PURE__ */ new Map();
|
|
341
|
-
for (const chip of inputProblem.chips) {
|
|
342
|
-
for (const pin of chip.pins) {
|
|
343
|
-
pinIdMap.set(pin.pinId, pin);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
for (const chip of inputProblem.chips) {
|
|
347
|
-
graphics.rects.push({
|
|
348
|
-
label: chip.chipId,
|
|
349
|
-
center: chip.center,
|
|
350
|
-
width: chip.width,
|
|
351
|
-
height: chip.height,
|
|
352
|
-
fill: getColorFromString(chip.chipId, chipAlpha)
|
|
353
|
-
});
|
|
354
|
-
for (const pin of chip.pins) {
|
|
355
|
-
graphics.points.push({
|
|
356
|
-
label: `${pin.pinId}
|
|
357
|
-
${pin._facingDirection ?? getPinDirection(pin, chip)}`,
|
|
358
|
-
x: pin.x,
|
|
359
|
-
y: pin.y,
|
|
360
|
-
color: getColorFromString(pin.pinId, 0.8)
|
|
361
|
-
});
|
|
366
|
+
// lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts
|
|
367
|
+
function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
|
|
368
|
+
const n = pins.length;
|
|
369
|
+
const maxDistance = opts?.maxDistance ?? Number.POSITIVE_INFINITY;
|
|
370
|
+
if (n <= 1) return [];
|
|
371
|
+
{
|
|
372
|
+
const seen = /* @__PURE__ */ new Set();
|
|
373
|
+
for (const p of pins) {
|
|
374
|
+
if (seen.has(p.pinId)) {
|
|
375
|
+
throw new Error(`Duplicate pinId detected: "${p.pinId}"`);
|
|
376
|
+
}
|
|
377
|
+
seen.add(p.pinId);
|
|
362
378
|
}
|
|
363
379
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
strokeColor: "rgba(160, 0, 220, 0.9)"
|
|
372
|
-
});
|
|
380
|
+
const manhattan = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
381
|
+
const inTree = new Array(n).fill(false);
|
|
382
|
+
const bestDist = new Array(n).fill(Number.POSITIVE_INFINITY);
|
|
383
|
+
const parent = new Array(n).fill(-1);
|
|
384
|
+
let startIndex = 0;
|
|
385
|
+
for (let i = 1; i < n; i++) {
|
|
386
|
+
if (pins[i].pinId < pins[startIndex].pinId) startIndex = i;
|
|
373
387
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
388
|
+
bestDist[startIndex] = 0;
|
|
389
|
+
const edges = [];
|
|
390
|
+
for (let iter = 0; iter < n; iter++) {
|
|
391
|
+
let u = -1;
|
|
392
|
+
let best = Number.POSITIVE_INFINITY;
|
|
393
|
+
let bestId = "";
|
|
394
|
+
for (let i = 0; i < n; i++) {
|
|
395
|
+
if (!inTree[i]) {
|
|
396
|
+
const d = bestDist[i];
|
|
397
|
+
if (d < best || d === best && (bestId === "" || pins[i].pinId < bestId)) {
|
|
398
|
+
best = d;
|
|
399
|
+
bestId = pins[i].pinId;
|
|
400
|
+
u = i;
|
|
401
|
+
}
|
|
381
402
|
}
|
|
382
|
-
graphics.lines.push({
|
|
383
|
-
points: [
|
|
384
|
-
{
|
|
385
|
-
x: pin1.x,
|
|
386
|
-
y: pin1.y
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
x: pin2.x,
|
|
390
|
-
y: pin2.y
|
|
391
|
-
}
|
|
392
|
-
],
|
|
393
|
-
strokeColor: getColorFromString(
|
|
394
|
-
directConn.netId ?? `${pinId1}-${pinId2}`,
|
|
395
|
-
connectionAlpha
|
|
396
|
-
)
|
|
397
|
-
});
|
|
398
403
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
{ x: pin2.x, y: pin2.y }
|
|
412
|
-
],
|
|
413
|
-
strokeColor: getColorFromString(netConn.netId, connectionAlpha),
|
|
414
|
-
strokeDash: "4 2"
|
|
415
|
-
});
|
|
404
|
+
inTree[u] = true;
|
|
405
|
+
if (parent[u] !== -1) {
|
|
406
|
+
edges.push([pins[u].pinId, pins[parent[u]].pinId]);
|
|
407
|
+
}
|
|
408
|
+
for (let v = 0; v < n; v++) {
|
|
409
|
+
if (!inTree[v]) {
|
|
410
|
+
const d0 = manhattan(pins[u], pins[v]);
|
|
411
|
+
const isForbidden = opts?.forbidEdge?.(pins[u], pins[v]) ?? false;
|
|
412
|
+
const d = d0 > maxDistance || isForbidden ? Number.POSITIVE_INFINITY : d0;
|
|
413
|
+
if (d < bestDist[v] || d === bestDist[v] && pins[u].pinId < pins[parent[v]]?.pinId) {
|
|
414
|
+
bestDist[v] = d;
|
|
415
|
+
parent[v] = u;
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
|
-
return
|
|
421
|
-
}
|
|
420
|
+
return edges;
|
|
421
|
+
}
|
|
422
422
|
|
|
423
423
|
// lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
|
|
424
|
+
var getPinPairKey = (pinIds) => [...pinIds].sort().join("::");
|
|
424
425
|
var MspConnectionPairSolver = class extends BaseSolver {
|
|
425
426
|
inputProblem;
|
|
426
427
|
mspConnectionPairs = [];
|
|
@@ -431,6 +432,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
431
432
|
maxMspPairDistance;
|
|
432
433
|
pinMap;
|
|
433
434
|
userNetIdByPinId;
|
|
435
|
+
directConnectionPinPairKeys;
|
|
434
436
|
constructor({ inputProblem }) {
|
|
435
437
|
super();
|
|
436
438
|
this.inputProblem = inputProblem;
|
|
@@ -449,7 +451,9 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
449
451
|
this.chipMap[chip.chipId] = chip;
|
|
450
452
|
}
|
|
451
453
|
this.userNetIdByPinId = {};
|
|
454
|
+
this.directConnectionPinPairKeys = /* @__PURE__ */ new Set();
|
|
452
455
|
for (const dc of inputProblem.directConnections) {
|
|
456
|
+
this.directConnectionPinPairKeys.add(getPinPairKey(dc.pinIds));
|
|
453
457
|
if (dc.netId) {
|
|
454
458
|
const [a, b] = dc.pinIds;
|
|
455
459
|
this.userNetIdByPinId[a] = dc.netId;
|
|
@@ -483,8 +487,9 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
483
487
|
const [pin1, pin2] = directlyConnectedPins;
|
|
484
488
|
const p1 = this.pinMap[pin1];
|
|
485
489
|
const p2 = this.pinMap[pin2];
|
|
486
|
-
const
|
|
487
|
-
|
|
490
|
+
const pinPairKey = getPinPairKey([pin1, pin2]);
|
|
491
|
+
const pairDistance = this.directConnectionPinPairKeys.has(pinPairKey) ? distance(p1, p2) : Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
|
|
492
|
+
if (pairDistance > this.maxMspPairDistance) {
|
|
488
493
|
return;
|
|
489
494
|
}
|
|
490
495
|
if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
|
|
@@ -860,6 +865,68 @@ var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, eps = EPS2) =
|
|
|
860
865
|
}
|
|
861
866
|
};
|
|
862
867
|
|
|
868
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts
|
|
869
|
+
var getSegmentAxis = (start, end) => {
|
|
870
|
+
if (isVertical(start, end)) return "x";
|
|
871
|
+
if (isHorizontal(start, end)) return "y";
|
|
872
|
+
return null;
|
|
873
|
+
};
|
|
874
|
+
var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
|
|
875
|
+
const nextPoint = path[index + 1];
|
|
876
|
+
if (!nextPoint) return true;
|
|
877
|
+
if (!isHorizontal(point, nextPoint) && !isVertical(point, nextPoint)) {
|
|
878
|
+
return false;
|
|
879
|
+
}
|
|
880
|
+
return Math.abs(point.x - nextPoint.x) + Math.abs(point.y - nextPoint.y) > 0;
|
|
881
|
+
});
|
|
882
|
+
var generateEndpointCollisionDetours = ({
|
|
883
|
+
path,
|
|
884
|
+
collidingSegmentIndex,
|
|
885
|
+
obstacle
|
|
886
|
+
}) => {
|
|
887
|
+
if (path.length !== 3) return [];
|
|
888
|
+
const lastSegmentIndex = path.length - 2;
|
|
889
|
+
if (collidingSegmentIndex !== 0 && collidingSegmentIndex !== lastSegmentIndex) {
|
|
890
|
+
return [];
|
|
891
|
+
}
|
|
892
|
+
const shouldReverse = collidingSegmentIndex === lastSegmentIndex;
|
|
893
|
+
const orderedPath = shouldReverse ? [...path].reverse() : path;
|
|
894
|
+
const [start, corner, end] = orderedPath;
|
|
895
|
+
const firstSegmentAxis = getSegmentAxis(start, corner);
|
|
896
|
+
const secondSegmentAxis = getSegmentAxis(corner, end);
|
|
897
|
+
if (!firstSegmentAxis || !secondSegmentAxis) return [];
|
|
898
|
+
if (firstSegmentAxis === secondSegmentAxis) return [];
|
|
899
|
+
const escapeCoordinates = [
|
|
900
|
+
...midBetweenPointAndRect(secondSegmentAxis, start, obstacle),
|
|
901
|
+
...midBetweenPointAndRect(secondSegmentAxis, end, obstacle)
|
|
902
|
+
];
|
|
903
|
+
const detourCoordinates = [
|
|
904
|
+
...midBetweenPointAndRect(firstSegmentAxis, start, obstacle),
|
|
905
|
+
...midBetweenPointAndRect(firstSegmentAxis, end, obstacle)
|
|
906
|
+
];
|
|
907
|
+
const detours = [];
|
|
908
|
+
for (const escapeCoordinate of [...new Set(escapeCoordinates)]) {
|
|
909
|
+
for (const detourCoordinate of [...new Set(detourCoordinates)]) {
|
|
910
|
+
const orderedDetour = firstSegmentAxis === "y" ? [
|
|
911
|
+
start,
|
|
912
|
+
{ x: escapeCoordinate, y: start.y },
|
|
913
|
+
{ x: escapeCoordinate, y: detourCoordinate },
|
|
914
|
+
{ x: end.x, y: detourCoordinate },
|
|
915
|
+
end
|
|
916
|
+
] : [
|
|
917
|
+
start,
|
|
918
|
+
{ x: start.x, y: escapeCoordinate },
|
|
919
|
+
{ x: detourCoordinate, y: escapeCoordinate },
|
|
920
|
+
{ x: detourCoordinate, y: end.y },
|
|
921
|
+
end
|
|
922
|
+
];
|
|
923
|
+
const detour = shouldReverse ? orderedDetour.reverse() : orderedDetour;
|
|
924
|
+
if (hasOnlyNonzeroOrthogonalSegments(detour)) detours.push(detour);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return detours;
|
|
928
|
+
};
|
|
929
|
+
|
|
863
930
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
|
|
864
931
|
var EPS3 = 1e-9;
|
|
865
932
|
var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
|
|
@@ -1210,6 +1277,29 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1210
1277
|
let { segIndex, rect } = collision;
|
|
1211
1278
|
const isFirstSegment = segIndex === 0;
|
|
1212
1279
|
const isLastSegment = segIndex === path.length - 2;
|
|
1280
|
+
if (path.length === 3 && (isFirstSegment || isLastSegment)) {
|
|
1281
|
+
const detours = generateEndpointCollisionDetours({
|
|
1282
|
+
path,
|
|
1283
|
+
collidingSegmentIndex: segIndex,
|
|
1284
|
+
obstacle: rect
|
|
1285
|
+
}).filter((detour) => {
|
|
1286
|
+
const key = pathKey(detour);
|
|
1287
|
+
if (this.visited.has(key)) return false;
|
|
1288
|
+
this.visited.add(key);
|
|
1289
|
+
return true;
|
|
1290
|
+
}).sort(
|
|
1291
|
+
(a2, b2) => this.pathLength(a2) - this.pathLength(b2) || this.getPinBandPenalty(a2) - this.getPinBandPenalty(b2)
|
|
1292
|
+
);
|
|
1293
|
+
for (const detour of detours) {
|
|
1294
|
+
const nextCollisionRects = new Set(collisionRects);
|
|
1295
|
+
nextCollisionRects.add(rect);
|
|
1296
|
+
this.queue.push({
|
|
1297
|
+
path: detour,
|
|
1298
|
+
collisionRects: nextCollisionRects
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1213
1303
|
if (isFirstSegment) {
|
|
1214
1304
|
if (path.length < 3) {
|
|
1215
1305
|
return;
|
|
@@ -2961,7 +3051,7 @@ var mergeLabelGroup = (group, groupKey) => {
|
|
|
2961
3051
|
};
|
|
2962
3052
|
|
|
2963
3053
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts
|
|
2964
|
-
import { distance } from "@tscircuit/math-utils";
|
|
3054
|
+
import { distance as distance2 } from "@tscircuit/math-utils";
|
|
2965
3055
|
var filterLabelsAtTraceEdges = ({
|
|
2966
3056
|
labels,
|
|
2967
3057
|
traces,
|
|
@@ -2994,8 +3084,8 @@ var filterLabelsAtTraceEdges = ({
|
|
|
2994
3084
|
if (trace.tracePath.length === 0) continue;
|
|
2995
3085
|
const startPoint = trace.tracePath[0];
|
|
2996
3086
|
const endPoint = trace.tracePath[trace.tracePath.length - 1];
|
|
2997
|
-
const startDist =
|
|
2998
|
-
const endDist =
|
|
3087
|
+
const startDist = distance2(label.center, startPoint);
|
|
3088
|
+
const endDist = distance2(label.center, endPoint);
|
|
2999
3089
|
if (startDist <= distanceThreshold || endDist <= distanceThreshold) {
|
|
3000
3090
|
isNearTraceEdge = true;
|
|
3001
3091
|
break;
|
|
@@ -4077,7 +4167,7 @@ function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
|
|
|
4077
4167
|
|
|
4078
4168
|
// lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
|
|
4079
4169
|
var NEAREST_NEIGHBOR_COUNT = 3;
|
|
4080
|
-
var
|
|
4170
|
+
var distance3 = (p1, p2) => {
|
|
4081
4171
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
4082
4172
|
};
|
|
4083
4173
|
var LongDistancePairSolver = class extends BaseSolver {
|
|
@@ -4118,7 +4208,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4118
4208
|
return [
|
|
4119
4209
|
{
|
|
4120
4210
|
pin: targetPin,
|
|
4121
|
-
distance:
|
|
4211
|
+
distance: distance3(sourcePin, targetPin)
|
|
4122
4212
|
}
|
|
4123
4213
|
];
|
|
4124
4214
|
}).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
|
|
@@ -4932,10 +5022,10 @@ var projectPointToPath = (point, path) => {
|
|
|
4932
5022
|
let bestDistance = Number.POSITIVE_INFINITY;
|
|
4933
5023
|
for (let i = 0; i < path.length - 1; i++) {
|
|
4934
5024
|
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
4935
|
-
const
|
|
4936
|
-
if (
|
|
5025
|
+
const distance4 = getDistance2(point, projectedPoint);
|
|
5026
|
+
if (distance4 < bestDistance) {
|
|
4937
5027
|
bestPoint = projectedPoint;
|
|
4938
|
-
bestDistance =
|
|
5028
|
+
bestDistance = distance4;
|
|
4939
5029
|
}
|
|
4940
5030
|
}
|
|
4941
5031
|
return bestPoint;
|
|
@@ -6565,11 +6655,11 @@ var getLabelHugDistance = (tracePath, obstacleLabel) => {
|
|
|
6565
6655
|
obstacleLabel.width,
|
|
6566
6656
|
obstacleLabel.height
|
|
6567
6657
|
);
|
|
6568
|
-
let
|
|
6658
|
+
let distance4 = 0;
|
|
6569
6659
|
for (const point of tracePath) {
|
|
6570
|
-
|
|
6660
|
+
distance4 += getPointDistanceFromRect(point, bounds);
|
|
6571
6661
|
}
|
|
6572
|
-
return
|
|
6662
|
+
return distance4;
|
|
6573
6663
|
};
|
|
6574
6664
|
var getPointDistanceFromRect = (point, rect) => {
|
|
6575
6665
|
const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
|
|
@@ -6846,16 +6936,16 @@ var Example28Solver = class extends BaseSolver {
|
|
|
6846
6936
|
const outward = dir(label.orientation);
|
|
6847
6937
|
if (outward.x === 0 && outward.y === 0) return null;
|
|
6848
6938
|
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
6849
|
-
const
|
|
6939
|
+
const distance4 = step * LABEL_OUTWARD_STEP;
|
|
6850
6940
|
const candidate = {
|
|
6851
6941
|
...label,
|
|
6852
6942
|
anchorPoint: {
|
|
6853
|
-
x: label.anchorPoint.x + outward.x *
|
|
6854
|
-
y: label.anchorPoint.y + outward.y *
|
|
6943
|
+
x: label.anchorPoint.x + outward.x * distance4,
|
|
6944
|
+
y: label.anchorPoint.y + outward.y * distance4
|
|
6855
6945
|
},
|
|
6856
6946
|
center: {
|
|
6857
|
-
x: label.center.x + outward.x *
|
|
6858
|
-
y: label.center.y + outward.y *
|
|
6947
|
+
x: label.center.x + outward.x * distance4,
|
|
6948
|
+
y: label.center.y + outward.y * distance4
|
|
6859
6949
|
}
|
|
6860
6950
|
};
|
|
6861
6951
|
const candidateWithClearance = {
|
|
@@ -7393,10 +7483,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7393
7483
|
outwardDistance,
|
|
7394
7484
|
phase = "shift"
|
|
7395
7485
|
} = params;
|
|
7396
|
-
for (let
|
|
7486
|
+
for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS9; distance4 += LABEL_SEARCH_STEP) {
|
|
7397
7487
|
const anchorPoint = {
|
|
7398
|
-
x: baseAnchor.x + direction.x *
|
|
7399
|
-
y: baseAnchor.y + direction.y *
|
|
7488
|
+
x: baseAnchor.x + direction.x * distance4,
|
|
7489
|
+
y: baseAnchor.y + direction.y * distance4
|
|
7400
7490
|
};
|
|
7401
7491
|
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
7402
7492
|
const result = this.evaluateCandidate(
|
|
@@ -7404,7 +7494,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7404
7494
|
label,
|
|
7405
7495
|
labelIndex,
|
|
7406
7496
|
phase,
|
|
7407
|
-
|
|
7497
|
+
distance4,
|
|
7408
7498
|
outwardDistance
|
|
7409
7499
|
);
|
|
7410
7500
|
this.currentCandidateResults.push(result);
|
|
@@ -7416,11 +7506,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7416
7506
|
}
|
|
7417
7507
|
return null;
|
|
7418
7508
|
}
|
|
7419
|
-
evaluateCandidate(candidate, label, labelIndex, phase,
|
|
7509
|
+
evaluateCandidate(candidate, label, labelIndex, phase, distance4, outwardDistance) {
|
|
7420
7510
|
return {
|
|
7421
7511
|
...candidate,
|
|
7422
7512
|
phase,
|
|
7423
|
-
distance:
|
|
7513
|
+
distance: distance4,
|
|
7424
7514
|
outwardDistance,
|
|
7425
7515
|
selected: false,
|
|
7426
7516
|
status: this.getCandidateStatus({
|
|
@@ -7758,10 +7848,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7758
7848
|
if (point.x < bounds.minX - EPS9 || point.x > bounds.maxX + EPS9 || point.y < bounds.minY - EPS9 || point.y > bounds.maxY + EPS9) {
|
|
7759
7849
|
continue;
|
|
7760
7850
|
}
|
|
7761
|
-
for (const [side,
|
|
7762
|
-
if (
|
|
7851
|
+
for (const [side, distance4] of getSideDistances(point, bounds)) {
|
|
7852
|
+
if (distance4 < nearestDistance) {
|
|
7763
7853
|
nearestSide = side;
|
|
7764
|
-
nearestDistance =
|
|
7854
|
+
nearestDistance = distance4;
|
|
7765
7855
|
}
|
|
7766
7856
|
}
|
|
7767
7857
|
}
|
|
@@ -7790,10 +7880,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7790
7880
|
let nearestSide = null;
|
|
7791
7881
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
7792
7882
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7793
|
-
for (const [side,
|
|
7794
|
-
if (
|
|
7883
|
+
for (const [side, distance4] of getSideDistances(point, chip.bounds)) {
|
|
7884
|
+
if (distance4 < nearestDistance) {
|
|
7795
7885
|
nearestSide = side;
|
|
7796
|
-
nearestDistance =
|
|
7886
|
+
nearestDistance = distance4;
|
|
7797
7887
|
}
|
|
7798
7888
|
}
|
|
7799
7889
|
}
|
|
@@ -8194,17 +8284,17 @@ var getTraceLength = (trace) => {
|
|
|
8194
8284
|
}
|
|
8195
8285
|
return length;
|
|
8196
8286
|
};
|
|
8197
|
-
var getPointAtTraceDistance = (trace,
|
|
8287
|
+
var getPointAtTraceDistance = (trace, distance4) => {
|
|
8198
8288
|
let pathDistance = 0;
|
|
8199
8289
|
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
8200
8290
|
const start = trace.tracePath[i];
|
|
8201
8291
|
const end = trace.tracePath[i + 1];
|
|
8202
8292
|
const segmentLength = getManhattanDistance(start, end);
|
|
8203
8293
|
const nextDistance = pathDistance + segmentLength;
|
|
8204
|
-
if (
|
|
8294
|
+
if (distance4 <= nextDistance + EPS10) {
|
|
8205
8295
|
const offset = Math.max(
|
|
8206
8296
|
0,
|
|
8207
|
-
Math.min(segmentLength,
|
|
8297
|
+
Math.min(segmentLength, distance4 - pathDistance)
|
|
8208
8298
|
);
|
|
8209
8299
|
const direction = getSegmentDirection(start, end);
|
|
8210
8300
|
return {
|
|
@@ -8329,13 +8419,13 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
|
|
|
8329
8419
|
const distances = /* @__PURE__ */ new Set();
|
|
8330
8420
|
const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
|
|
8331
8421
|
for (let i = 0; i <= maxSteps; i++) {
|
|
8332
|
-
const
|
|
8333
|
-
distances.add(roundDistance(
|
|
8422
|
+
const distance4 = Math.min(traceLength, i * CANDIDATE_STEP);
|
|
8423
|
+
distances.add(roundDistance(distance4));
|
|
8334
8424
|
}
|
|
8335
|
-
for (const
|
|
8336
|
-
distances.add(roundDistance(
|
|
8425
|
+
for (const distance4 of vertexDistances) {
|
|
8426
|
+
distances.add(roundDistance(distance4));
|
|
8337
8427
|
}
|
|
8338
|
-
return [...distances].filter((
|
|
8428
|
+
return [...distances].filter((distance4) => distance4 >= -EPS10 && distance4 <= traceLength + EPS10).sort((a, b) => a - b);
|
|
8339
8429
|
};
|
|
8340
8430
|
var getOrientationsForPoint = (params) => {
|
|
8341
8431
|
const { inputProblem, label, point, orientationConstraint } = params;
|
|
@@ -8513,7 +8603,7 @@ var getNetLabelHeight = (inputProblem, label) => {
|
|
|
8513
8603
|
(nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
|
|
8514
8604
|
)?.netLabelHeight;
|
|
8515
8605
|
};
|
|
8516
|
-
var roundDistance = (
|
|
8606
|
+
var roundDistance = (distance4) => Number(distance4.toFixed(6));
|
|
8517
8607
|
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS10 && Math.abs(point.y - label.anchorPoint.y) <= EPS10 && orientation === label.orientation;
|
|
8518
8608
|
|
|
8519
8609
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
|