@tscircuit/schematic-trace-solver 0.0.31 → 0.0.33

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.
Files changed (32) hide show
  1. package/CLAUDE.md +8 -0
  2. package/dist/index.d.ts +24 -53
  3. package/dist/index.js +494 -807
  4. package/lib/index.ts +1 -0
  5. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +54 -3
  6. package/lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines.ts +62 -0
  7. package/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts +7 -2
  8. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts +5 -11
  9. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts +37 -7
  10. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts +7 -0
  11. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +239 -0
  12. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +57 -0
  13. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts +97 -0
  14. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +65 -0
  15. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +19 -0
  16. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +14 -14
  17. package/package.json +1 -1
  18. package/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver03.page.tsx +486 -0
  19. package/site/examples/example09.page.tsx +1 -1
  20. package/tests/examples/__snapshots__/example01.snap.svg +29 -29
  21. package/tests/examples/__snapshots__/example02.snap.svg +13 -10
  22. package/tests/examples/__snapshots__/example04.snap.svg +12 -12
  23. package/tests/examples/__snapshots__/example05.snap.svg +38 -38
  24. package/tests/examples/__snapshots__/example06.snap.svg +14 -14
  25. package/tests/examples/__snapshots__/example08.snap.svg +29 -23
  26. package/tests/examples/__snapshots__/example09.snap.svg +119 -149
  27. package/tests/examples/__snapshots__/example11.snap.svg +39 -33
  28. package/tests/examples/__snapshots__/example12.snap.svg +32 -29
  29. package/tests/examples/__snapshots__/example13.snap.svg +87 -84
  30. package/tests/examples/__snapshots__/example15.snap.svg +57 -57
  31. package/tests/examples/__snapshots__/example16.snap.svg +3 -3
  32. package/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro03.test.ts +491 -0
package/dist/index.js CHANGED
@@ -139,7 +139,8 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
139
139
  for (let v = 0; v < n; v++) {
140
140
  if (!inTree[v]) {
141
141
  const d0 = manhattan(pins[u], pins[v]);
142
- const d = d0 > maxDistance ? Number.POSITIVE_INFINITY : d0;
142
+ const isForbidden = opts?.forbidEdge?.(pins[u], pins[v]) ?? false;
143
+ const d = d0 > maxDistance || isForbidden ? Number.POSITIVE_INFINITY : d0;
143
144
  if (d < bestDist[v] || d === bestDist[v] && pins[u].pinId < pins[parent[v]]?.pinId) {
144
145
  bestDist[v] = d;
145
146
  parent[v] = u;
@@ -150,14 +151,6 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
150
151
  return edges;
151
152
  }
152
153
 
153
- // lib/utils/getColorFromString.ts
154
- var getColorFromString = (string, alpha = 1) => {
155
- const hash = string.split("").reduce((acc, char) => {
156
- return acc * 31 + char.charCodeAt(0);
157
- }, 0);
158
- return `hsl(${hash % 360}, 100%, 50%, ${alpha})`;
159
- };
160
-
161
154
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts
162
155
  var getPinDirection = (pin, chip) => {
163
156
  const { x, y } = pin;
@@ -188,6 +181,126 @@ var getPinDirection = (pin, chip) => {
188
181
  return "x-";
189
182
  };
190
183
 
184
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts
185
+ var getRestrictedCenterLines = (params) => {
186
+ const { pins, inputProblem, pinIdMap, chipMap } = params;
187
+ const findAllDirectlyConnectedPins = (startPinId) => {
188
+ const visited = /* @__PURE__ */ new Set();
189
+ const queue = [startPinId];
190
+ visited.add(startPinId);
191
+ const directConns = inputProblem.directConnections || [];
192
+ while (queue.length) {
193
+ const cur = queue.shift();
194
+ for (const dc of directConns) {
195
+ if (dc.pinIds.includes(cur)) {
196
+ for (const p of dc.pinIds) {
197
+ if (!visited.has(p)) {
198
+ visited.add(p);
199
+ queue.push(p);
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+ return visited;
206
+ };
207
+ const p0 = pins[0].pinId;
208
+ const p1 = pins[1].pinId;
209
+ const relatedPinIds = /* @__PURE__ */ new Set([
210
+ ...findAllDirectlyConnectedPins(p0),
211
+ ...findAllDirectlyConnectedPins(p1)
212
+ ]);
213
+ const restrictedCenterLines = /* @__PURE__ */ new Map();
214
+ const chipFacingMap = /* @__PURE__ */ new Map();
215
+ const chipsOfFacingPins = new Set(pins.map((p) => p.chipId));
216
+ for (const pinId of relatedPinIds) {
217
+ const pin = pinIdMap.get(pinId);
218
+ if (!pin) continue;
219
+ const chip = chipMap[pin.chipId];
220
+ if (!chip) continue;
221
+ const facing = pin._facingDirection ?? getPinDirection(pin, chip);
222
+ let entry = chipFacingMap.get(chip.chipId);
223
+ if (!entry) {
224
+ entry = { center: chip.center };
225
+ const counts = { xPos: 0, xNeg: 0, yPos: 0, yNeg: 0 };
226
+ for (const cp of chip.pins) {
227
+ const cpFacing = cp._facingDirection ?? getPinDirection(cp, chip);
228
+ if (cpFacing === "x+") counts.xPos++;
229
+ if (cpFacing === "x-") counts.xNeg++;
230
+ if (cpFacing === "y+") counts.yPos++;
231
+ if (cpFacing === "y-") counts.yNeg++;
232
+ }
233
+ entry.counts = counts;
234
+ chipFacingMap.set(chip.chipId, entry);
235
+ }
236
+ if (facing === "x+") entry.hasXPos = true;
237
+ if (facing === "x-") entry.hasXNeg = true;
238
+ if (facing === "y+") entry.hasYPos = true;
239
+ if (facing === "y-") entry.hasYNeg = true;
240
+ }
241
+ for (const [chipId, faces] of chipFacingMap) {
242
+ const axes = /* @__PURE__ */ new Set();
243
+ const rcl = { axes };
244
+ const counts = faces.counts;
245
+ const anySideHasMultiplePins = !!(counts && (counts.xPos > 1 || counts.xNeg > 1 || counts.yPos > 1 || counts.yNeg > 1));
246
+ const skipCenterRestriction = !anySideHasMultiplePins && chipsOfFacingPins.has(chipId);
247
+ if (!skipCenterRestriction) {
248
+ if (faces.hasXPos && faces.hasXNeg) {
249
+ rcl.x = faces.center.x;
250
+ axes.add("x");
251
+ }
252
+ if (faces.hasYPos && faces.hasYNeg) {
253
+ rcl.y = faces.center.y;
254
+ axes.add("y");
255
+ }
256
+ }
257
+ if (axes.size > 0) {
258
+ restrictedCenterLines.set(chipId, rcl);
259
+ }
260
+ }
261
+ return restrictedCenterLines;
262
+ };
263
+
264
+ // lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines.ts
265
+ var doesPairCrossRestrictedCenterLines = (params) => {
266
+ const { inputProblem, chipMap, pinIdMap, p1, p2 } = params;
267
+ const restrictedCenterLines = getRestrictedCenterLines({
268
+ pins: [p1, p2],
269
+ inputProblem,
270
+ pinIdMap,
271
+ chipMap
272
+ });
273
+ if (restrictedCenterLines.size === 0) return false;
274
+ const EPS4 = 1e-9;
275
+ const crossesSegment = (a, b) => {
276
+ for (const [, rcl] of restrictedCenterLines) {
277
+ if (rcl.axes.has("x") && typeof rcl.x === "number") {
278
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS4) return true;
279
+ }
280
+ if (rcl.axes.has("y") && typeof rcl.y === "number") {
281
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS4) return true;
282
+ }
283
+ }
284
+ return false;
285
+ };
286
+ if (Math.abs(p1.x - p2.x) < EPS4 || Math.abs(p1.y - p2.y) < EPS4) {
287
+ return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
288
+ }
289
+ const elbowHV = { x: p2.x, y: p1.y };
290
+ const elbowVH = { x: p1.x, y: p2.y };
291
+ const hvCrosses = crossesSegment({ x: p1.x, y: p1.y }, elbowHV) || crossesSegment(elbowHV, { x: p2.x, y: p2.y });
292
+ const vhCrosses = crossesSegment({ x: p1.x, y: p1.y }, elbowVH) || crossesSegment(elbowVH, { x: p2.x, y: p2.y });
293
+ return hvCrosses && vhCrosses;
294
+ };
295
+
296
+ // lib/utils/getColorFromString.ts
297
+ var getColorFromString = (string, alpha = 1) => {
298
+ const hash = string.split("").reduce((acc, char) => {
299
+ return acc * 31 + char.charCodeAt(0);
300
+ }, 0);
301
+ return `hsl(${hash % 360}, 100%, 50%, ${alpha})`;
302
+ };
303
+
191
304
  // lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts
192
305
  var visualizeInputProblem = (inputProblem, opts = {}) => {
193
306
  const { connectionAlpha = 0.8, chipAlpha = 0.8 } = opts;
@@ -328,6 +441,16 @@ var MspConnectionPairSolver = class extends BaseSolver {
328
441
  if (manhattanDist > this.maxMspPairDistance) {
329
442
  return;
330
443
  }
444
+ const pinIdMap2 = new Map(Object.entries(this.pinMap));
445
+ if (doesPairCrossRestrictedCenterLines({
446
+ inputProblem: this.inputProblem,
447
+ chipMap: this.chipMap,
448
+ pinIdMap: pinIdMap2,
449
+ p1,
450
+ p2
451
+ })) {
452
+ return;
453
+ }
331
454
  const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
332
455
  const userNetId = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
333
456
  this.mspConnectionPairs.push({
@@ -339,11 +462,32 @@ var MspConnectionPairSolver = class extends BaseSolver {
339
462
  });
340
463
  return;
341
464
  }
465
+ const pinIdMap = new Map(Object.entries(this.pinMap));
342
466
  const msp = getOrthogonalMinimumSpanningTree(
343
467
  directlyConnectedPins.map((p) => this.pinMap[p]).filter(Boolean),
344
- { maxDistance: this.maxMspPairDistance }
468
+ {
469
+ maxDistance: this.maxMspPairDistance,
470
+ forbidEdge: (a, b) => doesPairCrossRestrictedCenterLines({
471
+ inputProblem: this.inputProblem,
472
+ chipMap: this.chipMap,
473
+ pinIdMap,
474
+ p1: a,
475
+ p2: b
476
+ })
477
+ }
345
478
  );
346
479
  for (const [pin1, pin2] of msp) {
480
+ const p1Obj = this.pinMap[pin1];
481
+ const p2Obj = this.pinMap[pin2];
482
+ if (doesPairCrossRestrictedCenterLines({
483
+ inputProblem: this.inputProblem,
484
+ chipMap: this.chipMap,
485
+ pinIdMap,
486
+ p1: p1Obj,
487
+ p2: p2Obj
488
+ })) {
489
+ continue;
490
+ }
347
491
  const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
348
492
  const userNetId = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
349
493
  this.mspConnectionPairs.push({
@@ -351,7 +495,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
351
495
  dcConnNetId: dcNetId,
352
496
  globalConnNetId,
353
497
  userNetId,
354
- pins: [this.pinMap[pin1], this.pinMap[pin2]]
498
+ pins: [p1Obj, p2Obj]
355
499
  });
356
500
  }
357
501
  }
@@ -382,11 +526,8 @@ var MspConnectionPairSolver = class extends BaseSolver {
382
526
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts
383
527
  import "graphics-debug";
384
528
 
385
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts
386
- import { getBounds as getBounds2 } from "graphics-debug";
387
-
388
- // lib/data-structures/ChipObstacleSpatialIndex.ts
389
- import Flatbush from "flatbush";
529
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
530
+ import { calculateElbow } from "calculate-elbow";
390
531
 
391
532
  // lib/solvers/GuidelinesSolver/getInputChipBounds.ts
392
533
  function getInputChipBounds(chip) {
@@ -400,456 +541,189 @@ function getInputChipBounds(chip) {
400
541
  };
401
542
  }
402
543
 
403
- // lib/data-structures/ChipObstacleSpatialIndex.ts
404
- var ChipObstacleSpatialIndex = class {
405
- chips;
406
- spatialIndex;
407
- spatialIndexIdToChip;
408
- constructor(chips) {
409
- this.chips = chips.map((chip) => ({
410
- ...chip,
411
- bounds: getInputChipBounds(chip),
412
- spatialIndexId: null
413
- }));
414
- this.spatialIndexIdToChip = /* @__PURE__ */ new Map();
415
- this.spatialIndex = new Flatbush(chips.length);
416
- for (const chip of this.chips) {
417
- chip.spatialIndexId = this.spatialIndex.add(
418
- chip.bounds.minX,
419
- chip.bounds.minY,
420
- chip.bounds.maxX,
421
- chip.bounds.maxY
422
- );
423
- this.spatialIndexIdToChip.set(chip.spatialIndexId, chip);
424
- }
425
- this.spatialIndex.finish();
426
- }
427
- getChipsInBounds(bounds) {
428
- const chipSpatialIndexIds = this.spatialIndex.search(
429
- bounds.minX,
430
- bounds.minY,
431
- bounds.maxX,
432
- bounds.maxY
433
- );
434
- return chipSpatialIndexIds.map((id) => this.spatialIndexIdToChip.get(id));
435
- }
436
- doesOrthogonalLineIntersectChip(line, opts = {}) {
437
- const excludeChipIds = opts.excludeChipIds ?? [];
438
- const [p1, p2] = line;
439
- const { x: x1, y: y1 } = p1;
440
- const { x: x2, y: y2 } = p2;
441
- const chips = this.getChipsInBounds({
442
- minX: Math.min(x1, x2),
443
- minY: Math.min(y1, y2),
444
- maxX: Math.max(x1, x2),
445
- maxY: Math.max(y1, y2)
446
- }).filter((chip) => !excludeChipIds.includes(chip.chipId));
447
- return chips.length > 0;
448
- }
544
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
545
+ var chipToRect = (chip) => {
546
+ const b = getInputChipBounds(chip);
547
+ return { chipId: chip.chipId, ...b };
449
548
  };
450
-
451
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts
452
- import { calculateElbow } from "calculate-elbow";
453
-
454
- // lib/utils/dir.ts
455
- var dir = (facingDirection) => {
456
- switch (facingDirection) {
457
- case "x+":
458
- return { x: 1, y: 0 };
459
- case "x-":
460
- return { x: -1, y: 0 };
461
- case "y+":
462
- return { x: 0, y: 1 };
463
- case "y-":
464
- return { x: 0, y: -1 };
465
- }
466
- throw new Error(`Invalid facing direction: ${facingDirection}`);
549
+ var getObstacleRects = (problem) => {
550
+ return problem.chips.map(chipToRect);
467
551
  };
468
552
 
469
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts
470
- var EPS = 1e-6;
471
- var MIN_LEN = 1e-6;
472
- var checkIfUTurnNeeded = (elbow) => {
473
- if (elbow.length !== 4) return false;
474
- const [start, p1, p2, end] = elbow;
475
- const startOrient = orientationOf(start, p1);
476
- const startFacing = startOrient === "horizontal" ? p1.x > start.x ? "x+" : "x-" : p1.y > start.y ? "y+" : "y-";
477
- const endOrient = orientationOf(p2, end);
478
- const endFacing = endOrient === "horizontal" ? end.x > p2.x ? "x+" : "x-" : end.y > p2.y ? "y+" : "y-";
479
- if (startFacing === "x+" && end.x < p1.x - EPS) return true;
480
- if (startFacing === "x-" && end.x > p1.x + EPS) return true;
481
- if (startFacing === "y+" && end.y < p1.y - EPS) return true;
482
- if (startFacing === "y-" && end.y > p1.y + EPS) return true;
483
- if (endFacing === "x-" && p2.x > end.x + EPS && startFacing === "x+")
484
- return true;
485
- if (endFacing === "x+" && p2.x < end.x - EPS && startFacing === "x-")
486
- return true;
487
- if (endFacing === "y-" && p2.y > end.y + EPS && startFacing === "y+")
488
- return true;
489
- if (endFacing === "y+" && p2.y < end.y - EPS && startFacing === "y-")
490
- return true;
491
- return false;
492
- };
493
- var expandToUTurn = (elbow) => {
494
- if (elbow.length !== 4) return elbow;
495
- const [start, p1, p2, end] = elbow;
496
- const overshoot = Math.max(
497
- Math.abs(start.x - p1.x),
498
- Math.abs(start.y - p1.y),
499
- 0.2
500
- // minimum overshoot
501
- );
502
- const startOrient = orientationOf(start, p1);
503
- const expanded = [{ ...start }];
504
- if (startOrient === "horizontal") {
505
- const startDir = p1.x > start.x ? 1 : -1;
506
- expanded.push({ x: start.x + startDir * overshoot, y: start.y });
507
- const verticalSpace = Math.max(
508
- overshoot * 2,
509
- Math.abs(end.y - start.y) + overshoot
510
- );
511
- const midY = end.y > start.y ? start.y - verticalSpace : start.y + verticalSpace;
512
- expanded.push({ x: start.x + startDir * overshoot, y: midY });
513
- const endOrient = orientationOf(p2, end);
514
- const endApproachX = endOrient === "horizontal" ? end.x > p2.x ? end.x - overshoot : end.x + overshoot : end.x;
515
- expanded.push({ x: endApproachX, y: midY });
516
- expanded.push({ x: endApproachX, y: end.y });
553
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts
554
+ var EPS = 1e-9;
555
+ var isVertical = (a, b, eps = EPS) => Math.abs(a.x - b.x) < eps;
556
+ var isHorizontal = (a, b, eps = EPS) => Math.abs(a.y - b.y) < eps;
557
+ var segmentIntersectsRect = (a, b, r, eps = EPS) => {
558
+ const vert = isVertical(a, b, eps);
559
+ const horz = isHorizontal(a, b, eps);
560
+ if (!vert && !horz) return false;
561
+ if (vert) {
562
+ const x = a.x;
563
+ if (x < r.minX - eps || x > r.maxX + eps) return false;
564
+ const segMinY = Math.min(a.y, b.y);
565
+ const segMaxY = Math.max(a.y, b.y);
566
+ const overlap = Math.min(segMaxY, r.maxY) - Math.max(segMinY, r.minY);
567
+ return overlap > eps;
517
568
  } else {
518
- const startDir = p1.y > start.y ? 1 : -1;
519
- expanded.push({ x: start.x, y: start.y + startDir * overshoot });
520
- const horizontalSpace = Math.max(
521
- overshoot * 2,
522
- Math.abs(end.x - start.x) + overshoot
523
- );
524
- const midX = end.x > start.x ? start.x - horizontalSpace : start.x + horizontalSpace;
525
- expanded.push({ x: midX, y: start.y + startDir * overshoot });
526
- const endOrient = orientationOf(p2, end);
527
- const endApproachY = endOrient === "vertical" ? end.y > p2.y ? end.y - overshoot : end.y + overshoot : end.y;
528
- expanded.push({ x: midX, y: endApproachY });
529
- expanded.push({ x: end.x, y: endApproachY });
530
- }
531
- expanded.push({ ...end });
532
- return expanded;
533
- };
534
- var isAxisAligned = (a, b) => Math.abs(a.x - b.x) < EPS || Math.abs(a.y - b.y) < EPS;
535
- var orientationOf = (a, b) => {
536
- const dx = Math.abs(a.x - b.x);
537
- const dy = Math.abs(a.y - b.y);
538
- if (dx < EPS && dy >= EPS) return "vertical";
539
- if (dy < EPS && dx >= EPS) return "horizontal";
540
- throw new Error("Non-orthogonal or degenerate segment detected.");
541
- };
542
- var assertOrthogonalPolyline = (pts) => {
543
- if (pts.length < 2) {
544
- throw new Error("Polyline must have at least two points.");
569
+ const y = a.y;
570
+ if (y < r.minY - eps || y > r.maxY + eps) return false;
571
+ const segMinX = Math.min(a.x, b.x);
572
+ const segMaxX = Math.max(a.x, b.x);
573
+ const overlap = Math.min(segMaxX, r.maxX) - Math.max(segMinX, r.minX);
574
+ return overlap > eps;
545
575
  }
576
+ };
577
+ var findFirstCollision = (pts, rects, opts = {}) => {
546
578
  for (let i = 0; i < pts.length - 1; i++) {
547
579
  const a = pts[i];
548
580
  const b = pts[i + 1];
549
- if (!isAxisAligned(a, b)) {
550
- throw new Error("Polyline contains a non-orthogonal segment.");
551
- }
552
- const manhattan = Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
553
- if (manhattan < MIN_LEN) {
554
- throw new Error("Polyline contains a near-zero length segment.");
555
- }
556
- }
557
- };
558
- var uniqSortedWithTol = (vals, tol = EPS) => {
559
- const sorted = vals.slice().sort((a, b) => a - b);
560
- const out = [];
561
- for (const v of sorted) {
562
- if (out.length === 0 || Math.abs(v - out[out.length - 1]) > tol) out.push(v);
563
- }
564
- return out;
565
- };
566
- var keyForPolyline = (pts, decimals = 9) => pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|");
567
- var preprocessElbow = (pts, tol = MIN_LEN) => {
568
- if (pts.length === 0) return [];
569
- const out = [{ ...pts[0] }];
570
- for (let i = 1; i < pts.length; i++) {
571
- const p = pts[i];
572
- const last = out[out.length - 1];
573
- const manhattan = Math.abs(p.x - last.x) + Math.abs(p.y - last.y);
574
- if (manhattan > tol) out.push({ ...p });
575
- }
576
- return out;
577
- };
578
- var collectAxisCandidates = (axis, guidelines, currentCoord) => {
579
- const positions = [];
580
- for (const g of guidelines) {
581
- if (axis === "x") {
582
- if (g.orientation === "vertical" && typeof g.x === "number") {
583
- positions.push(g.x);
584
- }
585
- } else {
586
- if (g.orientation === "horizontal" && typeof g.y === "number") {
587
- positions.push(g.y);
581
+ const excluded = opts.excludeRectIdsForSegment?.(i) ?? /* @__PURE__ */ new Set();
582
+ for (const r of rects) {
583
+ if (excluded.has(r.chipId)) continue;
584
+ if (segmentIntersectsRect(a, b, r)) {
585
+ return { segIndex: i, rect: r };
588
586
  }
589
587
  }
590
588
  }
591
- positions.push(currentCoord);
592
- return uniqSortedWithTol(positions);
589
+ return null;
593
590
  };
594
- var computeFreedom = (prev, start, end) => {
595
- const orient = orientationOf(start, end);
596
- if (orient === "horizontal") {
597
- const facing = prev.y <= start.y ? "y+" : "y-";
598
- return { axis: "y", facing };
591
+
592
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts
593
+ var EPS2 = 1e-9;
594
+ var aabbFromPoints = (a, b) => ({
595
+ minX: Math.min(a.x, b.x),
596
+ maxX: Math.max(a.x, b.x),
597
+ minY: Math.min(a.y, b.y),
598
+ maxY: Math.max(a.y, b.y)
599
+ });
600
+ var midBetweenPointAndRect = (axis, p, r, eps = EPS2) => {
601
+ if (axis === "x") {
602
+ if (p.x < r.minX - eps) {
603
+ return [(p.x + r.minX) / 2];
604
+ }
605
+ if (p.x > r.maxX + eps) {
606
+ return [(p.x + r.maxX) / 2];
607
+ }
608
+ return [r.minX - 0.2, r.maxX + 0.2];
599
609
  } else {
600
- const facing = prev.x <= start.x ? "x+" : "x-";
601
- return { axis: "x", facing };
602
- }
603
- };
604
- var cartesian = (arrays) => arrays.length === 0 ? [[]] : arrays.reduce(
605
- (acc, curr) => acc.flatMap((a) => curr.map((c) => [...a, c])),
606
- [[]]
607
- );
608
- var generateElbowVariants = ({
609
- baseElbow,
610
- guidelines
611
- }) => {
612
- const elbow = preprocessElbow(baseElbow);
613
- assertOrthogonalPolyline(elbow);
614
- const nPts = elbow.length;
615
- if (nPts < 4) {
616
- return {
617
- elbowVariants: [elbow.map((p) => ({ ...p }))],
618
- movableSegments: []
619
- };
620
- } else if (nPts === 4) {
621
- const needsUTurn = checkIfUTurnNeeded(elbow);
622
- if (needsUTurn) {
623
- const expandedElbow = expandToUTurn(elbow);
624
- return generateElbowVariants({ baseElbow: expandedElbow, guidelines });
625
- }
626
- }
627
- const firstMovableIndex = 1;
628
- const lastMovableIndex = nPts - 3;
629
- const movableSegments = [];
630
- const movableIdx = [];
631
- const axes = [];
632
- const optionsPerSegment = [];
633
- for (let i = firstMovableIndex; i <= lastMovableIndex; i++) {
634
- const prev = elbow[i - 1];
635
- const start = elbow[i];
636
- const end = elbow[i + 1];
637
- const next2 = elbow[i + 2];
638
- const { axis, facing } = computeFreedom(prev, start, end);
639
- movableSegments.push({
640
- start: { ...start },
641
- end: { ...end },
642
- freedom: facing,
643
- dir: dir(facing)
644
- });
645
- movableIdx.push(i);
646
- axes.push(axis);
647
- const currentCoord = axis === "x" ? start.x : start.y;
648
- const rawCandidates = collectAxisCandidates(axis, guidelines, currentCoord);
649
- const filtered = rawCandidates.filter((pos) => {
650
- if (axis === "y") {
651
- const lenPrev = Math.abs(prev.y - pos);
652
- const lenNext = Math.abs(next2.y - pos);
653
- return lenPrev > MIN_LEN && lenNext > MIN_LEN;
654
- } else {
655
- const lenPrev = Math.abs(prev.x - pos);
656
- const lenNext = Math.abs(next2.x - pos);
657
- return lenPrev > MIN_LEN && lenNext > MIN_LEN;
658
- }
659
- });
660
- optionsPerSegment.push(filtered);
661
- }
662
- const combos = cartesian(optionsPerSegment);
663
- const seen = /* @__PURE__ */ new Set();
664
- const elbowVariants = [];
665
- {
666
- const key = keyForPolyline(elbow);
667
- seen.add(key);
668
- elbowVariants.push(elbow.map((p) => ({ ...p })));
669
- }
670
- for (const combo of combos) {
671
- if (combo.length === 0) continue;
672
- const variant = elbow.map((p) => ({ ...p }));
673
- for (let k = 0; k < movableIdx.length; k++) {
674
- const segIndex = movableIdx[k];
675
- const axis = axes[k];
676
- const newPos = combo[k];
677
- if (typeof newPos !== "number" || Number.isNaN(newPos)) continue;
678
- if (axis === "y") {
679
- variant[segIndex] = { ...variant[segIndex], y: newPos };
680
- variant[segIndex + 1] = { ...variant[segIndex + 1], y: newPos };
681
- } else {
682
- variant[segIndex] = { ...variant[segIndex], x: newPos };
683
- variant[segIndex + 1] = { ...variant[segIndex + 1], x: newPos };
684
- }
685
- }
686
- try {
687
- assertOrthogonalPolyline(variant);
688
- } catch {
689
- continue;
610
+ if (p.y < r.minY - eps) {
611
+ return [(p.y + r.minY) / 2];
690
612
  }
691
- const key = keyForPolyline(variant);
692
- if (!seen.has(key)) {
693
- seen.add(key);
694
- elbowVariants.push(variant);
613
+ if (p.y > r.maxY + eps) {
614
+ return [(p.y + r.maxY) / 2];
695
615
  }
616
+ return [r.minY - 0.2, r.maxY + 0.2];
696
617
  }
697
- return { elbowVariants, movableSegments };
698
618
  };
699
-
700
- // lib/solvers/GuidelinesSolver/visualizeGuidelines.ts
701
- import { getBounds } from "graphics-debug";
702
- var visualizeGuidelines = ({
703
- guidelines,
704
- graphics
705
- }) => {
706
- const globalBounds = getBounds(graphics);
707
- const boundsWidth = globalBounds.maxX - globalBounds.minX;
708
- const boundsHeight = globalBounds.maxY - globalBounds.minY;
709
- globalBounds.minX -= boundsWidth * 0.3;
710
- globalBounds.maxX += boundsWidth * 0.3;
711
- globalBounds.minY -= boundsHeight * 0.3;
712
- globalBounds.maxY += boundsHeight * 0.3;
713
- for (const guideline of guidelines) {
714
- if (guideline.orientation === "horizontal") {
715
- graphics.lines.push({
716
- points: [
717
- { x: globalBounds.minX, y: guideline.y },
718
- { x: globalBounds.maxX, y: guideline.y }
719
- ],
720
- strokeColor: "rgba(0, 0, 0, 0.5)",
721
- strokeDash: "2 2"
722
- });
619
+ var candidateMidsFromSet = (axis, colliding, rectsById, collisionRectIds, aabb, eps = EPS2) => {
620
+ const setRects = [...collisionRectIds].map((id) => rectsById.get(id)).filter((r) => !!r);
621
+ if (axis === "x") {
622
+ const leftBoundaries = [aabb.minX, ...setRects.map((r) => r.maxX)].filter(
623
+ (v) => v < colliding.minX - eps
624
+ );
625
+ const rightBoundaries = [aabb.maxX, ...setRects.map((r) => r.minX)].filter(
626
+ (v) => v > colliding.maxX + eps
627
+ );
628
+ const leftNeighbor = leftBoundaries.length > 0 ? Math.max(...leftBoundaries) : void 0;
629
+ const rightNeighbor = rightBoundaries.length > 0 ? Math.min(...rightBoundaries) : void 0;
630
+ const out = [];
631
+ if (leftNeighbor !== void 0) {
632
+ out.push((leftNeighbor + colliding.minX) / 2);
723
633
  }
724
- if (guideline.orientation === "vertical") {
725
- graphics.lines.push({
726
- points: [
727
- { x: guideline.x, y: globalBounds.minY },
728
- { x: guideline.x, y: globalBounds.maxY }
729
- ],
730
- strokeColor: "rgba(0, 0, 0, 0.5)",
731
- strokeDash: "2 2"
732
- });
634
+ if (rightNeighbor !== void 0) {
635
+ out.push((colliding.maxX + rightNeighbor) / 2);
733
636
  }
734
- }
735
- return graphics;
736
- };
737
-
738
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts
739
- var getRestrictedCenterLines = (params) => {
740
- const { pins, inputProblem, pinIdMap, chipMap } = params;
741
- const findAllDirectlyConnectedPins = (startPinId) => {
742
- const visited = /* @__PURE__ */ new Set();
743
- const queue = [startPinId];
744
- visited.add(startPinId);
745
- const directConns = inputProblem.directConnections || [];
746
- while (queue.length) {
747
- const cur = queue.shift();
748
- for (const dc of directConns) {
749
- if (dc.pinIds.includes(cur)) {
750
- for (const p of dc.pinIds) {
751
- if (!visited.has(p)) {
752
- visited.add(p);
753
- queue.push(p);
754
- }
755
- }
756
- }
757
- }
637
+ return out;
638
+ } else {
639
+ const bottomBoundaries = [aabb.minY, ...setRects.map((r) => r.maxY)].filter(
640
+ (v) => v < colliding.minY - eps
641
+ );
642
+ const topBoundaries = [aabb.maxY, ...setRects.map((r) => r.minY)].filter(
643
+ (v) => v > colliding.maxY + eps
644
+ );
645
+ const bottomNeighbor = bottomBoundaries.length > 0 ? Math.max(...bottomBoundaries) : void 0;
646
+ const topNeighbor = topBoundaries.length > 0 ? Math.min(...topBoundaries) : void 0;
647
+ const out = [];
648
+ if (bottomNeighbor !== void 0) {
649
+ out.push((bottomNeighbor + colliding.minY) / 2);
758
650
  }
759
- return visited;
760
- };
761
- const p0 = pins[0].pinId;
762
- const p1 = pins[1].pinId;
763
- const relatedPinIds = /* @__PURE__ */ new Set([
764
- ...findAllDirectlyConnectedPins(p0),
765
- ...findAllDirectlyConnectedPins(p1)
766
- ]);
767
- const restrictedCenterLines = /* @__PURE__ */ new Map();
768
- const chipFacingMap = /* @__PURE__ */ new Map();
769
- const chipsOfFacingPins = new Set(pins.map((p) => p.chipId));
770
- for (const pinId of relatedPinIds) {
771
- const pin = pinIdMap.get(pinId);
772
- if (!pin) continue;
773
- const chip = chipMap[pin.chipId];
774
- if (!chip) continue;
775
- const facing = pin._facingDirection ?? getPinDirection(pin, chip);
776
- let entry = chipFacingMap.get(chip.chipId);
777
- if (!entry) {
778
- entry = { center: chip.center };
779
- const counts = { xPos: 0, xNeg: 0, yPos: 0, yNeg: 0 };
780
- for (const cp of chip.pins) {
781
- const cpFacing = cp._facingDirection ?? getPinDirection(cp, chip);
782
- if (cpFacing === "x+") counts.xPos++;
783
- if (cpFacing === "x-") counts.xNeg++;
784
- if (cpFacing === "y+") counts.yPos++;
785
- if (cpFacing === "y-") counts.yNeg++;
786
- }
787
- entry.counts = counts;
788
- chipFacingMap.set(chip.chipId, entry);
651
+ if (topNeighbor !== void 0) {
652
+ out.push((colliding.maxY + topNeighbor) / 2);
789
653
  }
790
- if (facing === "x+") entry.hasXPos = true;
791
- if (facing === "x-") entry.hasXNeg = true;
792
- if (facing === "y+") entry.hasYPos = true;
793
- if (facing === "y-") entry.hasYNeg = true;
654
+ return out;
794
655
  }
795
- for (const [chipId, faces] of chipFacingMap) {
796
- const axes = /* @__PURE__ */ new Set();
797
- const rcl = { axes };
798
- const counts = faces.counts;
799
- const anySideHasMultiplePins = !!(counts && (counts.xPos > 1 || counts.xNeg > 1 || counts.yPos > 1 || counts.yNeg > 1));
800
- const skipCenterRestriction = !anySideHasMultiplePins && chipsOfFacingPins.has(chipId);
801
- if (!skipCenterRestriction) {
802
- if (faces.hasXPos && faces.hasXNeg) {
803
- rcl.x = faces.center.x;
804
- axes.add("x");
805
- }
806
- if (faces.hasYPos && faces.hasYNeg) {
807
- rcl.y = faces.center.y;
808
- axes.add("y");
809
- }
810
- }
811
- if (axes.size > 0) {
812
- restrictedCenterLines.set(chipId, rcl);
813
- }
656
+ };
657
+
658
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
659
+ var EPS3 = 1e-9;
660
+ var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
661
+ if (segIndex < 0 || segIndex >= pts.length - 1) return null;
662
+ const a = pts[segIndex];
663
+ const b = pts[segIndex + 1];
664
+ const vert = isVertical(a, b, eps);
665
+ const horz = isHorizontal(a, b, eps);
666
+ if (!vert && !horz) return null;
667
+ if (vert && axis !== "x") return null;
668
+ if (horz && axis !== "y") return null;
669
+ const out = pts.map((p) => ({ ...p }));
670
+ if (axis === "x") {
671
+ if (Math.abs(a.x - newCoord) < eps && Math.abs(b.x - newCoord) < eps)
672
+ return null;
673
+ out[segIndex] = { ...out[segIndex], x: newCoord };
674
+ out[segIndex + 1] = { ...out[segIndex + 1], x: newCoord };
675
+ } else {
676
+ if (Math.abs(a.y - newCoord) < eps && Math.abs(b.y - newCoord) < eps)
677
+ return null;
678
+ out[segIndex] = { ...out[segIndex], y: newCoord };
679
+ out[segIndex + 1] = { ...out[segIndex + 1], y: newCoord };
680
+ }
681
+ if (segIndex - 1 >= 0) {
682
+ const p = out[segIndex - 1];
683
+ const q = out[segIndex];
684
+ const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y);
685
+ if (manhattan < eps) return null;
686
+ }
687
+ if (segIndex + 2 <= out.length - 1) {
688
+ const p = out[segIndex + 1];
689
+ const q = out[segIndex + 2];
690
+ const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y);
691
+ if (manhattan < eps) return null;
692
+ }
693
+ for (let i = 0; i < out.length - 1; i++) {
694
+ const u = out[i];
695
+ const v = out[i + 1];
696
+ if (!isHorizontal(u, v, eps) && !isVertical(u, v, eps)) return null;
814
697
  }
815
- return restrictedCenterLines;
698
+ return out;
816
699
  };
700
+ var pathKey = (pts, decimals = 6) => pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|");
817
701
 
818
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts
819
- var SchematicTraceSingleLineSolver = class extends BaseSolver {
702
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
703
+ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
820
704
  pins;
821
705
  inputProblem;
822
- guidelines;
823
706
  chipMap;
824
- movableSegments;
707
+ obstacles;
708
+ rectById;
709
+ aabb;
825
710
  baseElbow;
826
- allCandidatePaths;
827
- queuedCandidatePaths;
828
- chipObstacleSpatialIndex;
829
711
  solvedTracePath = null;
830
- // map of pinId -> pin (with chipId attached)
831
- pinIdMap = /* @__PURE__ */ new Map();
712
+ queue = [];
713
+ visited = /* @__PURE__ */ new Set();
832
714
  constructor(params) {
833
715
  super();
834
716
  this.pins = params.pins;
835
717
  this.inputProblem = params.inputProblem;
836
- this.guidelines = params.guidelines;
837
718
  this.chipMap = params.chipMap;
838
- this.chipObstacleSpatialIndex = this.inputProblem._chipObstacleSpatialIndex || new ChipObstacleSpatialIndex(this.inputProblem.chips);
839
- if (!this.inputProblem._chipObstacleSpatialIndex) {
840
- this.inputProblem._chipObstacleSpatialIndex = this.chipObstacleSpatialIndex;
841
- }
842
- for (const chip of this.inputProblem.chips) {
843
- for (const pin of chip.pins) {
844
- this.pinIdMap.set(pin.pinId, { ...pin, chipId: chip.chipId });
845
- }
846
- }
847
719
  for (const pin of this.pins) {
848
720
  if (!pin._facingDirection) {
849
721
  const chip = this.chipMap[pin.chipId];
850
722
  pin._facingDirection = getPinDirection(pin, chip);
851
723
  }
852
724
  }
725
+ this.obstacles = getObstacleRects(this.inputProblem);
726
+ this.rectById = new Map(this.obstacles.map((r) => [r.chipId, r]));
853
727
  const [pin1, pin2] = this.pins;
854
728
  this.baseElbow = calculateElbow(
855
729
  {
@@ -862,182 +736,130 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
862
736
  y: pin2.y,
863
737
  facingDirection: pin2._facingDirection
864
738
  },
865
- {
866
- overshoot: 0.2
867
- }
739
+ { overshoot: 0.2 }
868
740
  );
869
- const { elbowVariants, movableSegments } = generateElbowVariants({
870
- baseElbow: this.baseElbow,
871
- guidelines: this.guidelines
872
- });
873
- this.movableSegments = movableSegments;
874
- const getPathLength = (pts) => {
875
- let len = 0;
876
- for (let i = 0; i < pts.length - 1; i++) {
877
- const dx = pts[i + 1].x - pts[i].x;
878
- const dy = pts[i + 1].y - pts[i].y;
879
- len += Math.sqrt(dx * dx + dy * dy);
880
- }
881
- return len;
882
- };
883
- this.allCandidatePaths = [this.baseElbow, ...elbowVariants];
884
- this.queuedCandidatePaths = [...this.allCandidatePaths].sort(
885
- (a, b) => getPathLength(a) - getPathLength(b)
741
+ this.aabb = aabbFromPoints(
742
+ { x: pin1.x, y: pin1.y },
743
+ { x: pin2.x, y: pin2.y }
886
744
  );
745
+ this.queue.push({ path: this.baseElbow, collisionChipIds: /* @__PURE__ */ new Set() });
746
+ this.visited.add(pathKey(this.baseElbow));
887
747
  }
888
748
  getConstructorParams() {
889
749
  return {
890
750
  chipMap: this.chipMap,
891
751
  pins: this.pins,
892
- guidelines: this.guidelines,
893
752
  inputProblem: this.inputProblem
894
753
  };
895
754
  }
755
+ axisOfSegment(a, b) {
756
+ if (isVertical(a, b)) return "x";
757
+ if (isHorizontal(a, b)) return "y";
758
+ return null;
759
+ }
760
+ pathLength(pts) {
761
+ let sum = 0;
762
+ for (let i = 0; i < pts.length - 1; i++) {
763
+ sum += Math.abs(pts[i + 1].x - pts[i].x) + Math.abs(pts[i + 1].y - pts[i].y);
764
+ }
765
+ return sum;
766
+ }
896
767
  _step() {
897
- if (this.queuedCandidatePaths.length === 0) {
768
+ if (this.solvedTracePath) {
769
+ this.solved = true;
770
+ return;
771
+ }
772
+ const state = this.queue.shift();
773
+ if (!state) {
898
774
  this.failed = true;
899
- this.error = "No more candidate elbows, everything had collisions";
775
+ this.error = "No collision-free path found";
900
776
  return;
901
777
  }
902
- const nextCandidatePath = this.queuedCandidatePaths.shift();
903
- const restrictedCenterLines = getRestrictedCenterLines({
904
- pins: this.pins,
905
- inputProblem: this.inputProblem,
906
- pinIdMap: this.pinIdMap,
907
- chipMap: this.chipMap
908
- });
909
- for (let i = 0; i < nextCandidatePath.length - 1; i++) {
910
- const start = nextCandidatePath[i];
911
- const end = nextCandidatePath[i + 1];
912
- let excludeChipIds = [];
913
- const EPS2 = 1e-9;
914
- for (const [, rcl] of restrictedCenterLines) {
915
- if (rcl.axes.has("x") && typeof rcl.x === "number") {
916
- if ((start.x - rcl.x) * (end.x - rcl.x) < -EPS2) return;
917
- }
918
- if (rcl.axes.has("y") && typeof rcl.y === "number") {
919
- if ((start.y - rcl.y) * (end.y - rcl.y) < -EPS2) return;
920
- }
921
- }
922
- const isStartPin = this.pins.some(
923
- (pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10
924
- );
925
- const isEndPin = this.pins.some(
926
- (pin) => Math.abs(pin.x - end.x) < 1e-10 && Math.abs(pin.y - end.y) < 1e-10
927
- );
928
- if (isStartPin) {
929
- const startPin = this.pins.find(
930
- (pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10
931
- );
932
- if (startPin) {
933
- const bounds = getInputChipBounds(this.chipMap[startPin.chipId]);
934
- const dx = end.x - start.x;
935
- const dy = end.y - start.y;
936
- const EPS3 = 1e-9;
937
- const onLeft = Math.abs(start.x - bounds.minX) < 1e-9;
938
- const onRight = Math.abs(start.x - bounds.maxX) < 1e-9;
939
- const onBottom = Math.abs(start.y - bounds.minY) < 1e-9;
940
- const onTop = Math.abs(start.y - bounds.maxY) < 1e-9;
941
- const entersInterior = onLeft && dx > EPS3 || onRight && dx < -EPS3 || onBottom && dy > EPS3 || onTop && dy < -EPS3;
942
- if (entersInterior) return;
943
- if (!excludeChipIds.includes(startPin.chipId)) {
944
- excludeChipIds.push(startPin.chipId);
945
- }
946
- }
778
+ const { path, collisionChipIds } = state;
779
+ const collision = findFirstCollision(path, this.obstacles);
780
+ if (!collision) {
781
+ this.solvedTracePath = path;
782
+ this.solved = true;
783
+ return;
784
+ }
785
+ let { segIndex, rect } = collision;
786
+ const isFirstSegment = segIndex === 0;
787
+ const isLastSegment = segIndex === path.length - 2;
788
+ if (isFirstSegment) {
789
+ if (path.length < 3) {
790
+ return;
947
791
  }
948
- if (isEndPin) {
949
- const endPin = this.pins.find(
950
- (pin) => Math.abs(pin.x - end.x) < 1e-10 && Math.abs(pin.y - end.y) < 1e-10
951
- );
952
- if (endPin) {
953
- const bounds = getInputChipBounds(this.chipMap[endPin.chipId]);
954
- const dx = start.x - end.x;
955
- const dy = start.y - end.y;
956
- const EPS3 = 1e-9;
957
- const onLeft = Math.abs(end.x - bounds.minX) < 1e-9;
958
- const onRight = Math.abs(end.x - bounds.maxX) < 1e-9;
959
- const onBottom = Math.abs(end.y - bounds.minY) < 1e-9;
960
- const onTop = Math.abs(end.y - bounds.maxY) < 1e-9;
961
- const entersInterior = onLeft && dx > EPS3 || onRight && dx < -EPS3 || onBottom && dy > EPS3 || onTop && dy < -EPS3;
962
- if (entersInterior) return;
963
- if (!excludeChipIds.includes(endPin.chipId)) {
964
- excludeChipIds.push(endPin.chipId);
965
- }
966
- }
792
+ segIndex = 1;
793
+ } else if (isLastSegment) {
794
+ if (path.length < 3) {
795
+ return;
967
796
  }
968
- const obstacleOps = { excludeChipIds };
969
- const intersects = this.chipObstacleSpatialIndex.doesOrthogonalLineIntersectChip(
970
- [start, end],
971
- obstacleOps
797
+ segIndex = path.length - 3;
798
+ }
799
+ const a = path[segIndex];
800
+ const b = path[segIndex + 1];
801
+ const axis = this.axisOfSegment(a, b);
802
+ if (!axis) {
803
+ return;
804
+ }
805
+ const [PA, PB] = this.pins;
806
+ const candidates = [];
807
+ if (collisionChipIds.size === 0) {
808
+ const m1 = midBetweenPointAndRect(axis, { x: PA.x, y: PA.y }, rect);
809
+ const m2 = midBetweenPointAndRect(axis, { x: PB.x, y: PB.y }, rect);
810
+ const allCandidates = [...m1, ...m2];
811
+ const uniqueCandidates = [...new Set(allCandidates)];
812
+ candidates.push(...uniqueCandidates);
813
+ } else {
814
+ const mids = candidateMidsFromSet(
815
+ axis,
816
+ rect,
817
+ this.rectById,
818
+ collisionChipIds,
819
+ this.aabb
972
820
  );
973
- if (intersects) return;
821
+ candidates.push(...mids);
822
+ }
823
+ const newStates = [];
824
+ for (const coord of candidates) {
825
+ const newPath = shiftSegmentOrth(path, segIndex, axis, coord);
826
+ if (!newPath) continue;
827
+ const key = pathKey(newPath);
828
+ if (this.visited.has(key)) continue;
829
+ this.visited.add(key);
830
+ const nextSet = new Set(collisionChipIds);
831
+ nextSet.add(rect.chipId);
832
+ const len = this.pathLength(newPath);
833
+ newStates.push({ path: newPath, collisionRectIds: nextSet, len });
834
+ }
835
+ newStates.sort((a2, b2) => a2.len - b2.len);
836
+ for (const st of newStates) {
837
+ this.queue.push({ path: st.path, collisionChipIds: st.collisionRectIds });
974
838
  }
975
- this.solvedTracePath = nextCandidatePath;
976
- this.solved = true;
977
839
  }
978
840
  visualize() {
979
- const graphics = visualizeInputProblem(this.inputProblem, {
841
+ const g = visualizeInputProblem(this.inputProblem, {
980
842
  chipAlpha: 0.1,
981
843
  connectionAlpha: 0.1
982
844
  });
983
- const bounds = getBounds2(graphics);
984
- const boundsWidth = bounds.maxX - bounds.minX;
985
- const boundsHeight = bounds.maxY - bounds.minY;
986
- visualizeGuidelines({ guidelines: this.guidelines, graphics });
987
- for (const { start, end, dir: dir2 } of this.movableSegments) {
988
- const mid = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
989
- const dist = Math.sqrt((start.x - end.x) ** 2 + (start.y - end.y) ** 2);
990
- graphics.lines.push({
991
- points: [start, mid, end],
992
- strokeColor: "rgba(0,0,255,0.5)",
993
- strokeDash: "2 2"
994
- });
995
- graphics.lines.push({
996
- points: [
997
- mid,
998
- { x: mid.x + dir2.x * dist * 0.1, y: mid.y + dir2.y * dist * 0.1 }
999
- ],
1000
- strokeColor: "rgba(0,0,255,0.5)",
1001
- strokeDash: "2 2"
1002
- });
1003
- }
1004
- if (!this.solvedTracePath) {
1005
- if (this.queuedCandidatePaths.length > 0) {
1006
- graphics.lines.push({
1007
- points: this.queuedCandidatePaths[0],
1008
- strokeColor: "orange"
1009
- });
1010
- }
1011
- for (let i = 1; i < this.queuedCandidatePaths.length; i++) {
1012
- const candidatePath = this.queuedCandidatePaths[i];
1013
- const pi = i / this.queuedCandidatePaths.length;
1014
- graphics.lines.push({
1015
- points: candidatePath.map((p) => ({
1016
- x: p.x + pi * boundsWidth * 5e-3,
1017
- y: p.y + pi * boundsHeight * 5e-3
1018
- })),
1019
- strokeColor: getColorFromString(
1020
- `${candidatePath.reduce((acc, p) => `${acc},${p.x},${p.y}`, "")}`,
1021
- 0.5
1022
- ),
1023
- strokeDash: "8 8"
1024
- });
1025
- }
845
+ for (const { path, collisionChipIds: collisionRectIds } of this.queue) {
846
+ g.lines.push({ points: path, strokeColor: "teal", strokeDash: "2 2" });
1026
847
  }
1027
848
  if (this.solvedTracePath) {
1028
- graphics.lines.push({
1029
- points: this.solvedTracePath,
1030
- strokeColor: "green"
1031
- });
849
+ g.lines.push({ points: this.solvedTracePath, strokeColor: "green" });
850
+ } else if (this.queue.length > 0) {
851
+ g.lines.push({ points: this.queue[0].path, strokeColor: "orange" });
1032
852
  }
1033
- return graphics;
853
+ return g;
1034
854
  }
1035
855
  };
1036
856
 
857
+ // lib/solvers/GuidelinesSolver/visualizeGuidelines.ts
858
+ import { getBounds } from "graphics-debug";
859
+
1037
860
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts
1038
861
  var SchematicTraceLinesSolver = class extends BaseSolver {
1039
862
  inputProblem;
1040
- guidelines;
1041
863
  mspConnectionPairs;
1042
864
  dcConnMap;
1043
865
  globalConnMap;
@@ -1052,7 +874,6 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1052
874
  this.mspConnectionPairs = params.mspConnectionPairs;
1053
875
  this.dcConnMap = params.dcConnMap;
1054
876
  this.globalConnMap = params.globalConnMap;
1055
- this.guidelines = params.guidelines;
1056
877
  this.chipMap = params.chipMap;
1057
878
  this.queuedConnectionPairs = [...this.mspConnectionPairs];
1058
879
  }
@@ -1062,8 +883,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1062
883
  chipMap: this.chipMap,
1063
884
  mspConnectionPairs: this.mspConnectionPairs,
1064
885
  dcConnMap: this.dcConnMap,
1065
- globalConnMap: this.globalConnMap,
1066
- guidelines: this.guidelines
886
+ globalConnMap: this.globalConnMap
1067
887
  };
1068
888
  }
1069
889
  _step() {
@@ -1095,17 +915,16 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1095
915
  return;
1096
916
  }
1097
917
  const connectionPair = this.queuedConnectionPairs.shift();
1098
- this.currentConnectionPair = connectionPair;
1099
918
  if (!connectionPair) {
1100
919
  this.solved = true;
1101
920
  return;
1102
921
  }
922
+ this.currentConnectionPair = connectionPair;
1103
923
  const { pins } = connectionPair;
1104
- this.activeSubSolver = new SchematicTraceSingleLineSolver({
924
+ this.activeSubSolver = new SchematicTraceSingleLineSolver2({
1105
925
  inputProblem: this.inputProblem,
1106
926
  pins,
1107
- chipMap: this.chipMap,
1108
- guidelines: this.guidelines
927
+ chipMap: this.chipMap
1109
928
  });
1110
929
  }
1111
930
  visualize() {
@@ -1116,7 +935,6 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1116
935
  chipAlpha: 0.1,
1117
936
  connectionAlpha: 0.1
1118
937
  });
1119
- visualizeGuidelines({ guidelines: this.guidelines, graphics });
1120
938
  for (const { mspPairId, tracePath } of this.solvedTracePaths) {
1121
939
  graphics.lines.push({
1122
940
  points: tracePath,
@@ -1143,17 +961,17 @@ var applyJogToTerminalSegment = ({
1143
961
  segmentIndex: si,
1144
962
  offset,
1145
963
  JOG_SIZE,
1146
- EPS: EPS2 = 1e-6
964
+ EPS: EPS4 = 1e-6
1147
965
  }) => {
1148
966
  if (si !== 0 && si !== pts.length - 2) return;
1149
967
  const start = pts[si];
1150
968
  const end = pts[si + 1];
1151
- const isVertical = Math.abs(start.x - end.x) < EPS2;
1152
- const isHorizontal = Math.abs(start.y - end.y) < EPS2;
1153
- if (!isVertical && !isHorizontal) return;
1154
- const segDir = isVertical ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
969
+ const isVertical2 = Math.abs(start.x - end.x) < EPS4;
970
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS4;
971
+ if (!isVertical2 && !isHorizontal2) return;
972
+ const segDir = isVertical2 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1155
973
  if (si === 0) {
1156
- if (isVertical) {
974
+ if (isVertical2) {
1157
975
  const jogY = start.y + segDir * JOG_SIZE;
1158
976
  pts.splice(
1159
977
  1,
@@ -1173,7 +991,7 @@ var applyJogToTerminalSegment = ({
1173
991
  );
1174
992
  }
1175
993
  } else {
1176
- if (isVertical) {
994
+ if (isVertical2) {
1177
995
  const jogY = end.y - segDir * JOG_SIZE;
1178
996
  pts.splice(
1179
997
  si,
@@ -1216,13 +1034,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1216
1034
  }
1217
1035
  }
1218
1036
  _step() {
1219
- const EPS2 = 1e-6;
1037
+ const EPS4 = 1e-6;
1220
1038
  const offsets = this.overlappingTraceSegments.map((_, idx) => {
1221
1039
  const n = Math.floor(idx / 2) + 1;
1222
1040
  const signed = idx % 2 === 0 ? -n : n;
1223
1041
  return signed * this.SHIFT_DISTANCE;
1224
1042
  });
1225
- const eq = (a, b) => Math.abs(a - b) < EPS2;
1043
+ const eq = (a, b) => Math.abs(a - b) < EPS4;
1226
1044
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1227
1045
  this.overlappingTraceSegments.forEach((group, gidx) => {
1228
1046
  const offset = offsets[gidx];
@@ -1248,15 +1066,15 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1248
1066
  segmentIndex: si,
1249
1067
  offset,
1250
1068
  JOG_SIZE,
1251
- EPS: EPS2
1069
+ EPS: EPS4
1252
1070
  });
1253
1071
  } else {
1254
1072
  const start = pts[si];
1255
1073
  const end = pts[si + 1];
1256
- const isVertical = Math.abs(start.x - end.x) < EPS2;
1257
- const isHorizontal = Math.abs(start.y - end.y) < EPS2;
1258
- if (!isVertical && !isHorizontal) continue;
1259
- if (isVertical) {
1074
+ const isVertical2 = Math.abs(start.x - end.x) < EPS4;
1075
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS4;
1076
+ if (!isVertical2 && !isHorizontal2) continue;
1077
+ if (isVertical2) {
1260
1078
  start.x += offset;
1261
1079
  end.x += offset;
1262
1080
  } else {
@@ -1350,7 +1168,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1350
1168
  return islands;
1351
1169
  }
1352
1170
  findNextOverlapIssue() {
1353
- const EPS2 = 2e-3;
1171
+ const EPS4 = 2e-3;
1354
1172
  const netIds = Object.keys(this.traceNetIslands);
1355
1173
  for (let i = 0; i < netIds.length; i++) {
1356
1174
  for (let j = i + 1; j < netIds.length; j++) {
@@ -1368,7 +1186,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1368
1186
  const minB = Math.min(b1, b2);
1369
1187
  const maxB = Math.max(b1, b2);
1370
1188
  const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
1371
- return overlap > EPS2;
1189
+ return overlap > EPS4;
1372
1190
  };
1373
1191
  for (let pa = 0; pa < pathsA.length; pa++) {
1374
1192
  const pathA = pathsA[pa];
@@ -1376,8 +1194,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1376
1194
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
1377
1195
  const a1 = ptsA[sa];
1378
1196
  const a2 = ptsA[sa + 1];
1379
- const aVert = Math.abs(a1.x - a2.x) < EPS2;
1380
- const aHorz = Math.abs(a1.y - a2.y) < EPS2;
1197
+ const aVert = Math.abs(a1.x - a2.x) < EPS4;
1198
+ const aHorz = Math.abs(a1.y - a2.y) < EPS4;
1381
1199
  if (!aVert && !aHorz) continue;
1382
1200
  for (let pb = 0; pb < pathsB.length; pb++) {
1383
1201
  const pathB = pathsB[pb];
@@ -1385,11 +1203,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1385
1203
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
1386
1204
  const b1 = ptsB[sb];
1387
1205
  const b2 = ptsB[sb + 1];
1388
- const bVert = Math.abs(b1.x - b2.x) < EPS2;
1389
- const bHorz = Math.abs(b1.y - b2.y) < EPS2;
1206
+ const bVert = Math.abs(b1.x - b2.x) < EPS4;
1207
+ const bHorz = Math.abs(b1.y - b2.y) < EPS4;
1390
1208
  if (!bVert && !bHorz) continue;
1391
1209
  if (aVert && bVert) {
1392
- if (Math.abs(a1.x - b1.x) < EPS2) {
1210
+ if (Math.abs(a1.x - b1.x) < EPS4) {
1393
1211
  if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
1394
1212
  const keyA = `${pa}:${sa}`;
1395
1213
  const keyB = `${pb}:${sb}`;
@@ -1410,7 +1228,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1410
1228
  }
1411
1229
  }
1412
1230
  } else if (aHorz && bHorz) {
1413
- if (Math.abs(a1.y - b1.y) < EPS2) {
1231
+ if (Math.abs(a1.y - b1.y) < EPS4) {
1414
1232
  if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
1415
1233
  const keyA = `${pa}:${sa}`;
1416
1234
  const keyB = `${pb}:${sb}`;
@@ -1487,6 +1305,55 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1487
1305
  }
1488
1306
  };
1489
1307
 
1308
+ // lib/data-structures/ChipObstacleSpatialIndex.ts
1309
+ import Flatbush from "flatbush";
1310
+ var ChipObstacleSpatialIndex = class {
1311
+ chips;
1312
+ spatialIndex;
1313
+ spatialIndexIdToChip;
1314
+ constructor(chips) {
1315
+ this.chips = chips.map((chip) => ({
1316
+ ...chip,
1317
+ bounds: getInputChipBounds(chip),
1318
+ spatialIndexId: null
1319
+ }));
1320
+ this.spatialIndexIdToChip = /* @__PURE__ */ new Map();
1321
+ this.spatialIndex = new Flatbush(chips.length);
1322
+ for (const chip of this.chips) {
1323
+ chip.spatialIndexId = this.spatialIndex.add(
1324
+ chip.bounds.minX,
1325
+ chip.bounds.minY,
1326
+ chip.bounds.maxX,
1327
+ chip.bounds.maxY
1328
+ );
1329
+ this.spatialIndexIdToChip.set(chip.spatialIndexId, chip);
1330
+ }
1331
+ this.spatialIndex.finish();
1332
+ }
1333
+ getChipsInBounds(bounds) {
1334
+ const chipSpatialIndexIds = this.spatialIndex.search(
1335
+ bounds.minX,
1336
+ bounds.minY,
1337
+ bounds.maxX,
1338
+ bounds.maxY
1339
+ );
1340
+ return chipSpatialIndexIds.map((id) => this.spatialIndexIdToChip.get(id));
1341
+ }
1342
+ doesOrthogonalLineIntersectChip(line, opts = {}) {
1343
+ const excludeChipIds = opts.excludeChipIds ?? [];
1344
+ const [p1, p2] = line;
1345
+ const { x: x1, y: y1 } = p1;
1346
+ const { x: x2, y: y2 } = p2;
1347
+ const chips = this.getChipsInBounds({
1348
+ minX: Math.min(x1, x2),
1349
+ minY: Math.min(y1, y2),
1350
+ maxX: Math.max(x1, x2),
1351
+ maxY: Math.max(y1, y2)
1352
+ }).filter((chip) => !excludeChipIds.includes(chip.chipId));
1353
+ return chips.length > 0;
1354
+ }
1355
+ };
1356
+
1490
1357
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
1491
1358
  var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
1492
1359
  var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
@@ -1524,24 +1391,24 @@ function getRectBounds(center, w, h) {
1524
1391
  }
1525
1392
 
1526
1393
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
1527
- function segmentIntersectsRect(p1, p2, rect, EPS2 = 1e-9) {
1528
- const isVert = Math.abs(p1.x - p2.x) < EPS2;
1529
- const isHorz = Math.abs(p1.y - p2.y) < EPS2;
1394
+ function segmentIntersectsRect2(p1, p2, rect, EPS4 = 1e-9) {
1395
+ const isVert = Math.abs(p1.x - p2.x) < EPS4;
1396
+ const isHorz = Math.abs(p1.y - p2.y) < EPS4;
1530
1397
  if (!isVert && !isHorz) return false;
1531
1398
  if (isVert) {
1532
1399
  const x = p1.x;
1533
- if (x < rect.minX - EPS2 || x > rect.maxX + EPS2) return false;
1400
+ if (x < rect.minX - EPS4 || x > rect.maxX + EPS4) return false;
1534
1401
  const segMinY = Math.min(p1.y, p2.y);
1535
1402
  const segMaxY = Math.max(p1.y, p2.y);
1536
1403
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
1537
- return overlap > EPS2;
1404
+ return overlap > EPS4;
1538
1405
  } else {
1539
1406
  const y = p1.y;
1540
- if (y < rect.minY - EPS2 || y > rect.maxY + EPS2) return false;
1407
+ if (y < rect.minY - EPS4 || y > rect.maxY + EPS4) return false;
1541
1408
  const segMinX = Math.min(p1.x, p2.x);
1542
1409
  const segMaxX = Math.max(p1.x, p2.x);
1543
1410
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
1544
- return overlap > EPS2;
1411
+ return overlap > EPS4;
1545
1412
  }
1546
1413
  }
1547
1414
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -1549,7 +1416,7 @@ function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex)
1549
1416
  const pts = solved.tracePath;
1550
1417
  for (let i = 0; i < pts.length - 1; i++) {
1551
1418
  if (pairId === hostPathId && i === hostSegIndex) continue;
1552
- if (segmentIntersectsRect(pts[i], pts[i + 1], bounds))
1419
+ if (segmentIntersectsRect2(pts[i], pts[i + 1], bounds))
1553
1420
  return { hasIntersection: true, mspPairId: pairId, segIndex: i };
1554
1421
  }
1555
1422
  }
@@ -1898,14 +1765,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1898
1765
  };
1899
1766
  let bestCandidate = null;
1900
1767
  let bestScore = -Infinity;
1901
- const EPS2 = 1e-6;
1768
+ const EPS4 = 1e-6;
1902
1769
  for (const curr of tracesToScan) {
1903
1770
  const pts = curr.tracePath.slice();
1904
1771
  for (let si = 0; si < pts.length - 1; si++) {
1905
1772
  const a = pts[si];
1906
1773
  const b = pts[si + 1];
1907
- const isH = Math.abs(a.y - b.y) < EPS2;
1908
- const isV = Math.abs(a.x - b.x) < EPS2;
1774
+ const isH = Math.abs(a.y - b.y) < EPS4;
1775
+ const isV = Math.abs(a.x - b.x) < EPS4;
1909
1776
  if (!isH && !isV) continue;
1910
1777
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
1911
1778
  const candidateOrients = orientations.filter(
@@ -2237,187 +2104,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2237
2104
  };
2238
2105
 
2239
2106
  // lib/solvers/GuidelinesSolver/GuidelinesSolver.ts
2240
- import { getBounds as getBounds4 } from "graphics-debug";
2241
-
2242
- // lib/solvers/GuidelinesSolver/getGeneratorForAllChipPairs.ts
2243
- var getGeneratorForAllChipPairs = (chips) => {
2244
- return (function* () {
2245
- for (let i = 0; i < chips.length; i++) {
2246
- for (let j = 0; j < chips.length; j++) {
2247
- if (i !== j) {
2248
- yield [chips[i], chips[j]];
2249
- }
2250
- }
2251
- }
2252
- })();
2253
- };
2254
-
2255
- // lib/solvers/GuidelinesSolver/getHorizontalGuidelineY.ts
2256
- function getHorizontalGuidelineY(chip1Bounds, chip2Bounds) {
2257
- if (chip1Bounds.maxY <= chip2Bounds.minY) {
2258
- return (chip1Bounds.maxY + chip2Bounds.minY) / 2;
2259
- }
2260
- if (chip2Bounds.maxY <= chip1Bounds.minY) {
2261
- return (chip2Bounds.maxY + chip1Bounds.minY) / 2;
2262
- }
2263
- const overlapMinY = Math.max(chip1Bounds.minY, chip2Bounds.minY);
2264
- const overlapMaxY = Math.min(chip1Bounds.maxY, chip2Bounds.maxY);
2265
- return (overlapMinY + overlapMaxY) / 2;
2266
- }
2267
-
2268
- // lib/solvers/GuidelinesSolver/getVerticalGuidelineX.ts
2269
- function getVerticalGuidelineX(chip1Bounds, chip2Bounds) {
2270
- if (chip1Bounds.maxX <= chip2Bounds.minX) {
2271
- return (chip1Bounds.maxX + chip2Bounds.minX) / 2;
2272
- }
2273
- if (chip2Bounds.maxX <= chip1Bounds.minX) {
2274
- return (chip2Bounds.maxX + chip1Bounds.minX) / 2;
2275
- }
2276
- const overlapMinX = Math.max(chip1Bounds.minX, chip2Bounds.minX);
2277
- const overlapMaxX = Math.min(chip1Bounds.maxX, chip2Bounds.maxX);
2278
- return (overlapMinX + overlapMaxX) / 2;
2279
- }
2280
-
2281
- // lib/solvers/GuidelinesSolver/getInputProblemBounds.ts
2282
- var getInputProblemBounds = (inputProblem) => {
2283
- const bounds = {
2284
- minX: Infinity,
2285
- maxX: -Infinity,
2286
- minY: Infinity,
2287
- maxY: -Infinity
2288
- };
2289
- for (const chip of inputProblem.chips) {
2290
- const chipBounds = getInputChipBounds(chip);
2291
- bounds.minX = Math.min(bounds.minX, chipBounds.minX);
2292
- bounds.maxX = Math.max(bounds.maxX, chipBounds.maxX);
2293
- bounds.minY = Math.min(bounds.minY, chipBounds.minY);
2294
- bounds.maxY = Math.max(bounds.maxY, chipBounds.maxY);
2295
- }
2296
- return bounds;
2297
- };
2298
-
2299
- // lib/solvers/GuidelinesSolver/GuidelinesSolver.ts
2300
- var GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS = 0.1;
2301
- var GuidelinesSolver = class extends BaseSolver {
2302
- inputProblem;
2303
- guidelines;
2304
- chipPairsGenerator;
2305
- usedXGuidelines;
2306
- usedYGuidelines;
2307
- constructor(params) {
2308
- super();
2309
- this.inputProblem = params.inputProblem;
2310
- const inputProblemBounds = getInputProblemBounds(this.inputProblem);
2311
- this.guidelines = [
2312
- {
2313
- orientation: "horizontal",
2314
- y: inputProblemBounds.minY - GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS,
2315
- x: void 0
2316
- },
2317
- {
2318
- orientation: "horizontal",
2319
- y: inputProblemBounds.maxY + GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS,
2320
- x: void 0
2321
- },
2322
- {
2323
- orientation: "vertical",
2324
- y: void 0,
2325
- x: inputProblemBounds.minX - GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS
2326
- },
2327
- {
2328
- orientation: "vertical",
2329
- y: void 0,
2330
- x: inputProblemBounds.maxX + GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS
2331
- }
2332
- ];
2333
- this.chipPairsGenerator = getGeneratorForAllChipPairs(
2334
- this.inputProblem.chips
2335
- );
2336
- this.usedXGuidelines = /* @__PURE__ */ new Set();
2337
- this.usedYGuidelines = /* @__PURE__ */ new Set();
2338
- }
2339
- getConstructorParams() {
2340
- return {
2341
- inputProblem: this.inputProblem
2342
- };
2343
- }
2344
- _step() {
2345
- const { done, value: chipPair } = this.chipPairsGenerator.next();
2346
- if (done) {
2347
- this.solved = true;
2348
- return;
2349
- }
2350
- const [chip1, chip2] = chipPair;
2351
- const chip1Bounds = getInputChipBounds(chip1);
2352
- const chip2Bounds = getInputChipBounds(chip2);
2353
- const horizontalGuidelineY = getHorizontalGuidelineY(
2354
- chip1Bounds,
2355
- chip2Bounds
2356
- );
2357
- const verticalGuidelineX = getVerticalGuidelineX(chip1Bounds, chip2Bounds);
2358
- if (!this.usedYGuidelines.has(horizontalGuidelineY)) {
2359
- this.usedYGuidelines.add(horizontalGuidelineY);
2360
- this.guidelines.push({
2361
- orientation: "horizontal",
2362
- y: horizontalGuidelineY,
2363
- x: void 0
2364
- });
2365
- }
2366
- if (!this.usedXGuidelines.has(verticalGuidelineX)) {
2367
- this.usedXGuidelines.add(verticalGuidelineX);
2368
- this.guidelines.push({
2369
- orientation: "vertical",
2370
- y: void 0,
2371
- x: verticalGuidelineX
2372
- });
2373
- }
2374
- }
2375
- visualize() {
2376
- const graphics = visualizeInputProblem(this.inputProblem);
2377
- const bounds = getBounds4(graphics);
2378
- const boundsWidth = bounds.maxX - bounds.minX;
2379
- const boundsHeight = bounds.maxY - bounds.minY;
2380
- bounds.minX -= boundsWidth * 0.3;
2381
- bounds.maxX += boundsWidth * 0.3;
2382
- bounds.minY -= boundsHeight * 0.3;
2383
- bounds.maxY += boundsHeight * 0.3;
2384
- for (const guideline of this.guidelines) {
2385
- if (guideline.orientation === "horizontal") {
2386
- graphics.lines.push({
2387
- points: [
2388
- {
2389
- x: bounds.minX,
2390
- y: guideline.y
2391
- },
2392
- {
2393
- x: bounds.maxX,
2394
- y: guideline.y
2395
- }
2396
- ],
2397
- strokeColor: "rgba(0, 0, 0, 0.5)",
2398
- strokeDash: "2 2"
2399
- });
2400
- }
2401
- if (guideline.orientation === "vertical") {
2402
- graphics.lines.push({
2403
- points: [
2404
- {
2405
- x: guideline.x,
2406
- y: bounds.minY
2407
- },
2408
- {
2409
- x: guideline.x,
2410
- y: bounds.maxY
2411
- }
2412
- ],
2413
- strokeColor: "rgba(0, 0, 0, 0.5)",
2414
- strokeDash: "2 2"
2415
- });
2416
- }
2417
- }
2418
- return graphics;
2419
- }
2420
- };
2107
+ import { getBounds as getBounds3 } from "graphics-debug";
2421
2108
 
2422
2109
  // lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts
2423
2110
  var correctPinsInsideChips = (problem) => {
@@ -2481,7 +2168,7 @@ function definePipelineStep(solverName, solverClass, getConstructorParams, opts
2481
2168
  }
2482
2169
  var SchematicTracePipelineSolver = class extends BaseSolver {
2483
2170
  mspConnectionPairSolver;
2484
- guidelinesSolver;
2171
+ // guidelinesSolver?: GuidelinesSolver
2485
2172
  schematicTraceLinesSolver;
2486
2173
  traceOverlapShiftSolver;
2487
2174
  netLabelPlacementSolver;
@@ -2500,19 +2187,18 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
2500
2187
  }
2501
2188
  }
2502
2189
  ),
2503
- definePipelineStep(
2504
- "guidelinesSolver",
2505
- GuidelinesSolver,
2506
- () => [
2507
- {
2508
- inputProblem: this.inputProblem
2509
- }
2510
- ],
2511
- {
2512
- onSolved: (guidelinesSolver) => {
2513
- }
2514
- }
2515
- ),
2190
+ // definePipelineStep(
2191
+ // "guidelinesSolver",
2192
+ // GuidelinesSolver,
2193
+ // () => [
2194
+ // {
2195
+ // inputProblem: this.inputProblem,
2196
+ // },
2197
+ // ],
2198
+ // {
2199
+ // onSolved: (guidelinesSolver) => {},
2200
+ // },
2201
+ // ),
2516
2202
  definePipelineStep(
2517
2203
  "schematicTraceLinesSolver",
2518
2204
  SchematicTraceLinesSolver,
@@ -2522,7 +2208,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
2522
2208
  dcConnMap: this.mspConnectionPairSolver.dcConnMap,
2523
2209
  globalConnMap: this.mspConnectionPairSolver.globalConnMap,
2524
2210
  inputProblem: this.inputProblem,
2525
- guidelines: this.guidelinesSolver.guidelines,
2211
+ // guidelines: this.guidelinesSolver!.guidelines,
2526
2212
  chipMap: this.mspConnectionPairSolver.chipMap
2527
2213
  }
2528
2214
  ],
@@ -2669,5 +2355,6 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
2669
2355
  }
2670
2356
  };
2671
2357
  export {
2672
- SchematicTracePipelineSolver
2358
+ SchematicTracePipelineSolver,
2359
+ SchematicTraceSingleLineSolver2
2673
2360
  };