@tscircuit/schematic-trace-solver 0.0.97 → 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.
Files changed (50) hide show
  1. package/dist/index.d.ts +10 -0
  2. package/dist/index.js +1077 -519
  3. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +19 -9
  4. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +30 -0
  5. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts +84 -0
  6. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +45 -1
  7. package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +55 -13
  8. package/lib/solvers/TraceCleanupSolver/alignSameNetRails.ts +66 -0
  9. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts +124 -0
  10. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts +108 -0
  11. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts +121 -0
  12. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts +114 -0
  13. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts +27 -0
  14. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts +30 -0
  15. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/scoreRailAlignment.ts +67 -0
  16. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts +34 -0
  17. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +1 -60
  18. package/lib/utils/doesPathCoincideWithTraces.ts +61 -0
  19. package/package.json +1 -1
  20. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +18 -18
  21. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +7 -7
  22. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +5 -7
  23. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +6 -12
  24. package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +1 -1
  25. package/tests/examples/__snapshots__/example02.snap.svg +1 -1
  26. package/tests/examples/__snapshots__/example09.snap.svg +9 -13
  27. package/tests/examples/__snapshots__/example14.snap.svg +1 -1
  28. package/tests/examples/__snapshots__/example18.snap.svg +1 -1
  29. package/tests/examples/__snapshots__/example21.snap.svg +30 -32
  30. package/tests/examples/__snapshots__/example29.snap.svg +13 -19
  31. package/tests/examples/__snapshots__/example46.snap.svg +1 -1
  32. package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +1 -1
  33. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +1 -1
  34. package/tests/repros/__snapshots__/repro129-host-custom-symbol-passives.snap.svg +60 -0
  35. package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +1 -1
  36. package/tests/repros/assets/repro129-host-custom-symbol-passives.input.json +94 -0
  37. package/tests/repros/repro-rp2040-gamepad-trace-alignment.test.ts +24 -0
  38. package/tests/repros/repro129-host-custom-symbol-passives.test.ts +16 -0
  39. package/tests/solvers/MspConnectionPairSolver/msp-connection-pair-solver-direct-connection-distance.test.ts +46 -0
  40. package/tests/solvers/SchematicTraceSingleLineSolver2/generate-endpoint-collision-detours.test.ts +38 -0
  41. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-component-scope.test.ts +38 -0
  42. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-eligible-traces.test.ts +28 -0
  43. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-horizontal.test.ts +62 -0
  44. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-label-anchor.test.ts +36 -0
  45. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-label-junction.test.ts +42 -0
  46. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-obstacle.test.ts +28 -0
  47. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-pipeline-label-connector.test.ts +52 -0
  48. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts +25 -0
  49. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-visible-length.test.ts +40 -0
  50. package/tests/solvers/TraceCleanupSolver/fixtures/alignSameNetRails.ts +89 -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 "connectivity-map";
79
+ import { distance } from "@tscircuit/math-utils";
80
80
 
81
- // lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem.ts
82
- import { ConnectivityMap } from "connectivity-map";
83
- var getConnectivityMapsFromInputProblem = (inputProblem) => {
84
- const directConnMap = new ConnectivityMap({});
85
- for (const directConn of inputProblem.directConnections) {
86
- directConnMap.addConnections([
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/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts
98
- function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
99
- const n = pins.length;
100
- const maxDistance = opts?.maxDistance ?? Number.POSITIVE_INFINITY;
101
- if (n <= 1) return [];
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
- bestDist[startIndex] = 0;
120
- const edges = [];
121
- for (let iter = 0; iter < n; iter++) {
122
- let u = -1;
123
- let best = Number.POSITIVE_INFINITY;
124
- let bestId = "";
125
- for (let i = 0; i < n; i++) {
126
- if (!inTree[i]) {
127
- const d = bestDist[i];
128
- if (d < best || d === best && (bestId === "" || pins[i].pinId < bestId)) {
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 edges;
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/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
-
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
- return sectionByPinId.get(pin.pinId);
311
- };
312
- var arePinsInDifferentSchematicSections = (inputProblem, p1, p2) => {
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
- if (sectionByChipId.size === 0) return false;
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/SchematicTracePipelineSolver/visualizeInputProblem.ts
329
- var visualizeInputProblem = (inputProblem, opts = {}) => {
330
- const {
331
- connectionAlpha = 0.8,
332
- chipAlpha = 0.8,
333
- hideRatsNet = inputProblem._hideRatsNet ?? false
334
- } = opts;
335
- const graphics = {
336
- lines: [],
337
- points: [],
338
- rects: []
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
- for (const textBox of inputProblem.textBoxes ?? []) {
365
- graphics.rects.push({
366
- label: textBox.text ?? "schematic_text",
367
- center: textBox.center,
368
- width: textBox.width,
369
- height: textBox.height,
370
- fill: "rgba(160, 0, 220, 0.14)",
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
- if (!hideRatsNet) {
375
- for (const directConn of inputProblem.directConnections) {
376
- const [pinId1, pinId2] = directConn.pinIds;
377
- const pin1 = pinIdMap.get(pinId1);
378
- const pin2 = pinIdMap.get(pinId2);
379
- if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
380
- continue;
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
- for (const netConn of inputProblem.netConnections) {
400
- const pins = netConn.pinIds.map((pinId) => pinIdMap.get(pinId));
401
- for (let i = 0; i < pins.length - 1; i++) {
402
- for (let j = i + 1; j < pins.length; j++) {
403
- const pin1 = pins[i];
404
- const pin2 = pins[j];
405
- if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
406
- continue;
407
- }
408
- graphics.lines.push({
409
- points: [
410
- { x: pin1.x, y: pin1.y },
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 graphics;
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 manhattanDist = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
487
- if (manhattanDist > this.maxMspPairDistance) {
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)) {
@@ -856,8 +861,70 @@ var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, eps = EPS2) =
856
861
  if (topNeighbor !== void 0) {
857
862
  out.push((colliding.maxY + topNeighbor) / 2);
858
863
  }
859
- return out;
864
+ return out;
865
+ }
866
+ };
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
+ }
860
926
  }
927
+ return detours;
861
928
  };
862
929
 
863
930
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
@@ -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;
@@ -1440,12 +1530,12 @@ var applyJogToTerminalSegment = ({
1440
1530
  if (si !== 0 && si !== pts.length - 2) return;
1441
1531
  const start = pts[si];
1442
1532
  const end = pts[si + 1];
1443
- const isVertical4 = Math.abs(start.x - end.x) < EPS11;
1444
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS11;
1445
- if (!isVertical4 && !isHorizontal3) return;
1446
- const segDir = isVertical4 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1533
+ const isVertical5 = Math.abs(start.x - end.x) < EPS11;
1534
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS11;
1535
+ if (!isVertical5 && !isHorizontal4) return;
1536
+ const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1447
1537
  if (pts.length === 2) {
1448
- if (isVertical4) {
1538
+ if (isVertical5) {
1449
1539
  const jogYNearStart = start.y + segDir * JOG_SIZE;
1450
1540
  const jogYNearEnd = end.y - segDir * JOG_SIZE;
1451
1541
  pts.splice(
@@ -1471,7 +1561,7 @@ var applyJogToTerminalSegment = ({
1471
1561
  return;
1472
1562
  }
1473
1563
  if (si === 0) {
1474
- if (isVertical4) {
1564
+ if (isVertical5) {
1475
1565
  const jogY = start.y + segDir * JOG_SIZE;
1476
1566
  pts.splice(
1477
1567
  1,
@@ -1491,7 +1581,7 @@ var applyJogToTerminalSegment = ({
1491
1581
  );
1492
1582
  }
1493
1583
  } else {
1494
- if (isVertical4) {
1584
+ if (isVertical5) {
1495
1585
  const jogY = end.y - segDir * JOG_SIZE;
1496
1586
  pts.splice(
1497
1587
  si,
@@ -1587,10 +1677,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1587
1677
  } else {
1588
1678
  const start = pts[si];
1589
1679
  const end = pts[si + 1];
1590
- const isVertical4 = Math.abs(start.x - end.x) < EPS4;
1591
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS4;
1592
- if (!isVertical4 && !isHorizontal3) continue;
1593
- if (isVertical4) {
1680
+ const isVertical5 = Math.abs(start.x - end.x) < EPS4;
1681
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
1682
+ if (!isVertical5 && !isHorizontal4) continue;
1683
+ if (isVertical5) {
1594
1684
  start.x += offset;
1595
1685
  end.x += offset;
1596
1686
  } else {
@@ -1619,11 +1709,11 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1619
1709
  }) {
1620
1710
  const blockingCollisions = this.getBlockingCollisions({ group, offset });
1621
1711
  if (blockingCollisions.length === 0) return offset;
1622
- const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical: isVertical4, obstacle }) => {
1712
+ const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical: isVertical5, obstacle }) => {
1623
1713
  let orig;
1624
1714
  let min;
1625
1715
  let max;
1626
- if (isVertical4) {
1716
+ if (isVertical5) {
1627
1717
  orig = start.x;
1628
1718
  min = obstacle.minX;
1629
1719
  max = obstacle.maxX;
@@ -1635,8 +1725,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1635
1725
  const next = orig + candidateOffset;
1636
1726
  return orig <= min + EPS4 && next >= max - EPS4 || orig >= max - EPS4 && next <= min + EPS4;
1637
1727
  });
1638
- const edges = blockingCollisions.map(({ start, isVertical: isVertical4, obstacle }) => {
1639
- if (isVertical4) {
1728
+ const edges = blockingCollisions.map(({ start, isVertical: isVertical5, obstacle }) => {
1729
+ if (isVertical5) {
1640
1730
  return {
1641
1731
  coord: start.x,
1642
1732
  lowEdge: obstacle.minX,
@@ -1670,10 +1760,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1670
1760
  const start = trace.tracePath[traceSegmentIndex];
1671
1761
  const end = trace.tracePath[traceSegmentIndex + 1];
1672
1762
  if (!start || !end) continue;
1673
- const isVertical4 = Math.abs(start.x - end.x) < EPS4;
1674
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS4;
1675
- if (!isVertical4 && !isHorizontal3) continue;
1676
- const shiftedSegment = isVertical4 ? [
1763
+ const isVertical5 = Math.abs(start.x - end.x) < EPS4;
1764
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
1765
+ if (!isVertical5 && !isHorizontal4) continue;
1766
+ const shiftedSegment = isVertical5 ? [
1677
1767
  { ...start, x: start.x + offset },
1678
1768
  { ...end, x: end.x + offset }
1679
1769
  ] : [
@@ -1684,7 +1774,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1684
1774
  if (collision) {
1685
1775
  blockingCollisions.push({
1686
1776
  start,
1687
- isVertical: isVertical4,
1777
+ isVertical: isVertical5,
1688
1778
  obstacle: collision.rect
1689
1779
  });
1690
1780
  }
@@ -1867,9 +1957,9 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1867
1957
  for (let i = 0; i < tracePath.length - 1; i++) {
1868
1958
  const p1 = tracePath[i];
1869
1959
  const p2 = tracePath[i + 1];
1870
- const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS11;
1871
- const isVertical4 = Math.abs(p1.x - p2.x) < EPS11;
1872
- if (!isHorizontal3 && !isVertical4) {
1960
+ const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS11;
1961
+ const isVertical5 = Math.abs(p1.x - p2.x) < EPS11;
1962
+ if (!isHorizontal4 && !isVertical5) {
1873
1963
  return { mspPairId, tracePath, i, p1, p2 };
1874
1964
  }
1875
1965
  }
@@ -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 = distance(label.center, startPoint);
2998
- const endDist = distance(label.center, endPoint);
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;
@@ -3478,19 +3568,18 @@ var generateRerouteCandidates = ({
3478
3568
  return [...fourPointCandidates, ...snipReconnectCandidates];
3479
3569
  };
3480
3570
 
3481
- // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
3482
- var MAX_TRIES = 5;
3571
+ // lib/utils/doesPathCoincideWithTraces.ts
3483
3572
  var COINCIDENT_EPS = 2e-3;
3484
3573
  var doesPathCoincideWithTraces = (path, traces) => {
3485
3574
  const rangesOverlap1D = (a1, a2, b1, b2) => Math.min(Math.max(a1, a2), Math.max(b1, b2)) - Math.max(Math.min(a1, a2), Math.min(b1, b2)) > COINCIDENT_EPS;
3486
3575
  for (let i = 0; i < path.length - 1; i++) {
3487
3576
  const pathSegStart = path[i];
3488
3577
  const pathSegEnd = path[i + 1];
3489
- const isVertical4 = Math.abs(pathSegStart.x - pathSegEnd.x) < COINCIDENT_EPS;
3490
- const isHorizontal3 = Math.abs(pathSegStart.y - pathSegEnd.y) < COINCIDENT_EPS;
3491
- if (!isVertical4 && !isHorizontal3) continue;
3492
- const crossAxis = isVertical4 ? "x" : "y";
3493
- const alongAxis = isVertical4 ? "y" : "x";
3578
+ const isVertical5 = Math.abs(pathSegStart.x - pathSegEnd.x) < COINCIDENT_EPS;
3579
+ const isHorizontal4 = Math.abs(pathSegStart.y - pathSegEnd.y) < COINCIDENT_EPS;
3580
+ if (!isVertical5 && !isHorizontal4) continue;
3581
+ const crossAxis = isVertical5 ? "x" : "y";
3582
+ const alongAxis = isVertical5 ? "y" : "x";
3494
3583
  for (const trace of traces) {
3495
3584
  for (let j = 0; j < trace.tracePath.length - 1; j++) {
3496
3585
  const traceSegStart = trace.tracePath[j];
@@ -3512,6 +3601,9 @@ var doesPathCoincideWithTraces = (path, traces) => {
3512
3601
  }
3513
3602
  return false;
3514
3603
  };
3604
+
3605
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
3606
+ var MAX_TRIES = 5;
3515
3607
  var SingleOverlapSolver = class extends BaseSolver {
3516
3608
  queuedCandidatePaths;
3517
3609
  solvedTracePath = null;
@@ -4075,7 +4167,7 @@ function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
4075
4167
 
4076
4168
  // lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
4077
4169
  var NEAREST_NEIGHBOR_COUNT = 3;
4078
- var distance2 = (p1, p2) => {
4170
+ var distance3 = (p1, p2) => {
4079
4171
  return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
4080
4172
  };
4081
4173
  var LongDistancePairSolver = class extends BaseSolver {
@@ -4116,7 +4208,7 @@ var LongDistancePairSolver = class extends BaseSolver {
4116
4208
  return [
4117
4209
  {
4118
4210
  pin: targetPin,
4119
- distance: distance2(sourcePin, targetPin)
4211
+ distance: distance3(sourcePin, targetPin)
4120
4212
  }
4121
4213
  ];
4122
4214
  }).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
@@ -4668,26 +4760,610 @@ var balanceZShapes = ({
4668
4760
  if (Math.abs(len1Original - len2Original) < 1e-3) {
4669
4761
  continue;
4670
4762
  }
4671
- if (isHVHZShape) {
4672
- const idealX = (p1.x + p4.x) / 2;
4673
- p2New = { x: idealX, y: p2.y };
4674
- p3New = { x: idealX, y: p3.y };
4675
- } else {
4676
- const idealY = (p1.y + p4.y) / 2;
4677
- p2New = { x: p2.x, y: idealY };
4678
- p3New = { x: p3.x, y: idealY };
4679
- }
4680
- const collides = segmentIntersectsAnyRect(p1, p2New, combinedObstacles) || segmentIntersectsAnyRect(p2New, p3New, combinedObstacles) || segmentIntersectsAnyRect(p3New, p4, combinedObstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
4681
- if (!collides) {
4682
- newPath[i + 1] = p2New;
4683
- newPath[i + 2] = p3New;
4684
- i = 0;
4763
+ if (isHVHZShape) {
4764
+ const idealX = (p1.x + p4.x) / 2;
4765
+ p2New = { x: idealX, y: p2.y };
4766
+ p3New = { x: idealX, y: p3.y };
4767
+ } else {
4768
+ const idealY = (p1.y + p4.y) / 2;
4769
+ p2New = { x: p2.x, y: idealY };
4770
+ p3New = { x: p3.x, y: idealY };
4771
+ }
4772
+ const collides = segmentIntersectsAnyRect(p1, p2New, combinedObstacles) || segmentIntersectsAnyRect(p2New, p3New, combinedObstacles) || segmentIntersectsAnyRect(p3New, p4, combinedObstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
4773
+ if (!collides) {
4774
+ newPath[i + 1] = p2New;
4775
+ newPath[i + 2] = p3New;
4776
+ i = 0;
4777
+ }
4778
+ }
4779
+ const finalSimplifiedPath = simplifyPath(newPath);
4780
+ return {
4781
+ ...targetTrace,
4782
+ tracePath: finalSimplifiedPath
4783
+ };
4784
+ };
4785
+
4786
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
4787
+ var RAIL_ALIGNMENT_EPSILON = 2e-3;
4788
+ var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
4789
+ var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
4790
+ var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
4791
+ var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
4792
+ var getRailOrientation = (a, b) => {
4793
+ if (!isPositiveLength(a, b)) return null;
4794
+ if (isVertical2(a, b)) return "vertical";
4795
+ if (isHorizontal2(a, b)) return "horizontal";
4796
+ return null;
4797
+ };
4798
+ var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
4799
+ var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
4800
+ var getDistinctCoordinates = (coordinates) => {
4801
+ const distinct = [];
4802
+ for (const coordinate of coordinates) {
4803
+ if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
4804
+ distinct.push(coordinate);
4805
+ }
4806
+ }
4807
+ return distinct;
4808
+ };
4809
+ var getMergedIntervalLength = (intervals) => {
4810
+ const groups = [];
4811
+ for (const interval of intervals) {
4812
+ const group = groups.find(
4813
+ (items) => nearlyEqual(items[0].coordinate, interval.coordinate)
4814
+ );
4815
+ if (group) group.push(interval);
4816
+ else groups.push([interval]);
4817
+ }
4818
+ return groups.reduce((total, group) => {
4819
+ const sorted = [...group].sort((a, b) => a.min - b.min);
4820
+ let groupLength = 0;
4821
+ let currentMin = sorted[0].min;
4822
+ let currentMax = sorted[0].max;
4823
+ for (const interval of sorted.slice(1)) {
4824
+ if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
4825
+ currentMax = Math.max(currentMax, interval.max);
4826
+ } else {
4827
+ groupLength += currentMax - currentMin;
4828
+ currentMin = interval.min;
4829
+ currentMax = interval.max;
4830
+ }
4831
+ }
4832
+ return total + groupLength + currentMax - currentMin;
4833
+ }, 0);
4834
+ };
4835
+ var getVisibleTraceLength = (traces) => {
4836
+ const horizontal = [];
4837
+ const vertical = [];
4838
+ for (const trace of traces) {
4839
+ for (let index = 0; index < trace.tracePath.length - 1; index++) {
4840
+ const start = trace.tracePath[index];
4841
+ const end = trace.tracePath[index + 1];
4842
+ if (isHorizontal2(start, end)) {
4843
+ horizontal.push({
4844
+ coordinate: start.y,
4845
+ min: Math.min(start.x, end.x),
4846
+ max: Math.max(start.x, end.x)
4847
+ });
4848
+ } else if (isVertical2(start, end)) {
4849
+ vertical.push({
4850
+ coordinate: start.x,
4851
+ min: Math.min(start.y, end.y),
4852
+ max: Math.max(start.y, end.y)
4853
+ });
4854
+ }
4855
+ }
4856
+ }
4857
+ return getMergedIntervalLength(horizontal) + getMergedIntervalLength(vertical);
4858
+ };
4859
+
4860
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
4861
+ var moveRailSegments = (trace, segments, coordinate) => {
4862
+ const pointsToMove = /* @__PURE__ */ new Set();
4863
+ for (const segment of segments) {
4864
+ pointsToMove.add(segment.segmentIndex);
4865
+ pointsToMove.add(segment.segmentIndex + 1);
4866
+ }
4867
+ const orientation = segments[0].orientation;
4868
+ const tracePath = simplifyPath(
4869
+ trace.tracePath.map((point, index) => {
4870
+ if (!pointsToMove.has(index)) return point;
4871
+ return orientation === "vertical" ? { ...point, x: coordinate } : { ...point, y: coordinate };
4872
+ })
4873
+ );
4874
+ return { ...trace, tracePath };
4875
+ };
4876
+
4877
+ // lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
4878
+ var EPS5 = 1e-6;
4879
+ var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
4880
+ var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS5 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS5;
4881
+ var getTraceCorners = (path) => {
4882
+ const corners = [];
4883
+ for (let i = 1; i < path.length - 1; i++) {
4884
+ const previousPoint = path[i - 1];
4885
+ const cornerPoint = path[i];
4886
+ const nextPoint = path[i + 1];
4887
+ if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
4888
+ corners.push(cornerPoint);
4889
+ }
4890
+ }
4891
+ return corners;
4892
+ };
4893
+ var tracePathContainsPoint = (path, point) => {
4894
+ for (let i = 0; i < path.length - 1; i++) {
4895
+ if (isPointOnSegment(point, path[i], path[i + 1])) return true;
4896
+ }
4897
+ return false;
4898
+ };
4899
+ var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
4900
+ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
4901
+ var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
4902
+ var isPointOnSegment = (point, start, end) => {
4903
+ if (getSegmentOrientation(start, end) === "horizontal") {
4904
+ return Math.abs(point.y - start.y) <= EPS5 && point.x >= Math.min(start.x, end.x) - EPS5 && point.x <= Math.max(start.x, end.x) + EPS5;
4905
+ }
4906
+ return Math.abs(point.x - start.x) <= EPS5 && point.y >= Math.min(start.y, end.y) - EPS5 && point.y <= Math.max(start.y, end.y) + EPS5;
4907
+ };
4908
+ var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS5 ? "horizontal" : "vertical";
4909
+
4910
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
4911
+ var getAnchoredTraceIds = (label, traces) => new Set(
4912
+ traces.filter(
4913
+ (trace) => trace.globalConnNetId === label.globalConnNetId && tracePathContainsPoint(trace.tracePath, label.anchorPoint)
4914
+ ).map((trace) => trace.mspPairId)
4915
+ );
4916
+ var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
4917
+ const anchoredBefore = getAnchoredTraceIds(label, before);
4918
+ if (anchoredBefore.size === 0) return true;
4919
+ const anchoredAfter = getAnchoredTraceIds(label, after);
4920
+ return [...anchoredBefore].every((traceId) => anchoredAfter.has(traceId));
4921
+ });
4922
+
4923
+ // lib/solvers/Example28Solver/types.ts
4924
+ var EPS6 = 1e-9;
4925
+
4926
+ // lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
4927
+ var segmentRunsAlongRectBoundary = (start, end, rect) => {
4928
+ const isVertical5 = Math.abs(start.x - end.x) < EPS6;
4929
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS6;
4930
+ if (isVertical5) {
4931
+ return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
4932
+ }
4933
+ if (isHorizontal4) {
4934
+ return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
4935
+ }
4936
+ return false;
4937
+ };
4938
+
4939
+ // lib/solvers/Example28Solver/geometry.ts
4940
+ var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
4941
+ var getPathLength = (path) => {
4942
+ let length = 0;
4943
+ for (let i = 0; i < path.length - 1; i++) {
4944
+ length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
4945
+ }
4946
+ return length;
4947
+ };
4948
+ var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
4949
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS6 || Math.abs(start.y - end.y) < EPS6;
4950
+ var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS6 ? "horizontal" : "vertical";
4951
+ var projectPointToSegment = (point, start, end) => {
4952
+ if (Math.abs(start.x - end.x) < EPS6) {
4953
+ return {
4954
+ x: start.x,
4955
+ y: Math.min(
4956
+ Math.max(point.y, Math.min(start.y, end.y)),
4957
+ Math.max(start.y, end.y)
4958
+ )
4959
+ };
4960
+ }
4961
+ if (Math.abs(start.y - end.y) < EPS6) {
4962
+ return {
4963
+ x: Math.min(
4964
+ Math.max(point.x, Math.min(start.x, end.x)),
4965
+ Math.max(start.x, end.x)
4966
+ ),
4967
+ y: start.y
4968
+ };
4969
+ }
4970
+ return start;
4971
+ };
4972
+ var getPointToSegmentDistance = (point, start, end) => getDistance2(point, projectPointToSegment(point, start, end));
4973
+ var getSegments = (path) => {
4974
+ const segments = [];
4975
+ for (let i = 0; i < path.length - 1; i++) {
4976
+ const start = path[i];
4977
+ const end = path[i + 1];
4978
+ if (!isAxisAlignedSegment(start, end)) continue;
4979
+ segments.push({
4980
+ start,
4981
+ end,
4982
+ orientation: getSegmentOrientation2(start, end)
4983
+ });
4984
+ }
4985
+ return segments;
4986
+ };
4987
+ var findSegmentContainingPoint = (path, point) => {
4988
+ for (let i = 0; i < path.length - 1; i++) {
4989
+ const start = path[i];
4990
+ const end = path[i + 1];
4991
+ if (!isAxisAlignedSegment(start, end)) continue;
4992
+ if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
4993
+ return {
4994
+ index: i,
4995
+ start,
4996
+ end,
4997
+ orientation: getSegmentOrientation2(start, end)
4998
+ };
4999
+ }
5000
+ return null;
5001
+ };
5002
+ var isPointWithinSegmentPrimaryRange = (point, segment) => {
5003
+ if (segment.orientation === "horizontal") {
5004
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS6 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS6;
5005
+ }
5006
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
5007
+ };
5008
+ var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
5009
+ const matchingSegments = getSegments(path).filter(
5010
+ (segment) => segment.orientation === orientation
5011
+ );
5012
+ if (matchingSegments.length === 0) return null;
5013
+ const containingSegments = matchingSegments.filter(
5014
+ (segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
5015
+ );
5016
+ const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
5017
+ const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
5018
+ return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
5019
+ };
5020
+ var projectPointToPath = (point, path) => {
5021
+ let bestPoint = null;
5022
+ let bestDistance = Number.POSITIVE_INFINITY;
5023
+ for (let i = 0; i < path.length - 1; i++) {
5024
+ const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
5025
+ const distance4 = getDistance2(point, projectedPoint);
5026
+ if (distance4 < bestDistance) {
5027
+ bestPoint = projectedPoint;
5028
+ bestDistance = distance4;
5029
+ }
5030
+ }
5031
+ return bestPoint;
5032
+ };
5033
+ var segmentsIntersect = (a1, a2, b1, b2) => {
5034
+ const aVertical = Math.abs(a1.x - a2.x) < EPS6;
5035
+ const bVertical = Math.abs(b1.x - b2.x) < EPS6;
5036
+ const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS6 && value <= Math.max(p1, p2) + EPS6;
5037
+ if (aVertical && bVertical) {
5038
+ if (Math.abs(a1.x - b1.x) > EPS6) return false;
5039
+ const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
5040
+ return overlap > EPS6;
5041
+ }
5042
+ if (!aVertical && !bVertical) {
5043
+ if (Math.abs(a1.y - b1.y) > EPS6) return false;
5044
+ const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
5045
+ return overlap > EPS6;
5046
+ }
5047
+ const verticalA = aVertical ? a1 : b1;
5048
+ const verticalB = aVertical ? a2 : b2;
5049
+ const horizontalA = aVertical ? b1 : a1;
5050
+ const horizontalB = aVertical ? b2 : a2;
5051
+ return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
5052
+ };
5053
+ var countPathIntersections = (pathA, pathB) => {
5054
+ let count = 0;
5055
+ for (let i = 0; i < pathA.length - 1; i++) {
5056
+ for (let j = 0; j < pathB.length - 1; j++) {
5057
+ if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
5058
+ count++;
5059
+ }
5060
+ }
5061
+ }
5062
+ return count;
5063
+ };
5064
+ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
5065
+ for (let i = 0; i < path.length - 1; i++) {
5066
+ const start = path[i];
5067
+ const end = path[i + 1];
5068
+ for (const obstacle of chipObstacles) {
5069
+ if (!segmentIntersectsRect(start, end, obstacle)) continue;
5070
+ if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
5071
+ return true;
5072
+ }
5073
+ }
5074
+ return false;
5075
+ };
5076
+
5077
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/scoreRailAlignment.ts
5078
+ var countOtherNetCrossings = (traces, allTraces) => {
5079
+ let crossings = 0;
5080
+ for (const trace of traces) {
5081
+ for (const otherTrace of allTraces) {
5082
+ if (trace.globalConnNetId === otherTrace.globalConnNetId) continue;
5083
+ crossings += countPathIntersections(trace.tracePath, otherTrace.tracePath);
5084
+ }
5085
+ }
5086
+ return crossings;
5087
+ };
5088
+ var getTraceGeometryMetrics = (traces, allTraces) => ({
5089
+ turnCount: traces.reduce(
5090
+ (total, trace) => total + countTurns(trace.tracePath),
5091
+ 0
5092
+ ),
5093
+ visibleLength: getVisibleTraceLength(traces),
5094
+ pathLength: traces.reduce(
5095
+ (sum, trace) => sum + getPathLength(trace.tracePath),
5096
+ 0
5097
+ ),
5098
+ otherNetCrossings: countOtherNetCrossings(traces, allTraces)
5099
+ });
5100
+ var scoreIsBetter = (candidate, best) => {
5101
+ if (candidate.turnCount !== best.turnCount)
5102
+ return candidate.turnCount < best.turnCount;
5103
+ if (!nearlyEqual(candidate.visibleLength, best.visibleLength))
5104
+ return candidate.visibleLength < best.visibleLength;
5105
+ if (!nearlyEqual(candidate.pathLength, best.pathLength))
5106
+ return candidate.pathLength < best.pathLength;
5107
+ if (candidate.otherNetCrossings !== best.otherNetCrossings)
5108
+ return candidate.otherNetCrossings < best.otherNetCrossings;
5109
+ if (!nearlyEqual(candidate.displacement, best.displacement))
5110
+ return candidate.displacement < best.displacement;
5111
+ return candidate.coordinate < best.coordinate;
5112
+ };
5113
+ var isReadabilityImprovement = (candidate, baseline) => candidate.turnCount <= baseline.turnCount && (candidate.turnCount < baseline.turnCount || candidate.visibleLength <= baseline.visibleLength + RAIL_ALIGNMENT_EPSILON);
5114
+
5115
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts
5116
+ var tracePathChanged = (original, candidate) => original.tracePath.length !== candidate.tracePath.length || candidate.tracePath.some(
5117
+ (point, index) => !pointsEqual(point, original.tracePath[index])
5118
+ );
5119
+ var evaluateRailGroup = ({
5120
+ group,
5121
+ traces,
5122
+ netLabelPlacements,
5123
+ obstacles,
5124
+ eligibleTraceIds
5125
+ }) => {
5126
+ const groupTraceIds = new Set(group.map((segment) => segment.traceId));
5127
+ const originalGroupTraces = traces.filter(
5128
+ (trace) => groupTraceIds.has(trace.mspPairId)
5129
+ );
5130
+ const baseline = getTraceGeometryMetrics(originalGroupTraces, traces);
5131
+ const coordinates = getDistinctCoordinates(
5132
+ group.map((segment) => segment.coordinate)
5133
+ );
5134
+ const otherNetTraces = traces.filter(
5135
+ (trace) => trace.globalConnNetId !== group[0].globalConnNetId
5136
+ );
5137
+ const immutableSameNetTraces = traces.filter(
5138
+ (trace) => trace.globalConnNetId === group[0].globalConnNetId && !eligibleTraceIds.has(trace.mspPairId)
5139
+ );
5140
+ let best = null;
5141
+ for (const coordinate of coordinates) {
5142
+ const candidateMap = /* @__PURE__ */ new Map();
5143
+ for (const trace of originalGroupTraces) {
5144
+ const candidateTrace = moveRailSegments(
5145
+ trace,
5146
+ group.filter((segment) => segment.traceId === trace.mspPairId),
5147
+ coordinate
5148
+ );
5149
+ candidateMap.set(trace.mspPairId, candidateTrace);
5150
+ }
5151
+ const candidateTraces = [...candidateMap.values()];
5152
+ const allCandidateTraces = traces.map(
5153
+ (trace) => candidateMap.get(trace.mspPairId) ?? trace
5154
+ );
5155
+ const candidatesAreClear = candidateTraces.every(
5156
+ (candidate2) => !isPathCollidingWithObstacles(candidate2.tracePath, obstacles) && detectTraceLabelOverlap({
5157
+ traces: [candidate2],
5158
+ netLabels: netLabelPlacements
5159
+ }).length === 0 && !doesPathCoincideWithTraces(candidate2.tracePath, otherNetTraces) && !doesPathCoincideWithTraces(
5160
+ candidate2.tracePath,
5161
+ immutableSameNetTraces.filter(
5162
+ (trace) => trace.mspPairId !== candidate2.mspPairId
5163
+ )
5164
+ )
5165
+ );
5166
+ if (!candidatesAreClear) continue;
5167
+ if (!preservesLabelAnchors(netLabelPlacements, traces, allCandidateTraces)) {
5168
+ continue;
5169
+ }
5170
+ const metrics = getTraceGeometryMetrics(candidateTraces, allCandidateTraces);
5171
+ if (metrics.otherNetCrossings > baseline.otherNetCrossings) continue;
5172
+ if (!isReadabilityImprovement(metrics, baseline)) continue;
5173
+ const score = {
5174
+ ...metrics,
5175
+ displacement: group.reduce(
5176
+ (sum, segment) => sum + Math.abs(segment.coordinate - coordinate),
5177
+ 0
5178
+ ),
5179
+ coordinate
5180
+ };
5181
+ const changedTraceIds = candidateTraces.filter((candidate2) => {
5182
+ const original = traces.find(
5183
+ (trace) => trace.mspPairId === candidate2.mspPairId
5184
+ );
5185
+ return tracePathChanged(original, candidate2);
5186
+ }).map((trace) => trace.mspPairId);
5187
+ if (changedTraceIds.length === 0) continue;
5188
+ const candidate = { traces: allCandidateTraces, changedTraceIds, score };
5189
+ if (!best || scoreIsBetter(candidate.score, best.score)) best = candidate;
5190
+ }
5191
+ return best;
5192
+ };
5193
+
5194
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts
5195
+ var getMovableRailSegments = (trace) => {
5196
+ const segments = [];
5197
+ const path = trace.tracePath;
5198
+ for (let segmentIndex = 1; segmentIndex < path.length - 2; segmentIndex++) {
5199
+ const previous = path[segmentIndex - 1];
5200
+ const start = path[segmentIndex];
5201
+ const end = path[segmentIndex + 1];
5202
+ const next = path[segmentIndex + 2];
5203
+ const orientation = getRailOrientation(start, end);
5204
+ if (!orientation) continue;
5205
+ const previousOrientation = getRailOrientation(previous, start);
5206
+ const nextOrientation = getRailOrientation(end, next);
5207
+ if (!previousOrientation || !nextOrientation) continue;
5208
+ if (previousOrientation === orientation || nextOrientation === orientation)
5209
+ continue;
5210
+ const alongValues = orientation === "vertical" ? [start.y, end.y] : [start.x, end.x];
5211
+ segments.push({
5212
+ traceId: trace.mspPairId,
5213
+ segmentIndex,
5214
+ globalConnNetId: trace.globalConnNetId,
5215
+ orientation,
5216
+ coordinate: orientation === "vertical" ? start.x : start.y,
5217
+ minAlong: Math.min(...alongValues),
5218
+ maxAlong: Math.max(...alongValues)
5219
+ });
5220
+ }
5221
+ return segments;
5222
+ };
5223
+ var railIsOutsideComponent = (segment, chip, facingDirection) => {
5224
+ const minX = chip.center.x - chip.width / 2;
5225
+ const maxX = chip.center.x + chip.width / 2;
5226
+ const minY = chip.center.y - chip.height / 2;
5227
+ const maxY = chip.center.y + chip.height / 2;
5228
+ switch (facingDirection) {
5229
+ case "x-":
5230
+ return segment.orientation === "vertical" && segment.coordinate <= minX + RAIL_ALIGNMENT_EPSILON;
5231
+ case "x+":
5232
+ return segment.orientation === "vertical" && segment.coordinate >= maxX - RAIL_ALIGNMENT_EPSILON;
5233
+ case "y+":
5234
+ return segment.orientation === "horizontal" && segment.coordinate >= maxY - RAIL_ALIGNMENT_EPSILON;
5235
+ case "y-":
5236
+ return segment.orientation === "horizontal" && segment.coordinate <= minY + RAIL_ALIGNMENT_EPSILON;
5237
+ }
5238
+ };
5239
+ var getComponentSideRailSegments = (trace, chipMap) => {
5240
+ const segments = [];
5241
+ for (const segment of getMovableRailSegments(trace)) {
5242
+ const associations = trace.pins.flatMap((pin, pinIndex) => {
5243
+ const chip = chipMap.get(pin.chipId);
5244
+ if (!chip) return [];
5245
+ const componentFacingDirection = pin._facingDirection ?? getPinDirection(pin, chip);
5246
+ if (!railIsOutsideComponent(segment, chip, componentFacingDirection)) {
5247
+ return [];
5248
+ }
5249
+ return [
5250
+ {
5251
+ componentId: chip.chipId,
5252
+ componentFacingDirection,
5253
+ distanceFromEndpoint: pinIndex === 0 ? segment.segmentIndex : trace.tracePath.length - 2 - segment.segmentIndex
5254
+ }
5255
+ ];
5256
+ });
5257
+ associations.sort((a, b) => a.distanceFromEndpoint - b.distanceFromEndpoint);
5258
+ const association = associations[0];
5259
+ if (!association) continue;
5260
+ segments.push({ ...segment, ...association });
5261
+ }
5262
+ return segments;
5263
+ };
5264
+
5265
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts
5266
+ var getCorridor = (a, b) => {
5267
+ const overlapMin = Math.max(a.minAlong, b.minAlong);
5268
+ const overlapMax = Math.min(a.maxAlong, b.maxAlong);
5269
+ const along = (overlapMin + overlapMax) / 2;
5270
+ return a.orientation === "vertical" ? [
5271
+ { x: a.coordinate, y: along },
5272
+ { x: b.coordinate, y: along }
5273
+ ] : [
5274
+ { x: along, y: a.coordinate },
5275
+ { x: along, y: b.coordinate }
5276
+ ];
5277
+ };
5278
+ var corridorIsClear = (a, b, obstacles) => {
5279
+ if (!rangesTouchOrOverlap(a, b)) return true;
5280
+ const [start, end] = getCorridor(a, b);
5281
+ return !obstacles.some(
5282
+ (obstacle) => segmentIntersectsRect(start, end, obstacle)
5283
+ );
5284
+ };
5285
+ var tracesSharePin = (a, b, traceMap) => {
5286
+ if (a.traceId === b.traceId) return true;
5287
+ const aPinIds = new Set(traceMap.get(a.traceId).pins.map((pin) => pin.pinId));
5288
+ return traceMap.get(b.traceId).pins.some((pin) => aPinIds.has(pin.pinId));
5289
+ };
5290
+ var canJoinRailGroup = (start, current, candidate, traceMap, obstacles) => candidate.globalConnNetId === start.globalConnNetId && candidate.orientation === start.orientation && candidate.componentId === start.componentId && candidate.componentFacingDirection === start.componentFacingDirection && (rangesTouchOrOverlap(current, candidate) || tracesSharePin(current, candidate, traceMap)) && corridorIsClear(current, candidate, obstacles);
5291
+ var getRailGroups = (traces, eligibleTraceIds, inputProblem, obstacles) => {
5292
+ const chipMap = new Map(inputProblem.chips.map((chip) => [chip.chipId, chip]));
5293
+ const segments = traces.filter((trace) => eligibleTraceIds.has(trace.mspPairId)).flatMap((trace) => getComponentSideRailSegments(trace, chipMap));
5294
+ const traceMap = new Map(traces.map((trace) => [trace.mspPairId, trace]));
5295
+ const visited = /* @__PURE__ */ new Set();
5296
+ const groups = [];
5297
+ for (let startIndex = 0; startIndex < segments.length; startIndex++) {
5298
+ if (visited.has(startIndex)) continue;
5299
+ const start = segments[startIndex];
5300
+ const queue = [startIndex];
5301
+ const group = [];
5302
+ visited.add(startIndex);
5303
+ for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
5304
+ const current = segments[queue[queueIndex]];
5305
+ group.push(current);
5306
+ for (let candidateIndex = 0; candidateIndex < segments.length; candidateIndex++) {
5307
+ if (visited.has(candidateIndex)) continue;
5308
+ const candidate = segments[candidateIndex];
5309
+ if (!canJoinRailGroup(start, current, candidate, traceMap, obstacles)) {
5310
+ continue;
5311
+ }
5312
+ visited.add(candidateIndex);
5313
+ queue.push(candidateIndex);
5314
+ }
5315
+ }
5316
+ const traceCount = new Set(group.map((segment) => segment.traceId)).size;
5317
+ const hasDifferentCoordinates = group.some(
5318
+ (segment) => !nearlyEqual(segment.coordinate, group[0].coordinate)
5319
+ );
5320
+ if (traceCount >= 2 && hasDifferentCoordinates) groups.push(group);
5321
+ }
5322
+ return groups;
5323
+ };
5324
+
5325
+ // lib/solvers/TraceCleanupSolver/alignSameNetRails.ts
5326
+ var alignSameNetRails = ({
5327
+ inputProblem,
5328
+ traces,
5329
+ netLabelPlacements,
5330
+ eligibleTraceIds
5331
+ }) => {
5332
+ let outputTraces = [...traces];
5333
+ const obstacles = getObstacleRects(inputProblem);
5334
+ const alignedTraceIds = /* @__PURE__ */ new Set();
5335
+ let alignedRailGroupCount = 0;
5336
+ const maximumPasses = Math.max(
5337
+ 1,
5338
+ traces.reduce((sum, trace) => sum + trace.tracePath.length, 0)
5339
+ );
5340
+ for (let pass = 0; pass < maximumPasses; pass++) {
5341
+ const groups = getRailGroups(
5342
+ outputTraces,
5343
+ eligibleTraceIds,
5344
+ inputProblem,
5345
+ obstacles
5346
+ );
5347
+ let applied = null;
5348
+ for (const group of groups) {
5349
+ applied = evaluateRailGroup({
5350
+ group,
5351
+ traces: outputTraces,
5352
+ netLabelPlacements,
5353
+ obstacles,
5354
+ eligibleTraceIds
5355
+ });
5356
+ if (applied) break;
4685
5357
  }
5358
+ if (!applied) break;
5359
+ outputTraces = applied.traces;
5360
+ alignedRailGroupCount++;
5361
+ for (const traceId of applied.changedTraceIds) alignedTraceIds.add(traceId);
4686
5362
  }
4687
- const finalSimplifiedPath = simplifyPath(newPath);
4688
5363
  return {
4689
- ...targetTrace,
4690
- tracePath: finalSimplifiedPath
5364
+ traces: outputTraces,
5365
+ alignedRailGroupCount,
5366
+ alignedTraceCount: alignedTraceIds.size
4691
5367
  };
4692
5368
  };
4693
5369
 
@@ -4748,8 +5424,8 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
4748
5424
  };
4749
5425
 
4750
5426
  // lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
4751
- var EPS5 = 1e-6;
4752
- var isVertical2 = (a, b, eps = EPS5) => Math.abs(a.x - b.x) < eps;
5427
+ var EPS7 = 1e-6;
5428
+ var isVertical3 = (a, b, eps = EPS7) => Math.abs(a.x - b.x) < eps;
4753
5429
  var generateLShapeRerouteCandidates = ({
4754
5430
  lShape,
4755
5431
  rectangle,
@@ -4762,50 +5438,50 @@ var generateLShapeRerouteCandidates = ({
4762
5438
  let c2;
4763
5439
  let i1_padded = interactionPoint1;
4764
5440
  let i2_padded = interactionPoint2;
4765
- if (Math.abs(p2.x - x) < EPS5 && Math.abs(p2.y - (y + height)) < EPS5) {
5441
+ if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
4766
5442
  c2 = { x: x + width + padding, y: y - padding };
4767
- if (isVertical2(p1, p2)) {
5443
+ if (isVertical3(p1, p2)) {
4768
5444
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
4769
5445
  } else {
4770
5446
  i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
4771
5447
  }
4772
- if (isVertical2(p2, p3)) {
5448
+ if (isVertical3(p2, p3)) {
4773
5449
  i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
4774
5450
  } else {
4775
5451
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
4776
5452
  }
4777
- } else if (Math.abs(p2.x - (x + width)) < EPS5 && Math.abs(p2.y - (y + height)) < EPS5) {
5453
+ } else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
4778
5454
  c2 = { x: x - padding, y: y - padding };
4779
- if (isVertical2(p1, p2)) {
5455
+ if (isVertical3(p1, p2)) {
4780
5456
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
4781
5457
  } else {
4782
5458
  i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
4783
5459
  }
4784
- if (isVertical2(p2, p3)) {
5460
+ if (isVertical3(p2, p3)) {
4785
5461
  i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
4786
5462
  } else {
4787
5463
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
4788
5464
  }
4789
- } else if (Math.abs(p2.x - x) < EPS5 && Math.abs(p2.y - y) < EPS5) {
5465
+ } else if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - y) < EPS7) {
4790
5466
  c2 = { x: x + width + padding, y: y + height + padding };
4791
- if (isVertical2(p1, p2)) {
5467
+ if (isVertical3(p1, p2)) {
4792
5468
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
4793
5469
  } else {
4794
5470
  i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
4795
5471
  }
4796
- if (isVertical2(p2, p3)) {
5472
+ if (isVertical3(p2, p3)) {
4797
5473
  i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
4798
5474
  } else {
4799
5475
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
4800
5476
  }
4801
- } else if (Math.abs(p2.x - (x + width)) < EPS5 && Math.abs(p2.y - y) < EPS5) {
5477
+ } else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - y) < EPS7) {
4802
5478
  c2 = { x: x - padding, y: y + height + padding };
4803
- if (isVertical2(p1, p2)) {
5479
+ if (isVertical3(p1, p2)) {
4804
5480
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
4805
5481
  } else {
4806
5482
  i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
4807
5483
  }
4808
- if (isVertical2(p2, p3)) {
5484
+ if (isVertical3(p2, p3)) {
4809
5485
  i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
4810
5486
  } else {
4811
5487
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
@@ -5269,9 +5945,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5269
5945
  };
5270
5946
 
5271
5947
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
5272
- var EPS6 = 1e-6;
5273
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS6;
5274
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS6;
5948
+ var EPS8 = 1e-6;
5949
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS8;
5950
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS8;
5275
5951
  var is4PointRectangle = (path) => {
5276
5952
  if (path.length !== 4) return false;
5277
5953
  const [p0, p1, p2, p3] = path;
@@ -5281,12 +5957,19 @@ var is4PointRectangle = (path) => {
5281
5957
  };
5282
5958
 
5283
5959
  // lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
5960
+ var DEFAULT_OPERATIONS = [
5961
+ "untangling_traces",
5962
+ "minimizing_turns",
5963
+ "balancing_l_shapes"
5964
+ ];
5284
5965
  var TraceCleanupSolver = class extends BaseSolver {
5285
5966
  input;
5286
5967
  outputTraces;
5287
5968
  traceIdQueue;
5288
5969
  tracesMap;
5289
- pipelineStep = "untangling_traces";
5970
+ operations;
5971
+ operationIndex = 0;
5972
+ pipelineStep;
5290
5973
  activeTraceId = null;
5291
5974
  // New property
5292
5975
  activeSubSolver = null;
@@ -5295,6 +5978,8 @@ var TraceCleanupSolver = class extends BaseSolver {
5295
5978
  this.input = solverInput;
5296
5979
  this.outputTraces = [...solverInput.allTraces];
5297
5980
  this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
5981
+ this.operations = solverInput.operations ?? DEFAULT_OPERATIONS;
5982
+ this.pipelineStep = this.operations[0] ?? null;
5298
5983
  this.traceIdQueue = Array.from(
5299
5984
  solverInput.allTraces.map((e) => e.mspPairId)
5300
5985
  );
@@ -5307,13 +5992,17 @@ var TraceCleanupSolver = class extends BaseSolver {
5307
5992
  this.outputTraces = output.traces;
5308
5993
  this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
5309
5994
  this.activeSubSolver = null;
5310
- this.pipelineStep = "minimizing_turns";
5995
+ this._advancePipeline();
5311
5996
  } else if (this.activeSubSolver.failed) {
5312
5997
  this.activeSubSolver = null;
5313
- this.pipelineStep = "minimizing_turns";
5998
+ this._advancePipeline();
5314
5999
  }
5315
6000
  return;
5316
6001
  }
6002
+ if (!this.pipelineStep) {
6003
+ this.solved = true;
6004
+ return;
6005
+ }
5317
6006
  switch (this.pipelineStep) {
5318
6007
  case "untangling_traces":
5319
6008
  this._runUntangleTracesStep();
@@ -5324,8 +6013,17 @@ var TraceCleanupSolver = class extends BaseSolver {
5324
6013
  case "balancing_l_shapes":
5325
6014
  this._runBalanceLShapesStep();
5326
6015
  break;
6016
+ case "aligning_same_net_rails":
6017
+ this._runAlignSameNetRailsStep();
6018
+ break;
5327
6019
  }
5328
6020
  }
6021
+ _advancePipeline() {
6022
+ this.operationIndex++;
6023
+ this.pipelineStep = this.operations[this.operationIndex] ?? null;
6024
+ this.traceIdQueue = this.outputTraces.map((trace) => trace.mspPairId);
6025
+ if (this.pipelineStep) this.activeTraceId = null;
6026
+ }
5329
6027
  _runUntangleTracesStep() {
5330
6028
  this.activeSubSolver = new UntangleTraceSubsolver({
5331
6029
  ...this.input,
@@ -5334,17 +6032,14 @@ var TraceCleanupSolver = class extends BaseSolver {
5334
6032
  }
5335
6033
  _runMinimizeTurnsStep() {
5336
6034
  if (this.traceIdQueue.length === 0) {
5337
- this.pipelineStep = "balancing_l_shapes";
5338
- this.traceIdQueue = Array.from(
5339
- this.input.allTraces.map((e) => e.mspPairId)
5340
- );
6035
+ this._advancePipeline();
5341
6036
  return;
5342
6037
  }
5343
6038
  this._processTrace("minimizing_turns");
5344
6039
  }
5345
6040
  _runBalanceLShapesStep() {
5346
6041
  if (this.traceIdQueue.length === 0) {
5347
- this.solved = true;
6042
+ this._advancePipeline();
5348
6043
  return;
5349
6044
  }
5350
6045
  this._processTrace("balancing_l_shapes");
@@ -5374,6 +6069,19 @@ var TraceCleanupSolver = class extends BaseSolver {
5374
6069
  this.tracesMap.set(targetMspConnectionPairId, updatedTrace);
5375
6070
  this.outputTraces = Array.from(this.tracesMap.values());
5376
6071
  }
6072
+ _runAlignSameNetRailsStep() {
6073
+ const alignment = alignSameNetRails({
6074
+ inputProblem: this.input.inputProblem,
6075
+ traces: this.outputTraces,
6076
+ netLabelPlacements: this.input.allLabelPlacements,
6077
+ eligibleTraceIds: this.input.eligibleTraceIds ?? new Set(this.outputTraces.map((trace) => trace.mspPairId))
6078
+ });
6079
+ this.outputTraces = alignment.traces;
6080
+ this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
6081
+ this.stats.alignedRailGroupCount = alignment.alignedRailGroupCount;
6082
+ this.stats.alignedTraceCount = alignment.alignedTraceCount;
6083
+ this._advancePipeline();
6084
+ }
5377
6085
  getOutput() {
5378
6086
  return {
5379
6087
  traces: this.outputTraces
@@ -5452,160 +6160,6 @@ var dir = (facingDirection) => {
5452
6160
  throw new Error(`Invalid facing direction: ${facingDirection}`);
5453
6161
  };
5454
6162
 
5455
- // lib/solvers/Example28Solver/types.ts
5456
- var EPS7 = 1e-9;
5457
-
5458
- // lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
5459
- var segmentRunsAlongRectBoundary = (start, end, rect) => {
5460
- const isVertical4 = Math.abs(start.x - end.x) < EPS7;
5461
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
5462
- if (isVertical4) {
5463
- return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
5464
- }
5465
- if (isHorizontal3) {
5466
- return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
5467
- }
5468
- return false;
5469
- };
5470
-
5471
- // lib/solvers/Example28Solver/geometry.ts
5472
- var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
5473
- var getPathLength = (path) => {
5474
- let length = 0;
5475
- for (let i = 0; i < path.length - 1; i++) {
5476
- length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
5477
- }
5478
- return length;
5479
- };
5480
- var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
5481
- var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
5482
- var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
5483
- var projectPointToSegment = (point, start, end) => {
5484
- if (Math.abs(start.x - end.x) < EPS7) {
5485
- return {
5486
- x: start.x,
5487
- y: Math.min(
5488
- Math.max(point.y, Math.min(start.y, end.y)),
5489
- Math.max(start.y, end.y)
5490
- )
5491
- };
5492
- }
5493
- if (Math.abs(start.y - end.y) < EPS7) {
5494
- return {
5495
- x: Math.min(
5496
- Math.max(point.x, Math.min(start.x, end.x)),
5497
- Math.max(start.x, end.x)
5498
- ),
5499
- y: start.y
5500
- };
5501
- }
5502
- return start;
5503
- };
5504
- var getPointToSegmentDistance = (point, start, end) => getDistance(point, projectPointToSegment(point, start, end));
5505
- var getSegments = (path) => {
5506
- const segments = [];
5507
- for (let i = 0; i < path.length - 1; i++) {
5508
- const start = path[i];
5509
- const end = path[i + 1];
5510
- if (!isAxisAlignedSegment(start, end)) continue;
5511
- segments.push({
5512
- start,
5513
- end,
5514
- orientation: getSegmentOrientation(start, end)
5515
- });
5516
- }
5517
- return segments;
5518
- };
5519
- var findSegmentContainingPoint = (path, point) => {
5520
- for (let i = 0; i < path.length - 1; i++) {
5521
- const start = path[i];
5522
- const end = path[i + 1];
5523
- if (!isAxisAlignedSegment(start, end)) continue;
5524
- if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
5525
- return {
5526
- index: i,
5527
- start,
5528
- end,
5529
- orientation: getSegmentOrientation(start, end)
5530
- };
5531
- }
5532
- return null;
5533
- };
5534
- var isPointWithinSegmentPrimaryRange = (point, segment) => {
5535
- if (segment.orientation === "horizontal") {
5536
- return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
5537
- }
5538
- return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
5539
- };
5540
- var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
5541
- const matchingSegments = getSegments(path).filter(
5542
- (segment) => segment.orientation === orientation
5543
- );
5544
- if (matchingSegments.length === 0) return null;
5545
- const containingSegments = matchingSegments.filter(
5546
- (segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
5547
- );
5548
- const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
5549
- const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
5550
- return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
5551
- };
5552
- var projectPointToPath = (point, path) => {
5553
- let bestPoint = null;
5554
- let bestDistance = Number.POSITIVE_INFINITY;
5555
- for (let i = 0; i < path.length - 1; i++) {
5556
- const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
5557
- const distance3 = getDistance(point, projectedPoint);
5558
- if (distance3 < bestDistance) {
5559
- bestPoint = projectedPoint;
5560
- bestDistance = distance3;
5561
- }
5562
- }
5563
- return bestPoint;
5564
- };
5565
- var segmentsIntersect = (a1, a2, b1, b2) => {
5566
- const aVertical = Math.abs(a1.x - a2.x) < EPS7;
5567
- const bVertical = Math.abs(b1.x - b2.x) < EPS7;
5568
- const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
5569
- if (aVertical && bVertical) {
5570
- if (Math.abs(a1.x - b1.x) > EPS7) return false;
5571
- const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
5572
- return overlap > EPS7;
5573
- }
5574
- if (!aVertical && !bVertical) {
5575
- if (Math.abs(a1.y - b1.y) > EPS7) return false;
5576
- const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
5577
- return overlap > EPS7;
5578
- }
5579
- const verticalA = aVertical ? a1 : b1;
5580
- const verticalB = aVertical ? a2 : b2;
5581
- const horizontalA = aVertical ? b1 : a1;
5582
- const horizontalB = aVertical ? b2 : a2;
5583
- return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
5584
- };
5585
- var countPathIntersections = (pathA, pathB) => {
5586
- let count = 0;
5587
- for (let i = 0; i < pathA.length - 1; i++) {
5588
- for (let j = 0; j < pathB.length - 1; j++) {
5589
- if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
5590
- count++;
5591
- }
5592
- }
5593
- }
5594
- return count;
5595
- };
5596
- var isPathCollidingWithChipInterior = (path, chipObstacles) => {
5597
- for (let i = 0; i < path.length - 1; i++) {
5598
- const start = path[i];
5599
- const end = path[i + 1];
5600
- for (const obstacle of chipObstacles) {
5601
- if (!segmentIntersectsRect(start, end, obstacle)) continue;
5602
- if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
5603
- return true;
5604
- }
5605
- }
5606
- return false;
5607
- };
5608
-
5609
6163
  // lib/solvers/Example28Solver/getMovedAnchorPointForReroute.ts
5610
6164
  var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
5611
6165
  const originalSegment = findSegmentContainingPoint(
@@ -5668,7 +6222,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
5668
6222
  const end = path[i + 1];
5669
6223
  for (const obstacle of chipObstacles) {
5670
6224
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
5671
- if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS7) {
6225
+ if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS6) {
5672
6226
  return true;
5673
6227
  }
5674
6228
  }
@@ -5683,14 +6237,14 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
5683
6237
  for (const obstacle of chipObstacles) {
5684
6238
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
5685
6239
  const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
5686
- if (overlap > EPS7) total += overlap;
6240
+ if (overlap > EPS6) total += overlap;
5687
6241
  }
5688
6242
  }
5689
6243
  return total;
5690
6244
  };
5691
6245
  var getSegmentOverlapWithRectSpan = (start, end, rect) => {
5692
- const isVertical4 = Math.abs(start.x - end.x) < EPS7;
5693
- if (isVertical4) {
6246
+ const isVertical5 = Math.abs(start.x - end.x) < EPS6;
6247
+ if (isVertical5) {
5694
6248
  return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
5695
6249
  }
5696
6250
  return Math.min(Math.max(start.x, end.x), rect.maxX) - Math.max(Math.min(start.x, end.x), rect.minX);
@@ -6101,11 +6655,11 @@ var getLabelHugDistance = (tracePath, obstacleLabel) => {
6101
6655
  obstacleLabel.width,
6102
6656
  obstacleLabel.height
6103
6657
  );
6104
- let distance3 = 0;
6658
+ let distance4 = 0;
6105
6659
  for (const point of tracePath) {
6106
- distance3 += getPointDistanceFromRect(point, bounds);
6660
+ distance4 += getPointDistanceFromRect(point, bounds);
6107
6661
  }
6108
- return distance3;
6662
+ return distance4;
6109
6663
  };
6110
6664
  var getPointDistanceFromRect = (point, rect) => {
6111
6665
  const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
@@ -6382,16 +6936,16 @@ var Example28Solver = class extends BaseSolver {
6382
6936
  const outward = dir(label.orientation);
6383
6937
  if (outward.x === 0 && outward.y === 0) return null;
6384
6938
  for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
6385
- const distance3 = step * LABEL_OUTWARD_STEP;
6939
+ const distance4 = step * LABEL_OUTWARD_STEP;
6386
6940
  const candidate = {
6387
6941
  ...label,
6388
6942
  anchorPoint: {
6389
- x: label.anchorPoint.x + outward.x * distance3,
6390
- y: label.anchorPoint.y + outward.y * distance3
6943
+ x: label.anchorPoint.x + outward.x * distance4,
6944
+ y: label.anchorPoint.y + outward.y * distance4
6391
6945
  },
6392
6946
  center: {
6393
- x: label.center.x + outward.x * distance3,
6394
- y: label.center.y + outward.y * distance3
6947
+ x: label.center.x + outward.x * distance4,
6948
+ y: label.center.y + outward.y * distance4
6395
6949
  }
6396
6950
  };
6397
6951
  const candidateWithClearance = {
@@ -6453,16 +7007,16 @@ var createPortOnlyLabelConnectorTrace = ({
6453
7007
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
6454
7008
  var LABEL_SEARCH_STEP = 0.05;
6455
7009
  var WICK_CLEARANCE = 1e-3;
6456
- var EPS8 = 1e-9;
6457
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS8;
7010
+ var EPS9 = 1e-9;
7011
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS9;
6458
7012
  var CANDIDATE_SELECTED_COLOR2 = "blue";
6459
7013
  var CANDIDATE_REJECTED_COLOR2 = "red";
6460
7014
 
6461
7015
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
6462
7016
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
6463
7017
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
6464
- var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS8 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS8;
6465
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS8;
7018
+ var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
7019
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
6466
7020
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
6467
7021
  for (const trace of Object.values(traceMap)) {
6468
7022
  const points = trace.tracePath;
@@ -6485,7 +7039,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
6485
7039
  return false;
6486
7040
  }
6487
7041
  if (sameX2(p1, p2)) {
6488
- if (p1.x <= interiorBounds.minX + EPS8 || p1.x >= interiorBounds.maxX - EPS8) {
7042
+ if (p1.x <= interiorBounds.minX + EPS9 || p1.x >= interiorBounds.maxX - EPS9) {
6489
7043
  return false;
6490
7044
  }
6491
7045
  return rangesOverlap(
@@ -6496,7 +7050,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
6496
7050
  );
6497
7051
  }
6498
7052
  if (sameY2(p1, p2)) {
6499
- if (p1.y <= interiorBounds.minY + EPS8 || p1.y >= interiorBounds.maxY - EPS8) {
7053
+ if (p1.y <= interiorBounds.minY + EPS9 || p1.y >= interiorBounds.maxY - EPS9) {
6500
7054
  return false;
6501
7055
  }
6502
7056
  return rangesOverlap(
@@ -6535,7 +7089,7 @@ var getConnectorTracePath = (source, target, orientation) => simplifyOrthogonalP
6535
7089
  );
6536
7090
  var simplifyOrthogonalPath = (path) => {
6537
7091
  const deduped = path.filter(
6538
- (point, index) => index === 0 || !pointsEqual(point, path[index - 1])
7092
+ (point, index) => index === 0 || !pointsEqual2(point, path[index - 1])
6539
7093
  );
6540
7094
  if (deduped.length < 3) return deduped;
6541
7095
  const simplified = [deduped[0]];
@@ -6551,9 +7105,9 @@ var simplifyOrthogonalPath = (path) => {
6551
7105
  simplified.push(deduped[deduped.length - 1]);
6552
7106
  return simplified;
6553
7107
  };
6554
- var pointsEqual = (a, b) => sameX2(a, b) && sameY2(a, b);
6555
- var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS8;
6556
- var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS8;
7108
+ var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
7109
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS9;
7110
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS9;
6557
7111
  var getMaxSearchDistance = (inputProblem) => {
6558
7112
  const maxChipWidth = Math.max(
6559
7113
  ...inputProblem.chips.map((chip) => chip.width),
@@ -6856,7 +7410,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6856
7410
  );
6857
7411
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
6858
7412
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
6859
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS8; outwardDistance += LABEL_SEARCH_STEP) {
7413
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS9; outwardDistance += LABEL_SEARCH_STEP) {
6860
7414
  const baseAnchor = {
6861
7415
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
6862
7416
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -6929,10 +7483,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6929
7483
  outwardDistance,
6930
7484
  phase = "shift"
6931
7485
  } = params;
6932
- for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS8; distance3 += LABEL_SEARCH_STEP) {
7486
+ for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS9; distance4 += LABEL_SEARCH_STEP) {
6933
7487
  const anchorPoint = {
6934
- x: baseAnchor.x + direction.x * distance3,
6935
- y: baseAnchor.y + direction.y * distance3
7488
+ x: baseAnchor.x + direction.x * distance4,
7489
+ y: baseAnchor.y + direction.y * distance4
6936
7490
  };
6937
7491
  const candidate = this.createCandidate(label, anchorPoint, orientation);
6938
7492
  const result = this.evaluateCandidate(
@@ -6940,7 +7494,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6940
7494
  label,
6941
7495
  labelIndex,
6942
7496
  phase,
6943
- distance3,
7497
+ distance4,
6944
7498
  outwardDistance
6945
7499
  );
6946
7500
  this.currentCandidateResults.push(result);
@@ -6952,11 +7506,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6952
7506
  }
6953
7507
  return null;
6954
7508
  }
6955
- evaluateCandidate(candidate, label, labelIndex, phase, distance3, outwardDistance) {
7509
+ evaluateCandidate(candidate, label, labelIndex, phase, distance4, outwardDistance) {
6956
7510
  return {
6957
7511
  ...candidate,
6958
7512
  phase,
6959
- distance: distance3,
7513
+ distance: distance4,
6960
7514
  outwardDistance,
6961
7515
  selected: false,
6962
7516
  status: this.getCandidateStatus({
@@ -7166,9 +7720,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7166
7720
  const { anchorPoint, orientation } = candidate;
7167
7721
  const chipBounds = chip.bounds;
7168
7722
  if (isYOrientation(orientation)) {
7169
- return anchorPoint.x < chipBounds.minX - EPS8 || anchorPoint.x > chipBounds.maxX + EPS8;
7723
+ return anchorPoint.x < chipBounds.minX - EPS9 || anchorPoint.x > chipBounds.maxX + EPS9;
7170
7724
  }
7171
- return anchorPoint.y < chipBounds.minY - EPS8 || anchorPoint.y > chipBounds.maxY + EPS8;
7725
+ return anchorPoint.y < chipBounds.minY - EPS9 || anchorPoint.y > chipBounds.maxY + EPS9;
7172
7726
  });
7173
7727
  }
7174
7728
  getBoundsStatus(candidateBoundsCheck) {
@@ -7235,15 +7789,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7235
7789
  if (i === labelIndex) continue;
7236
7790
  const label = this.outputNetLabelPlacements[i];
7237
7791
  const otherBounds = getRectBounds(label.center, label.width, label.height);
7238
- if (rectsOverlap(bounds, otherBounds)) return true;
7792
+ if (rectsOverlap2(bounds, otherBounds)) return true;
7239
7793
  }
7240
7794
  return false;
7241
7795
  }
7242
7796
  sharesChipBoundary(bounds) {
7243
7797
  for (const chip of this.chipObstacleSpatialIndex.chips) {
7244
7798
  const chipBounds = chip.bounds;
7245
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS8 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS8;
7246
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS8 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS8;
7799
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS9;
7800
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS9;
7247
7801
  if (adjacentToVerticalSide && rangesOverlap(
7248
7802
  bounds.minY,
7249
7803
  bounds.maxY,
@@ -7291,13 +7845,13 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7291
7845
  let nearestDistance = Number.POSITIVE_INFINITY;
7292
7846
  for (const chip of this.chipObstacleSpatialIndex.chips) {
7293
7847
  const bounds = chip.bounds;
7294
- if (point.x < bounds.minX - EPS8 || point.x > bounds.maxX + EPS8 || point.y < bounds.minY - EPS8 || point.y > bounds.maxY + EPS8) {
7848
+ if (point.x < bounds.minX - EPS9 || point.x > bounds.maxX + EPS9 || point.y < bounds.minY - EPS9 || point.y > bounds.maxY + EPS9) {
7295
7849
  continue;
7296
7850
  }
7297
- for (const [side, distance3] of getSideDistances(point, bounds)) {
7298
- if (distance3 < nearestDistance) {
7851
+ for (const [side, distance4] of getSideDistances(point, bounds)) {
7852
+ if (distance4 < nearestDistance) {
7299
7853
  nearestSide = side;
7300
- nearestDistance = distance3;
7854
+ nearestDistance = distance4;
7301
7855
  }
7302
7856
  }
7303
7857
  }
@@ -7308,8 +7862,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7308
7862
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
7309
7863
  for (const chip of this.chipObstacleSpatialIndex.chips) {
7310
7864
  const bounds = chip.bounds;
7311
- const dx = point.x < bounds.minX - EPS8 ? bounds.minX - point.x : point.x > bounds.maxX + EPS8 ? point.x - bounds.maxX : 0;
7312
- const dy = point.y < bounds.minY - EPS8 ? bounds.minY - point.y : point.y > bounds.maxY + EPS8 ? point.y - bounds.maxY : 0;
7865
+ const dx = point.x < bounds.minX - EPS9 ? bounds.minX - point.x : point.x > bounds.maxX + EPS9 ? point.x - bounds.maxX : 0;
7866
+ const dy = point.y < bounds.minY - EPS9 ? bounds.minY - point.y : point.y > bounds.maxY + EPS9 ? point.y - bounds.maxY : 0;
7313
7867
  if (dx === 0 && dy === 0) continue;
7314
7868
  const distanceSq = dx ** 2 + dy ** 2;
7315
7869
  if (distanceSq >= nearestDistanceSq) continue;
@@ -7326,10 +7880,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7326
7880
  let nearestSide = null;
7327
7881
  let nearestDistance = Number.POSITIVE_INFINITY;
7328
7882
  for (const chip of this.chipObstacleSpatialIndex.chips) {
7329
- for (const [side, distance3] of getSideDistances(point, chip.bounds)) {
7330
- if (distance3 < nearestDistance) {
7883
+ for (const [side, distance4] of getSideDistances(point, chip.bounds)) {
7884
+ if (distance4 < nearestDistance) {
7331
7885
  nearestSide = side;
7332
- nearestDistance = distance3;
7886
+ nearestDistance = distance4;
7333
7887
  }
7334
7888
  }
7335
7889
  }
@@ -7347,39 +7901,6 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7347
7901
  }
7348
7902
  };
7349
7903
 
7350
- // lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
7351
- var EPS9 = 1e-6;
7352
- var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
7353
- var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
7354
- var getTraceCorners = (path) => {
7355
- const corners = [];
7356
- for (let i = 1; i < path.length - 1; i++) {
7357
- const previousPoint = path[i - 1];
7358
- const cornerPoint = path[i];
7359
- const nextPoint = path[i + 1];
7360
- if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
7361
- corners.push(cornerPoint);
7362
- }
7363
- }
7364
- return corners;
7365
- };
7366
- var tracePathContainsPoint = (path, point) => {
7367
- for (let i = 0; i < path.length - 1; i++) {
7368
- if (isPointOnSegment(point, path[i], path[i + 1])) return true;
7369
- }
7370
- return false;
7371
- };
7372
- var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
7373
- var getUniquePinCount = (pinIds) => new Set(pinIds).size;
7374
- var isTraceCorner = (a, b, c) => getSegmentOrientation2(a, b) !== getSegmentOrientation2(b, c);
7375
- var isPointOnSegment = (point, start, end) => {
7376
- if (getSegmentOrientation2(start, end) === "horizontal") {
7377
- return Math.abs(point.y - start.y) <= EPS9 && point.x >= Math.min(start.x, end.x) - EPS9 && point.x <= Math.max(start.x, end.x) + EPS9;
7378
- }
7379
- return Math.abs(point.x - start.x) <= EPS9 && point.y >= Math.min(start.y, end.y) - EPS9 && point.y <= Math.max(start.y, end.y) + EPS9;
7380
- };
7381
- var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS9 ? "horizontal" : "vertical";
7382
-
7383
7904
  // lib/solvers/RailNetLabelCornerPlacementSolver/visualize.ts
7384
7905
  var CANDIDATE_SELECTED_COLOR3 = "blue";
7385
7906
  var CANDIDATE_REJECTED_COLOR3 = "red";
@@ -7602,20 +8123,20 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
7602
8123
  }
7603
8124
  intersectsAnyChip(bounds) {
7604
8125
  return this.inputProblem.chips.some(
7605
- (chip) => rectsOverlap2(bounds, getRectBounds(chip.center, chip.width, chip.height))
8126
+ (chip) => rectsOverlap(bounds, getRectBounds(chip.center, chip.width, chip.height))
7606
8127
  );
7607
8128
  }
7608
8129
  intersectsAnyOtherNetLabel(bounds, labelIndex) {
7609
8130
  return this.outputNetLabelPlacements.some((label, index) => {
7610
8131
  if (index === labelIndex) return false;
7611
- return rectsOverlap2(
8132
+ return rectsOverlap(
7612
8133
  bounds,
7613
8134
  getRectBounds(label.center, label.width, label.height)
7614
8135
  );
7615
8136
  });
7616
8137
  }
7617
8138
  getCornerCandidatesForLabel(label) {
7618
- const isVertical4 = label.orientation === "y+" || label.orientation === "y-";
8139
+ const isVertical5 = label.orientation === "y+" || label.orientation === "y-";
7619
8140
  const candidates = [];
7620
8141
  const seenCornerKeys = /* @__PURE__ */ new Set();
7621
8142
  for (const trace of this.getTraceLinesForLabel(label)) {
@@ -7626,14 +8147,14 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
7626
8147
  if (seenCornerKeys.has(key)) continue;
7627
8148
  seenCornerKeys.add(key);
7628
8149
  const pinAligned = pins.some((pin) => {
7629
- if (isVertical4) return Math.abs(pin.x - anchorPoint.x) < EPS9;
7630
- return Math.abs(pin.y - anchorPoint.y) < EPS9;
8150
+ if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS5;
8151
+ return Math.abs(pin.y - anchorPoint.y) < EPS5;
7631
8152
  });
7632
8153
  if (!pinAligned) continue;
7633
8154
  candidates.push({
7634
8155
  anchorPoint,
7635
8156
  traceId: trace.mspPairId,
7636
- distance: getDistance2(anchorPoint, label.anchorPoint),
8157
+ distance: getDistance(anchorPoint, label.anchorPoint),
7637
8158
  pinAligned
7638
8159
  });
7639
8160
  }
@@ -7763,17 +8284,17 @@ var getTraceLength = (trace) => {
7763
8284
  }
7764
8285
  return length;
7765
8286
  };
7766
- var getPointAtTraceDistance = (trace, distance3) => {
8287
+ var getPointAtTraceDistance = (trace, distance4) => {
7767
8288
  let pathDistance = 0;
7768
8289
  for (let i = 0; i < trace.tracePath.length - 1; i++) {
7769
8290
  const start = trace.tracePath[i];
7770
8291
  const end = trace.tracePath[i + 1];
7771
8292
  const segmentLength = getManhattanDistance(start, end);
7772
8293
  const nextDistance = pathDistance + segmentLength;
7773
- if (distance3 <= nextDistance + EPS10) {
8294
+ if (distance4 <= nextDistance + EPS10) {
7774
8295
  const offset = Math.max(
7775
8296
  0,
7776
- Math.min(segmentLength, distance3 - pathDistance)
8297
+ Math.min(segmentLength, distance4 - pathDistance)
7777
8298
  );
7778
8299
  const direction = getSegmentDirection(start, end);
7779
8300
  return {
@@ -7808,7 +8329,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
7808
8329
  if (interior.minX >= interior.maxX || interior.minY >= interior.maxY) {
7809
8330
  return false;
7810
8331
  }
7811
- if (isVertical3(start, end)) {
8332
+ if (isVertical4(start, end)) {
7812
8333
  if (start.x <= interior.minX + EPS10 || start.x >= interior.maxX - EPS10) {
7813
8334
  return false;
7814
8335
  }
@@ -7819,7 +8340,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
7819
8340
  interior.maxY
7820
8341
  );
7821
8342
  }
7822
- if (isHorizontal2(start, end)) {
8343
+ if (isHorizontal3(start, end)) {
7823
8344
  if (start.y <= interior.minY + EPS10 || start.y >= interior.maxY - EPS10) {
7824
8345
  return false;
7825
8346
  }
@@ -7833,20 +8354,20 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
7833
8354
  return false;
7834
8355
  };
7835
8356
  var isPointOnSegment2 = (point, start, end) => {
7836
- if (isHorizontal2(start, end)) {
8357
+ if (isHorizontal3(start, end)) {
7837
8358
  return Math.abs(point.y - start.y) <= EPS10 && point.x >= Math.min(start.x, end.x) - EPS10 && point.x <= Math.max(start.x, end.x) + EPS10;
7838
8359
  }
7839
- if (isVertical3(start, end)) {
8360
+ if (isVertical4(start, end)) {
7840
8361
  return Math.abs(point.x - start.x) <= EPS10 && point.y >= Math.min(start.y, end.y) - EPS10 && point.y <= Math.max(start.y, end.y) + EPS10;
7841
8362
  }
7842
8363
  return false;
7843
8364
  };
7844
8365
  var getSegmentDirection = (start, end) => ({
7845
- x: isVertical3(start, end) ? 0 : Math.sign(end.x - start.x),
7846
- y: isHorizontal2(start, end) ? 0 : Math.sign(end.y - start.y)
8366
+ x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
8367
+ y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
7847
8368
  });
7848
- var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
7849
- var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
8369
+ var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
8370
+ var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
7850
8371
  var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
7851
8372
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
7852
8373
 
@@ -7898,13 +8419,13 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
7898
8419
  const distances = /* @__PURE__ */ new Set();
7899
8420
  const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
7900
8421
  for (let i = 0; i <= maxSteps; i++) {
7901
- const distance3 = Math.min(traceLength, i * CANDIDATE_STEP);
7902
- distances.add(roundDistance(distance3));
8422
+ const distance4 = Math.min(traceLength, i * CANDIDATE_STEP);
8423
+ distances.add(roundDistance(distance4));
7903
8424
  }
7904
- for (const distance3 of vertexDistances) {
7905
- distances.add(roundDistance(distance3));
8425
+ for (const distance4 of vertexDistances) {
8426
+ distances.add(roundDistance(distance4));
7906
8427
  }
7907
- return [...distances].filter((distance3) => distance3 >= -EPS10 && distance3 <= traceLength + EPS10).sort((a, b) => a - b);
8428
+ return [...distances].filter((distance4) => distance4 >= -EPS10 && distance4 <= traceLength + EPS10).sort((a, b) => a - b);
7908
8429
  };
7909
8430
  var getOrientationsForPoint = (params) => {
7910
8431
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -8082,7 +8603,7 @@ var getNetLabelHeight = (inputProblem, label) => {
8082
8603
  (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
8083
8604
  )?.netLabelHeight;
8084
8605
  };
8085
- var roundDistance = (distance3) => Number(distance3.toFixed(6));
8606
+ var roundDistance = (distance4) => Number(distance4.toFixed(6));
8086
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;
8087
8608
 
8088
8609
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
@@ -8855,11 +9376,11 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
8855
9376
  for (let si = 0; si < pts.length - 1; si++) {
8856
9377
  const segStart = pts[si];
8857
9378
  const segEnd = pts[si + 1];
8858
- const isHorizontal3 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
8859
- const isVertical4 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
8860
- if (!isHorizontal3 && !isVertical4) continue;
9379
+ const isHorizontal4 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
9380
+ const isVertical5 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
9381
+ if (!isHorizontal4 && !isVertical5) continue;
8861
9382
  let perpendicularOrientations;
8862
- if (isHorizontal3) {
9383
+ if (isHorizontal4) {
8863
9384
  perpendicularOrientations = ["y+", "y-"];
8864
9385
  } else {
8865
9386
  perpendicularOrientations = ["x+", "x-"];
@@ -9072,7 +9593,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
9072
9593
  availableNetOrientationSolver;
9073
9594
  railNetLabelCornerPlacementSolver;
9074
9595
  traceAnchoredNetLabelOverlapSolver;
9596
+ preAlignmentNetLabelTraceCollisionSolver;
9075
9597
  netLabelTraceCollisionSolver;
9598
+ traceCleanupSolver2;
9076
9599
  netLabelNetLabelCollisionSolver;
9077
9600
  startTimeOfPhase;
9078
9601
  endTimeOfPhase;
@@ -9257,7 +9780,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
9257
9780
  ]
9258
9781
  ),
9259
9782
  definePipelineStep(
9260
- "netLabelTraceCollisionSolver",
9783
+ "preAlignmentNetLabelTraceCollisionSolver",
9261
9784
  NetLabelTraceCollisionSolver,
9262
9785
  (instance) => [
9263
9786
  {
@@ -9267,6 +9790,41 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
9267
9790
  }
9268
9791
  ]
9269
9792
  ),
9793
+ definePipelineStep(
9794
+ "traceCleanupSolver2",
9795
+ TraceCleanupSolver,
9796
+ (instance) => {
9797
+ const collisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
9798
+ const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
9799
+ return [
9800
+ {
9801
+ inputProblem: instance.inputProblem,
9802
+ allTraces: collisionOutput.traces,
9803
+ allLabelPlacements: collisionOutput.netLabelPlacements,
9804
+ mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
9805
+ paddingBuffer: 0.1,
9806
+ operations: ["aligning_same_net_rails"],
9807
+ eligibleTraceIds: new Set(
9808
+ instance.traceCleanupSolver.getOutput().traces.map((trace) => trace.mspPairId)
9809
+ )
9810
+ }
9811
+ ];
9812
+ }
9813
+ ),
9814
+ definePipelineStep(
9815
+ "netLabelTraceCollisionSolver",
9816
+ NetLabelTraceCollisionSolver,
9817
+ (instance) => {
9818
+ const previousCollisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
9819
+ return [
9820
+ {
9821
+ inputProblem: instance.inputProblem,
9822
+ traces: instance.traceCleanupSolver2.getOutput().traces,
9823
+ netLabelPlacements: previousCollisionOutput.netLabelPlacements
9824
+ }
9825
+ ];
9826
+ }
9827
+ ),
9270
9828
  definePipelineStep(
9271
9829
  "netLabelNetLabelCollisionSolver",
9272
9830
  NetLabelNetLabelCollisionSolver,