@tscircuit/schematic-trace-solver 0.0.54 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -56,6 +56,7 @@ type FacingDirection = "x+" | "x-" | "y+" | "y-";
56
56
  type ChipId = string;
57
57
  type PinId = string;
58
58
  type NetId = string;
59
+ type SectionId = string;
59
60
  interface InputPin {
60
61
  pinId: PinId;
61
62
  x: number;
@@ -71,6 +72,7 @@ interface InputChip {
71
72
  width: number;
72
73
  height: number;
73
74
  pins: Array<InputPin>;
75
+ sectionId?: SectionId;
74
76
  }
75
77
  interface InputDirectConnection {
76
78
  pinIds: [PinId, PinId];
@@ -818,6 +820,32 @@ declare class TraceAnchoredNetLabelOverlapSolver extends BaseSolver {
818
820
  visualize(): GraphicsObject;
819
821
  }
820
822
 
823
+ interface NetLabelTraceCollisionSolverParams {
824
+ inputProblem: InputProblem;
825
+ traces: SolvedTracePath[];
826
+ netLabelPlacements: NetLabelPlacement[];
827
+ }
828
+ declare class NetLabelTraceCollisionSolver extends BaseSolver {
829
+ inputProblem: InputProblem;
830
+ traces: SolvedTracePath[];
831
+ netLabelPlacements: NetLabelPlacement[];
832
+ outputTraces: SolvedTracePath[];
833
+ outputNetLabelPlacements: NetLabelPlacement[];
834
+ activeSubSolver: SingleOverlapSolver | null;
835
+ private recentlyFailed;
836
+ private detourCounts;
837
+ private currentOverlap;
838
+ constructor(params: NetLabelTraceCollisionSolverParams);
839
+ getConstructorParams(): ConstructorParameters<typeof NetLabelTraceCollisionSolver>[0];
840
+ _step(): void;
841
+ getOutput(): {
842
+ traces: SolvedTracePath[];
843
+ netLabelPlacements: NetLabelPlacement[];
844
+ };
845
+ private getOverlapKey;
846
+ visualize(): GraphicsObject;
847
+ }
848
+
821
849
  /**
822
850
  * Pipeline solver that runs a series of solvers to find the best schematic layout.
823
851
  * Coordinates the entire layout process from chip partitioning through final packing.
@@ -847,12 +875,13 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
847
875
  availableNetOrientationSolver?: AvailableNetOrientationSolver;
848
876
  vccNetLabelCornerPlacementSolver?: VccNetLabelCornerPlacementSolver;
849
877
  traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver;
878
+ netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver;
850
879
  startTimeOfPhase: Record<string, number>;
851
880
  endTimeOfPhase: Record<string, number>;
852
881
  timeSpentOnPhase: Record<string, number>;
853
882
  firstIterationOfPhase: Record<string, number>;
854
883
  inputProblem: InputProblem;
855
- pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof VccNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver>)[];
884
+ pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof VccNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver>)[];
856
885
  constructor(inputProblem: InputProblem);
857
886
  getConstructorParams(): ConstructorParameters<typeof SchematicTracePipelineSolver>[0];
858
887
  currentPipelineStepIndex: number;
@@ -868,4 +897,4 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
868
897
  preview(): GraphicsObject;
869
898
  }
870
899
 
871
- export { type ChipId, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type PinId, SchematicTracePipelineSolver, type SchematicTracePipelineSolverParams, SchematicTraceSingleLineSolver2 };
900
+ export { type ChipId, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type PinId, SchematicTracePipelineSolver, type SchematicTracePipelineSolverParams, SchematicTraceSingleLineSolver2, type SectionId };
package/dist/index.js CHANGED
@@ -301,6 +301,30 @@ var getColorFromString = (string, alpha = 1) => {
301
301
  return `hsl(${hash % 360}, 100%, 50%, ${alpha})`;
302
302
  };
303
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;
309
+ }
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
+ }
321
+ }
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 && s1 !== s2;
326
+ };
327
+
304
328
  // lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts
305
329
  var visualizeInputProblem = (inputProblem, opts = {}) => {
306
330
  const { connectionAlpha = 0.8, chipAlpha = 0.8 } = opts;
@@ -337,6 +361,9 @@ ${pin._facingDirection ?? getPinDirection(pin, chip)}`,
337
361
  const [pinId1, pinId2] = directConn.pinIds;
338
362
  const pin1 = pinIdMap.get(pinId1);
339
363
  const pin2 = pinIdMap.get(pinId2);
364
+ if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
365
+ continue;
366
+ }
340
367
  graphics.lines.push({
341
368
  points: [
342
369
  {
@@ -360,6 +387,9 @@ ${pin._facingDirection ?? getPinDirection(pin, chip)}`,
360
387
  for (let j = i + 1; j < pins.length; j++) {
361
388
  const pin1 = pins[i];
362
389
  const pin2 = pins[j];
390
+ if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
391
+ continue;
392
+ }
363
393
  graphics.lines.push({
364
394
  points: [
365
395
  { x: pin1.x, y: pin1.y },
@@ -441,6 +471,9 @@ var MspConnectionPairSolver = class extends BaseSolver {
441
471
  if (manhattanDist > this.maxMspPairDistance) {
442
472
  return;
443
473
  }
474
+ if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
475
+ return;
476
+ }
444
477
  const pinIdMap2 = new Map(Object.entries(this.pinMap));
445
478
  if (doesPairCrossRestrictedCenterLines({
446
479
  inputProblem: this.inputProblem,
@@ -467,7 +500,11 @@ var MspConnectionPairSolver = class extends BaseSolver {
467
500
  directlyConnectedPins.map((p) => this.pinMap[p]).filter(Boolean),
468
501
  {
469
502
  maxDistance: this.maxMspPairDistance,
470
- forbidEdge: (a, b) => doesPairCrossRestrictedCenterLines({
503
+ forbidEdge: (a, b) => arePinsInDifferentSchematicSections(
504
+ this.inputProblem,
505
+ a,
506
+ b
507
+ ) || doesPairCrossRestrictedCenterLines({
471
508
  inputProblem: this.inputProblem,
472
509
  chipMap: this.chipMap,
473
510
  pinIdMap,
@@ -479,6 +516,9 @@ var MspConnectionPairSolver = class extends BaseSolver {
479
516
  for (const [pin1, pin2] of msp) {
480
517
  const p1Obj = this.pinMap[pin1];
481
518
  const p2Obj = this.pinMap[pin2];
519
+ if (arePinsInDifferentSchematicSections(this.inputProblem, p1Obj, p2Obj)) {
520
+ continue;
521
+ }
482
522
  if (doesPairCrossRestrictedCenterLines({
483
523
  inputProblem: this.inputProblem,
484
524
  chipMap: this.chipMap,
@@ -3461,6 +3501,9 @@ var LongDistancePairSolver = class extends BaseSolver {
3461
3501
  }).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
3462
3502
  for (const neighbor of neighbors) {
3463
3503
  const pair = [sourcePin, neighbor.pin];
3504
+ if (arePinsInDifferentSchematicSections(inputProblem, pair[0], pair[1])) {
3505
+ continue;
3506
+ }
3464
3507
  const pairKey = pair.map((p) => p.pinId).sort().join("--");
3465
3508
  if (!addedPairKeys.has(pairKey)) {
3466
3509
  candidatePairs.push(pair);
@@ -3559,7 +3602,10 @@ var LongDistancePairSolver = class extends BaseSolver {
3559
3602
  }
3560
3603
  getOutput() {
3561
3604
  if (!this.solved) {
3562
- return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
3605
+ return {
3606
+ newTraces: [],
3607
+ allTracesMerged: this.params.alreadySolvedTraces
3608
+ };
3563
3609
  }
3564
3610
  return {
3565
3611
  newTraces: this.solvedLongDistanceTraces,
@@ -7104,6 +7150,214 @@ var TraceAnchoredNetLabelOverlapSolver = class extends BaseSolver {
7104
7150
  }
7105
7151
  };
7106
7152
 
7153
+ // lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts
7154
+ function buildMergedObstacleLabel(seedLabel, allLabels) {
7155
+ const TOUCH_MARGIN = 0.01;
7156
+ const getBounds3 = (l) => getRectBounds(l.center, l.width, l.height);
7157
+ const touches = (a, b) => {
7158
+ const ba = getBounds3(a);
7159
+ const bb = getBounds3(b);
7160
+ return ba.minX <= bb.maxX + TOUCH_MARGIN && ba.maxX >= bb.minX - TOUCH_MARGIN && ba.minY <= bb.maxY + TOUCH_MARGIN && ba.maxY >= bb.minY - TOUCH_MARGIN;
7161
+ };
7162
+ const group = /* @__PURE__ */ new Set([seedLabel]);
7163
+ let changed = true;
7164
+ while (changed) {
7165
+ changed = false;
7166
+ for (const l of allLabels) {
7167
+ if (group.has(l)) continue;
7168
+ if ([...group].some((g) => touches(g, l))) {
7169
+ group.add(l);
7170
+ changed = true;
7171
+ }
7172
+ }
7173
+ }
7174
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
7175
+ for (const l of group) {
7176
+ const b = getBounds3(l);
7177
+ if (b.minX < minX) minX = b.minX;
7178
+ if (b.minY < minY) minY = b.minY;
7179
+ if (b.maxX > maxX) maxX = b.maxX;
7180
+ if (b.maxY > maxY) maxY = b.maxY;
7181
+ }
7182
+ const cx = (minX + maxX) / 2;
7183
+ const cy = (minY + maxY) / 2;
7184
+ const w = maxX - minX;
7185
+ const h = maxY - minY;
7186
+ return {
7187
+ ...seedLabel,
7188
+ center: { x: cx, y: cy },
7189
+ width: w,
7190
+ height: h
7191
+ };
7192
+ }
7193
+ function detectInnerTraceLabelOverlaps(traces, labels) {
7194
+ const overlaps = [];
7195
+ for (const trace of traces) {
7196
+ for (const label of labels) {
7197
+ if (trace.globalConnNetId === label.globalConnNetId) continue;
7198
+ const bounds = getRectBounds(label.center, label.width, label.height);
7199
+ const path = trace.tracePath;
7200
+ const innerStart = 1;
7201
+ const innerEnd = path.length - 2;
7202
+ for (let i = innerStart; i < innerEnd; i++) {
7203
+ if (segmentIntersectsRect2(path[i], path[i + 1], bounds)) {
7204
+ overlaps.push({ trace, label });
7205
+ break;
7206
+ }
7207
+ }
7208
+ }
7209
+ }
7210
+ return overlaps;
7211
+ }
7212
+ var PADDING_BUFFER = 0.1;
7213
+ var MAX_DETOUR_ATTEMPTS = 3;
7214
+ var NetLabelTraceCollisionSolver = class extends BaseSolver {
7215
+ inputProblem;
7216
+ traces;
7217
+ netLabelPlacements;
7218
+ outputTraces;
7219
+ outputNetLabelPlacements;
7220
+ activeSubSolver = null;
7221
+ recentlyFailed = /* @__PURE__ */ new Set();
7222
+ detourCounts = /* @__PURE__ */ new Map();
7223
+ currentOverlap = null;
7224
+ constructor(params) {
7225
+ super();
7226
+ this.inputProblem = params.inputProblem;
7227
+ this.traces = params.traces;
7228
+ this.netLabelPlacements = params.netLabelPlacements;
7229
+ this.outputTraces = [...params.traces];
7230
+ this.outputNetLabelPlacements = [...params.netLabelPlacements];
7231
+ }
7232
+ getConstructorParams() {
7233
+ return {
7234
+ inputProblem: this.inputProblem,
7235
+ traces: this.traces,
7236
+ netLabelPlacements: this.netLabelPlacements
7237
+ };
7238
+ }
7239
+ _step() {
7240
+ if (this.activeSubSolver) {
7241
+ this.activeSubSolver.step();
7242
+ if (this.activeSubSolver.solved) {
7243
+ const solvedPath = this.activeSubSolver.solvedTracePath;
7244
+ if (solvedPath) {
7245
+ const idx = this.outputTraces.findIndex(
7246
+ (t) => t.mspPairId === this.activeSubSolver.initialTrace.mspPairId
7247
+ );
7248
+ if (idx !== -1) {
7249
+ this.outputTraces[idx] = {
7250
+ ...this.outputTraces[idx],
7251
+ tracePath: solvedPath
7252
+ };
7253
+ }
7254
+ }
7255
+ this.activeSubSolver = null;
7256
+ this.currentOverlap = null;
7257
+ } else if (this.activeSubSolver.failed) {
7258
+ const key = this.getOverlapKey(
7259
+ this.activeSubSolver.initialTrace,
7260
+ this.activeSubSolver.label
7261
+ );
7262
+ this.recentlyFailed.add(key);
7263
+ this.activeSubSolver = null;
7264
+ this.currentOverlap = null;
7265
+ }
7266
+ return;
7267
+ }
7268
+ const overlaps = detectInnerTraceLabelOverlaps(
7269
+ this.outputTraces,
7270
+ this.outputNetLabelPlacements
7271
+ );
7272
+ const actionable = overlaps.filter((o) => {
7273
+ const key = this.getOverlapKey(o.trace, o.label);
7274
+ return !this.recentlyFailed.has(key);
7275
+ });
7276
+ if (actionable.length === 0) {
7277
+ this.solved = true;
7278
+ return;
7279
+ }
7280
+ const next = actionable[0];
7281
+ const traceToFix = this.outputTraces.find(
7282
+ (t) => t.mspPairId === next.trace.mspPairId
7283
+ );
7284
+ this.currentOverlap = next;
7285
+ const mergedLabel = buildMergedObstacleLabel(
7286
+ next.label,
7287
+ this.outputNetLabelPlacements.filter(
7288
+ (l) => l.globalConnNetId !== traceToFix.globalConnNetId
7289
+ )
7290
+ );
7291
+ const mergeKey = `${next.trace.mspPairId}::merged::${mergedLabel.center.x},${mergedLabel.center.y},${mergedLabel.width},${mergedLabel.height}`;
7292
+ const detourCount = this.detourCounts.get(mergeKey) ?? 0;
7293
+ if (detourCount >= MAX_DETOUR_ATTEMPTS) {
7294
+ this.recentlyFailed.add(this.getOverlapKey(next.trace, next.label));
7295
+ this.currentOverlap = null;
7296
+ return;
7297
+ }
7298
+ this.detourCounts.set(mergeKey, detourCount + 1);
7299
+ this.activeSubSolver = new SingleOverlapSolver({
7300
+ trace: traceToFix,
7301
+ label: mergedLabel,
7302
+ problem: this.inputProblem,
7303
+ paddingBuffer: PADDING_BUFFER,
7304
+ detourCount
7305
+ });
7306
+ }
7307
+ getOutput() {
7308
+ return {
7309
+ traces: this.outputTraces,
7310
+ netLabelPlacements: this.outputNetLabelPlacements
7311
+ };
7312
+ }
7313
+ getOverlapKey(trace, label) {
7314
+ return `${trace.mspPairId}::${label.globalConnNetId}`;
7315
+ }
7316
+ visualize() {
7317
+ if (this.activeSubSolver) return this.activeSubSolver.visualize();
7318
+ const graphics = visualizeInputProblem(this.inputProblem);
7319
+ if (!graphics.lines) graphics.lines = [];
7320
+ if (!graphics.rects) graphics.rects = [];
7321
+ if (!graphics.points) graphics.points = [];
7322
+ for (const trace of this.outputTraces) {
7323
+ graphics.lines.push({
7324
+ points: trace.tracePath,
7325
+ strokeColor: "purple"
7326
+ });
7327
+ }
7328
+ for (const label of this.outputNetLabelPlacements) {
7329
+ graphics.rects.push({
7330
+ center: label.center,
7331
+ width: label.width,
7332
+ height: label.height,
7333
+ fill: getColorFromString(label.globalConnNetId, 0.35),
7334
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
7335
+ label: `netId: ${label.netId}
7336
+ globalConnNetId: ${label.globalConnNetId}`
7337
+ });
7338
+ graphics.points.push({
7339
+ x: label.anchorPoint.x,
7340
+ y: label.anchorPoint.y,
7341
+ color: getColorFromString(label.globalConnNetId, 0.9)
7342
+ });
7343
+ }
7344
+ if (this.currentOverlap) {
7345
+ graphics.lines.push({
7346
+ points: this.currentOverlap.trace.tracePath,
7347
+ strokeColor: "red"
7348
+ });
7349
+ graphics.rects.push({
7350
+ center: this.currentOverlap.label.center,
7351
+ width: this.currentOverlap.label.width,
7352
+ height: this.currentOverlap.label.height,
7353
+ fill: "rgba(255,0,0,0.3)",
7354
+ strokeColor: "red"
7355
+ });
7356
+ }
7357
+ return graphics;
7358
+ }
7359
+ };
7360
+
7107
7361
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
7108
7362
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
7109
7363
  return {
@@ -7128,6 +7382,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
7128
7382
  availableNetOrientationSolver;
7129
7383
  vccNetLabelCornerPlacementSolver;
7130
7384
  traceAnchoredNetLabelOverlapSolver;
7385
+ netLabelTraceCollisionSolver;
7131
7386
  startTimeOfPhase;
7132
7387
  endTimeOfPhase;
7133
7388
  timeSpentOnPhase;
@@ -7308,6 +7563,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
7308
7563
  netLabelPlacements: instance.vccNetLabelCornerPlacementSolver.outputNetLabelPlacements
7309
7564
  }
7310
7565
  ]
7566
+ ),
7567
+ definePipelineStep(
7568
+ "netLabelTraceCollisionSolver",
7569
+ NetLabelTraceCollisionSolver,
7570
+ (instance) => [
7571
+ {
7572
+ inputProblem: instance.inputProblem,
7573
+ traces: instance.availableNetOrientationSolver.traces,
7574
+ netLabelPlacements: instance.traceAnchoredNetLabelOverlapSolver.outputNetLabelPlacements
7575
+ }
7576
+ ]
7311
7577
  )
7312
7578
  ];
7313
7579
  constructor(inputProblem) {
@@ -12,6 +12,7 @@ import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap
12
12
  import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
13
13
  import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
14
14
  import type { ConnectivityMap } from "connectivity-map"
15
+ import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDifferentSchematicSections"
15
16
 
16
17
  const NEAREST_NEIGHBOR_COUNT = 3
17
18
 
@@ -104,6 +105,11 @@ export class LongDistancePairSolver extends BaseSolver {
104
105
  InputPin & { chipId: string },
105
106
  InputPin & { chipId: string },
106
107
  ] = [sourcePin, neighbor.pin]
108
+ if (
109
+ arePinsInDifferentSchematicSections(inputProblem, pair[0], pair[1])
110
+ ) {
111
+ continue
112
+ }
107
113
  const pairKey = pair
108
114
  .map((p) => p.pinId)
109
115
  .sort()
@@ -225,7 +231,10 @@ export class LongDistancePairSolver extends BaseSolver {
225
231
  allTracesMerged: SolvedTracePath[]
226
232
  } {
227
233
  if (!this.solved) {
228
- return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces }
234
+ return {
235
+ newTraces: [],
236
+ allTracesMerged: this.params.alreadySolvedTraces,
237
+ }
229
238
  }
230
239
  return {
231
240
  newTraces: this.solvedLongDistanceTraces,
@@ -12,6 +12,7 @@ import { doesPairCrossRestrictedCenterLines } from "./doesPairCrossRestrictedCen
12
12
  import type { GraphicsObject } from "graphics-debug"
13
13
  import { getColorFromString } from "lib/utils/getColorFromString"
14
14
  import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
15
+ import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDifferentSchematicSections"
15
16
 
16
17
  export type MspConnectionPairId = string
17
18
 
@@ -110,6 +111,9 @@ export class MspConnectionPairSolver extends BaseSolver {
110
111
  // Too far apart; skip creating an MSP pair for this net
111
112
  return
112
113
  }
114
+ if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
115
+ return
116
+ }
113
117
  // Avoid pairs that would cross restricted center lines
114
118
  const pinIdMap = new Map(Object.entries(this.pinMap)) as Map<
115
119
  PinId,
@@ -153,6 +157,11 @@ export class MspConnectionPairSolver extends BaseSolver {
153
157
  {
154
158
  maxDistance: this.maxMspPairDistance,
155
159
  forbidEdge: (a, b) =>
160
+ arePinsInDifferentSchematicSections(
161
+ this.inputProblem,
162
+ a as InputPin & { chipId: string },
163
+ b as InputPin & { chipId: string },
164
+ ) ||
156
165
  doesPairCrossRestrictedCenterLines({
157
166
  inputProblem: this.inputProblem,
158
167
  chipMap: this.chipMap,
@@ -166,6 +175,11 @@ export class MspConnectionPairSolver extends BaseSolver {
166
175
  for (const [pin1, pin2] of msp) {
167
176
  const p1Obj = this.pinMap[pin1!]!
168
177
  const p2Obj = this.pinMap[pin2!]!
178
+ if (
179
+ arePinsInDifferentSchematicSections(this.inputProblem, p1Obj, p2Obj)
180
+ ) {
181
+ continue
182
+ }
169
183
  // Skip any edge that would cross a restricted center line (safety filter)
170
184
  if (
171
185
  doesPairCrossRestrictedCenterLines({