@tscircuit/schematic-trace-solver 0.0.144 → 0.0.146

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
@@ -146,6 +146,8 @@ type MspConnectionPair = {
146
146
  dcConnNetId: string;
147
147
  globalConnNetId: string;
148
148
  userNetId?: string;
149
+ /** The trace replaces fallback labels that could not fit between its pins. */
150
+ suppressNetLabel?: boolean;
149
151
  pins: [InputPin & {
150
152
  chipId: string;
151
153
  }, InputPin & {
@@ -1248,6 +1250,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
1248
1250
  queuedConnections: InlineEligibleConnection[];
1249
1251
  private tracesByPinPairKey;
1250
1252
  private hasAlignedPortOnlyStubs;
1253
+ private postProcessedOutput?;
1251
1254
  constructor(input: InlineNetLabelSolverInput);
1252
1255
  getConstructorParams(): [InlineNetLabelSolverInput];
1253
1256
  _step(): void;
@@ -1277,6 +1280,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
1277
1280
  */
1278
1281
  private getSupersededNetLabelKeys;
1279
1282
  private getOutputTraces;
1283
+ private buildPostProcessedOutput;
1280
1284
  getOutput(): {
1281
1285
  traces: SolvedTracePath[];
1282
1286
  netLabelPlacements: NetLabelPlacement[];
package/dist/index.js CHANGED
@@ -420,8 +420,48 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
420
420
  return edges;
421
421
  }
422
422
 
423
+ // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
424
+ var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
425
+ var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
426
+ function getDimsForOrientation(params) {
427
+ const { orientation, netLabelWidth, netLabelHeight } = params;
428
+ const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
429
+ const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
430
+ if (orientation === "y+" || orientation === "y-") {
431
+ return {
432
+ // Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
433
+ width: horizHeight,
434
+ height: horizWidth
435
+ };
436
+ }
437
+ return {
438
+ width: horizWidth,
439
+ height: horizHeight
440
+ };
441
+ }
442
+ function getCenterFromAnchor(anchor, orientation, width, height) {
443
+ switch (orientation) {
444
+ case "x+":
445
+ return { x: anchor.x + width / 2, y: anchor.y };
446
+ case "x-":
447
+ return { x: anchor.x - width / 2, y: anchor.y };
448
+ case "y+":
449
+ return { x: anchor.x, y: anchor.y + height / 2 };
450
+ case "y-":
451
+ return { x: anchor.x, y: anchor.y - height / 2 };
452
+ }
453
+ }
454
+ function getRectBounds(center, w, h) {
455
+ return {
456
+ minX: center.x - w / 2,
457
+ minY: center.y - h / 2,
458
+ maxX: center.x + w / 2,
459
+ maxY: center.y + h / 2
460
+ };
461
+ }
462
+
423
463
  // lib/solvers/MspConnectionPairSolver/isLabeledPeripheralConnection.ts
424
- var isLabeledPeripheralConnection = ({
464
+ var getLabeledConnectionRouteReason = ({
425
465
  inputProblem,
426
466
  chipMap,
427
467
  pins
@@ -430,12 +470,10 @@ var isLabeledPeripheralConnection = ({
430
470
  const directConnection = inputProblem.directConnections.find(
431
471
  (connection) => connection.pinIds.includes(firstPin.pinId) && connection.pinIds.includes(secondPin.pinId)
432
472
  );
433
- if (directConnection?.netLabelWidth === void 0) return false;
473
+ if (directConnection?.netLabelWidth === void 0) return null;
434
474
  const firstChip = chipMap[firstPin.chipId];
435
475
  const secondChip = chipMap[secondPin.chipId];
436
- if (!firstChip || !secondChip) return false;
437
- const hasSinglePinPeripheral = firstChip.pins.length === 1 || secondChip.pins.length === 1;
438
- if (!hasSinglePinPeripheral) return false;
476
+ if (!firstChip || !secondChip) return null;
439
477
  let firstFacingDirection = firstPin._facingDirection;
440
478
  if (!firstFacingDirection) {
441
479
  firstFacingDirection = getPinDirection(firstPin, firstChip);
@@ -444,8 +482,20 @@ var isLabeledPeripheralConnection = ({
444
482
  if (!secondFacingDirection) {
445
483
  secondFacingDirection = getPinDirection(secondPin, secondChip);
446
484
  }
447
- return firstFacingDirection === "x-" && secondFacingDirection === "x+" || firstFacingDirection === "x+" && secondFacingDirection === "x-";
448
- };
485
+ const hasOpposingHorizontalDirections = firstFacingDirection === "x-" && secondFacingDirection === "x+" || firstFacingDirection === "x+" && secondFacingDirection === "x-";
486
+ if (!hasOpposingHorizontalDirections) return null;
487
+ const hasSinglePinPeripheral = firstChip.pins.length === 1 || secondChip.pins.length === 1;
488
+ if (hasSinglePinPeripheral) return "single-pin-peripheral";
489
+ const sharedSectionId = firstChip.sectionId && firstChip.sectionId === secondChip.sectionId ? firstChip.sectionId : null;
490
+ if (!sharedSectionId) return null;
491
+ const firstIsLeft = firstPin.x <= secondPin.x;
492
+ const directionsPointTowardEachOther = firstIsLeft ? firstFacingDirection === "x+" && secondFacingDirection === "x-" : firstFacingDirection === "x-" && secondFacingDirection === "x+";
493
+ const labelsOverlapAlongX = Math.abs(firstPin.x - secondPin.x) < directConnection.netLabelWidth * 2;
494
+ const labelsOverlapAlongY = Math.abs(firstPin.y - secondPin.y) < NET_LABEL_HORIZONTAL_HEIGHT;
495
+ const fallbackLabelsOverlap = directionsPointTowardEachOther && labelsOverlapAlongX && labelsOverlapAlongY;
496
+ return fallbackLabelsOverlap ? "overlapping-fallback-labels" : null;
497
+ };
498
+ var isLabeledPeripheralConnection = (params) => getLabeledConnectionRouteReason(params) !== null;
449
499
 
450
500
  // lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
451
501
  var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
@@ -520,12 +570,12 @@ var MspConnectionPairSolver = class extends BaseSolver {
520
570
  if (this.directConnectionPinPairKeys.has(pinPairKey)) {
521
571
  pairDistance = distance(p1, p2);
522
572
  }
523
- const isLabeledPeripheral = isLabeledPeripheralConnection({
573
+ const labeledConnectionRouteReason = getLabeledConnectionRouteReason({
524
574
  inputProblem: this.inputProblem,
525
575
  chipMap: this.chipMap,
526
576
  pins: [p1, p2]
527
577
  });
528
- if (pairDistance > this.maxMspPairDistance && !isLabeledPeripheral) {
578
+ if (pairDistance > this.maxMspPairDistance && !labeledConnectionRouteReason) {
529
579
  return;
530
580
  }
531
581
  if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
@@ -543,11 +593,13 @@ var MspConnectionPairSolver = class extends BaseSolver {
543
593
  }
544
594
  const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
545
595
  const userNetId = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
596
+ const suppressNetLabel = pairDistance > this.maxMspPairDistance && labeledConnectionRouteReason === "overlapping-fallback-labels";
546
597
  this.mspConnectionPairs.push({
547
598
  mspPairId: `${pin1}-${pin2}`,
548
599
  dcConnNetId: dcNetId,
549
600
  globalConnNetId,
550
601
  userNetId,
602
+ suppressNetLabel,
551
603
  pins: [p1, p2]
552
604
  });
553
605
  return;
@@ -626,46 +678,6 @@ import "graphics-debug";
626
678
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
627
679
  import { calculateElbow as calculateElbow2 } from "calculate-elbow";
628
680
 
629
- // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
630
- var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
631
- var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
632
- function getDimsForOrientation(params) {
633
- const { orientation, netLabelWidth, netLabelHeight } = params;
634
- const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
635
- const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
636
- if (orientation === "y+" || orientation === "y-") {
637
- return {
638
- // Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
639
- width: horizHeight,
640
- height: horizWidth
641
- };
642
- }
643
- return {
644
- width: horizWidth,
645
- height: horizHeight
646
- };
647
- }
648
- function getCenterFromAnchor(anchor, orientation, width, height) {
649
- switch (orientation) {
650
- case "x+":
651
- return { x: anchor.x + width / 2, y: anchor.y };
652
- case "x-":
653
- return { x: anchor.x - width / 2, y: anchor.y };
654
- case "y+":
655
- return { x: anchor.x, y: anchor.y + height / 2 };
656
- case "y-":
657
- return { x: anchor.x, y: anchor.y - height / 2 };
658
- }
659
- }
660
- function getRectBounds(center, w, h) {
661
- return {
662
- minX: center.x - w / 2,
663
- minY: center.y - h / 2,
664
- maxX: center.x + w / 2,
665
- maxY: center.y + h / 2
666
- };
667
- }
668
-
669
681
  // lib/utils/textBoxBounds.ts
670
682
  function getTextBoxBounds(textBox, padding = {}) {
671
683
  return {
@@ -3148,6 +3160,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
3148
3160
  (t) => component.has(t.pins[0].pinId) && component.has(t.pins[1].pinId)
3149
3161
  );
3150
3162
  if (compTraces.length > 0) {
3163
+ if (compTraces.some((trace) => trace.suppressNetLabel)) continue;
3151
3164
  const lengthOf = (path) => {
3152
3165
  let sum = 0;
3153
3166
  const pts = path.tracePath;
@@ -10945,10 +10958,10 @@ function isPointOnPath(point, path) {
10945
10958
  }
10946
10959
  function buildMergedObstacleLabel(seedLabel, allLabels) {
10947
10960
  const TOUCH_MARGIN = 0.01;
10948
- const getBounds3 = (l) => getRectBounds(l.center, l.width, l.height);
10961
+ const getBounds4 = (l) => getRectBounds(l.center, l.width, l.height);
10949
10962
  const touches = (a, b) => {
10950
- const ba = getBounds3(a);
10951
- const bb = getBounds3(b);
10963
+ const ba = getBounds4(a);
10964
+ const bb = getBounds4(b);
10952
10965
  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;
10953
10966
  };
10954
10967
  const group = /* @__PURE__ */ new Set([seedLabel]);
@@ -10965,7 +10978,7 @@ function buildMergedObstacleLabel(seedLabel, allLabels) {
10965
10978
  }
10966
10979
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
10967
10980
  for (const l of group) {
10968
- const b = getBounds3(l);
10981
+ const b = getBounds4(l);
10969
10982
  if (b.minX < minX) minX = b.minX;
10970
10983
  if (b.minY < minY) minY = b.minY;
10971
10984
  if (b.maxX > maxX) maxX = b.maxX;
@@ -12776,6 +12789,365 @@ var alignPortOnlyInlineNetLabelStubs = ({
12776
12789
  return alignedPlacements;
12777
12790
  };
12778
12791
 
12792
+ // lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts
12793
+ var LABEL_CLEARANCE2 = 0.05;
12794
+ var POINT_EPSILON = 1e-6;
12795
+ var CONTIGUOUS_LABEL_GAP = 0.01;
12796
+ var MAX_OUTWARD_DISTANCE = 5;
12797
+ var getBounds3 = (placement) => {
12798
+ const renderedWidth = placement.axis === "y" ? placement.height : placement.width;
12799
+ const renderedHeight = placement.axis === "y" ? placement.width : placement.height;
12800
+ return {
12801
+ minX: placement.center.x - renderedWidth / 2,
12802
+ maxX: placement.center.x + renderedWidth / 2,
12803
+ minY: placement.center.y - renderedHeight / 2,
12804
+ maxY: placement.center.y + renderedHeight / 2
12805
+ };
12806
+ };
12807
+ var pointsEqual3 = (a, b) => Math.abs(a.x - b.x) <= POINT_EPSILON && Math.abs(a.y - b.y) <= POINT_EPSILON;
12808
+ var pathIntersectsBounds = (path, bounds) => {
12809
+ for (let index = 0; index < path.length - 1; index++) {
12810
+ const start = path[index];
12811
+ const end = path[index + 1];
12812
+ const segmentBounds = {
12813
+ minX: Math.min(start.x, end.x),
12814
+ maxX: Math.max(start.x, end.x),
12815
+ minY: Math.min(start.y, end.y),
12816
+ maxY: Math.max(start.y, end.y)
12817
+ };
12818
+ if (boundsOverlap(segmentBounds, bounds)) return true;
12819
+ }
12820
+ return false;
12821
+ };
12822
+ var isPointOnPath2 = (point, path) => {
12823
+ for (let index = 0; index < path.length - 1; index++) {
12824
+ const start = path[index];
12825
+ const end = path[index + 1];
12826
+ const minX = Math.min(start.x, end.x) - POINT_EPSILON;
12827
+ const maxX = Math.max(start.x, end.x) + POINT_EPSILON;
12828
+ const minY = Math.min(start.y, end.y) - POINT_EPSILON;
12829
+ const maxY = Math.max(start.y, end.y) + POINT_EPSILON;
12830
+ const isHorizontal4 = Math.abs(start.y - end.y) <= POINT_EPSILON;
12831
+ const isVertical5 = Math.abs(start.x - end.x) <= POINT_EPSILON;
12832
+ if ((isHorizontal4 && Math.abs(point.y - start.y) <= POINT_EPSILON || isVertical5 && Math.abs(point.x - start.x) <= POINT_EPSILON) && point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY) {
12833
+ return true;
12834
+ }
12835
+ }
12836
+ return false;
12837
+ };
12838
+ var getRequiredOutwardDistance = (label, inlineBounds) => {
12839
+ const labelBounds = getBounds3(label);
12840
+ if (label.orientation === "x-" || label.orientation === "x+") {
12841
+ const nearby2 = inlineBounds.filter(
12842
+ (bounds) => labelBounds.minY < bounds.maxY && labelBounds.maxY > bounds.minY
12843
+ );
12844
+ if (nearby2.length === 0) return 0;
12845
+ if (label.orientation === "x-") {
12846
+ const targetMaxX = Math.min(...nearby2.map((bounds) => bounds.minX));
12847
+ return Math.max(0, labelBounds.maxX - targetMaxX + LABEL_CLEARANCE2);
12848
+ }
12849
+ const targetMinX = Math.max(...nearby2.map((bounds) => bounds.maxX));
12850
+ return Math.max(0, targetMinX - labelBounds.minX + LABEL_CLEARANCE2);
12851
+ }
12852
+ const nearby = inlineBounds.filter(
12853
+ (bounds) => labelBounds.minX < bounds.maxX && labelBounds.maxX > bounds.minX
12854
+ );
12855
+ if (nearby.length === 0) return 0;
12856
+ if (label.orientation === "y-") {
12857
+ const targetMaxY = Math.min(...nearby.map((bounds) => bounds.minY));
12858
+ return Math.max(0, labelBounds.maxY - targetMaxY + LABEL_CLEARANCE2);
12859
+ }
12860
+ const targetMinY = Math.max(...nearby.map((bounds) => bounds.maxY));
12861
+ return Math.max(0, targetMinY - labelBounds.minY + LABEL_CLEARANCE2);
12862
+ };
12863
+ var moveLabel = (label, orientation, distance5) => {
12864
+ const direction = dir(orientation);
12865
+ return {
12866
+ ...label,
12867
+ anchorPoint: {
12868
+ x: label.anchorPoint.x + direction.x * distance5,
12869
+ y: label.anchorPoint.y + direction.y * distance5
12870
+ },
12871
+ center: {
12872
+ x: label.center.x + direction.x * distance5,
12873
+ y: label.center.y + direction.y * distance5
12874
+ }
12875
+ };
12876
+ };
12877
+ var getDistanceToShoveBoundsPast = (obstacleBounds, movingBounds, orientation) => {
12878
+ switch (orientation) {
12879
+ case "x-":
12880
+ return obstacleBounds.maxX - movingBounds.minX + LABEL_CLEARANCE2;
12881
+ case "x+":
12882
+ return movingBounds.maxX - obstacleBounds.minX + LABEL_CLEARANCE2;
12883
+ case "y-":
12884
+ return obstacleBounds.maxY - movingBounds.minY + LABEL_CLEARANCE2;
12885
+ case "y+":
12886
+ return movingBounds.maxY - obstacleBounds.minY + LABEL_CLEARANCE2;
12887
+ }
12888
+ };
12889
+ var boundsGapOnPerpendicularAxis = (a, b, orientation) => {
12890
+ if (orientation === "x-" || orientation === "x+") {
12891
+ return Math.max(0, a.minY - b.maxY, b.minY - a.maxY);
12892
+ }
12893
+ return Math.max(0, a.minX - b.maxX, b.minX - a.maxX);
12894
+ };
12895
+ var sharesOwnerChip = (label, ownerChipIds, chipIdByPinId) => label.pinIds.some((pinId) => ownerChipIds.has(chipIdByPinId.get(pinId) ?? ""));
12896
+ var isGeneratedLabelConnector = (trace) => trace.mspPairId.startsWith("available-net-orientation-") || trace.mspPairId.startsWith("inline-net-label-clearance-");
12897
+ var findConnectorTraceIndex = (label, traces) => traces.findIndex((trace) => {
12898
+ if (trace.globalConnNetId !== label.globalConnNetId) return false;
12899
+ if (!isGeneratedLabelConnector(trace)) return false;
12900
+ const first = trace.tracePath[0];
12901
+ const last = trace.tracePath.at(-1);
12902
+ return Boolean(
12903
+ first && pointsEqual3(first, label.anchorPoint) || last && pointsEqual3(last, label.anchorPoint)
12904
+ );
12905
+ });
12906
+ var canAddConnectorAtAnchor = (label, traces, pinMap) => {
12907
+ if (label.pinIds.some((pinId) => {
12908
+ const pin = pinMap[pinId];
12909
+ return pin && pointsEqual3(pin, label.anchorPoint);
12910
+ })) {
12911
+ return true;
12912
+ }
12913
+ return traces.some(
12914
+ (trace) => trace.globalConnNetId === label.globalConnNetId && isPointOnPath2(label.anchorPoint, trace.tracePath)
12915
+ );
12916
+ };
12917
+ var moveConnectorEndpoint = (trace, oldAnchor, newAnchor) => {
12918
+ const tracePath = trace.tracePath.map((point) => ({ ...point }));
12919
+ if (pointsEqual3(tracePath[0], oldAnchor)) tracePath[0] = newAnchor;
12920
+ if (pointsEqual3(tracePath.at(-1), oldAnchor)) {
12921
+ tracePath[tracePath.length - 1] = newAnchor;
12922
+ }
12923
+ return { ...trace, tracePath };
12924
+ };
12925
+ var createConnectorTrace = ({
12926
+ label,
12927
+ labelIndex,
12928
+ newAnchor,
12929
+ pinMap
12930
+ }) => {
12931
+ const mspPairId = `inline-net-label-clearance-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
12932
+ return {
12933
+ mspPairId,
12934
+ dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
12935
+ globalConnNetId: label.globalConnNetId,
12936
+ userNetId: label.netId,
12937
+ pins: getTracePins(label, pinMap),
12938
+ tracePath: [label.anchorPoint, newAnchor],
12939
+ mspConnectionPairIds: [mspPairId],
12940
+ pinIds: label.pinIds
12941
+ };
12942
+ };
12943
+ var getContiguousLabelGroup = ({
12944
+ triggerIndex,
12945
+ labels,
12946
+ chipIdByPinId
12947
+ }) => {
12948
+ const trigger = labels[triggerIndex];
12949
+ const ownerChipIds = new Set(
12950
+ trigger.pinIds.flatMap((pinId) => {
12951
+ const chipId = chipIdByPinId.get(pinId);
12952
+ return chipId ? [chipId] : [];
12953
+ })
12954
+ );
12955
+ const candidates = labels.map((label, labelIndex) => ({ label, labelIndex })).filter(
12956
+ ({ label }) => label.orientation === trigger.orientation && label.mspConnectionPairIds.length === 0 && sharesOwnerChip(label, ownerChipIds, chipIdByPinId)
12957
+ );
12958
+ const group = /* @__PURE__ */ new Set([triggerIndex]);
12959
+ let changed = true;
12960
+ while (changed) {
12961
+ changed = false;
12962
+ for (const { label, labelIndex } of candidates) {
12963
+ if (group.has(labelIndex)) continue;
12964
+ if ([...group].some(
12965
+ (memberIndex) => boundsGapOnPerpendicularAxis(
12966
+ getBounds3(labels[memberIndex]),
12967
+ getBounds3(label),
12968
+ trigger.orientation
12969
+ ) <= CONTIGUOUS_LABEL_GAP
12970
+ )) {
12971
+ group.add(labelIndex);
12972
+ changed = true;
12973
+ }
12974
+ }
12975
+ }
12976
+ return { group, ownerChipIds };
12977
+ };
12978
+ var pushAnchoredNetLabelsAwayFromInlineLabels = ({
12979
+ inputProblem,
12980
+ traces,
12981
+ netLabelPlacements,
12982
+ inlineNetLabelPlacements
12983
+ }) => {
12984
+ const outputTraces = traces.map((trace) => ({
12985
+ ...trace,
12986
+ tracePath: trace.tracePath.map((point) => ({ ...point }))
12987
+ }));
12988
+ const outputLabels = netLabelPlacements.map((label) => ({ ...label }));
12989
+ const inlineBounds = inlineNetLabelPlacements.map(getBounds3);
12990
+ const pinMap = getPinMap(inputProblem);
12991
+ const chipIdByPinId = /* @__PURE__ */ new Map();
12992
+ for (const chip of inputProblem.chips) {
12993
+ for (const pin of chip.pins) chipIdByPinId.set(pin.pinId, chip.chipId);
12994
+ }
12995
+ const movedLabelIndices = /* @__PURE__ */ new Set();
12996
+ for (let triggerIndex = 0; triggerIndex < outputLabels.length; triggerIndex++) {
12997
+ const trigger = outputLabels[triggerIndex];
12998
+ const distance5 = getRequiredOutwardDistance(trigger, inlineBounds);
12999
+ if (distance5 <= POINT_EPSILON || distance5 > MAX_OUTWARD_DISTANCE) continue;
13000
+ const { group, ownerChipIds } = getContiguousLabelGroup({
13001
+ triggerIndex,
13002
+ labels: outputLabels,
13003
+ chipIdByPinId
13004
+ });
13005
+ const distances = new Map(
13006
+ [...group].map((labelIndex) => [labelIndex, distance5])
13007
+ );
13008
+ let failed = false;
13009
+ for (let iteration = 0; iteration < outputLabels.length; iteration++) {
13010
+ let adjustedObstacle = false;
13011
+ for (const [movingIndex, movingDistance] of distances) {
13012
+ const movingBounds = getBounds3(
13013
+ moveLabel(
13014
+ outputLabels[movingIndex],
13015
+ trigger.orientation,
13016
+ movingDistance
13017
+ )
13018
+ );
13019
+ for (let obstacleIndex = 0; obstacleIndex < outputLabels.length; obstacleIndex++) {
13020
+ if (group.has(obstacleIndex)) continue;
13021
+ const obstacle = outputLabels[obstacleIndex];
13022
+ const existingObstacleDistance = distances.get(obstacleIndex) ?? 0;
13023
+ const obstacleBounds = getBounds3(
13024
+ moveLabel(obstacle, trigger.orientation, existingObstacleDistance)
13025
+ );
13026
+ if (!boundsOverlap(movingBounds, obstacleBounds)) continue;
13027
+ if (!sharesOwnerChip(obstacle, ownerChipIds, chipIdByPinId) || findConnectorTraceIndex(obstacle, outputTraces) === -1 && !canAddConnectorAtAnchor(obstacle, outputTraces, pinMap)) {
13028
+ failed = true;
13029
+ break;
13030
+ }
13031
+ const shoveDistance = getDistanceToShoveBoundsPast(
13032
+ getBounds3(obstacle),
13033
+ movingBounds,
13034
+ trigger.orientation
13035
+ );
13036
+ if (shoveDistance > MAX_OUTWARD_DISTANCE || shoveDistance <= existingObstacleDistance + POINT_EPSILON) {
13037
+ failed = true;
13038
+ break;
13039
+ }
13040
+ distances.set(obstacleIndex, shoveDistance);
13041
+ adjustedObstacle = true;
13042
+ }
13043
+ if (failed) break;
13044
+ }
13045
+ if (failed || !adjustedObstacle) break;
13046
+ }
13047
+ if (failed) continue;
13048
+ const proposals = /* @__PURE__ */ new Map();
13049
+ for (const [labelIndex, labelDistance] of distances) {
13050
+ proposals.set(
13051
+ labelIndex,
13052
+ moveLabel(
13053
+ outputLabels[labelIndex],
13054
+ trigger.orientation,
13055
+ labelDistance
13056
+ )
13057
+ );
13058
+ }
13059
+ const finalLabelAt = (labelIndex) => proposals.get(labelIndex) ?? outputLabels[labelIndex];
13060
+ for (const [labelIndex, movedLabel] of proposals) {
13061
+ const movedBounds = getBounds3(movedLabel);
13062
+ if (inlineBounds.some((bounds) => boundsOverlap(movedBounds, bounds))) {
13063
+ failed = true;
13064
+ break;
13065
+ }
13066
+ if (inputProblem.chips.some(
13067
+ (chip) => boundsOverlap(movedBounds, {
13068
+ minX: chip.center.x - chip.width / 2,
13069
+ maxX: chip.center.x + chip.width / 2,
13070
+ minY: chip.center.y - chip.height / 2,
13071
+ maxY: chip.center.y + chip.height / 2
13072
+ })
13073
+ ) || (inputProblem.textBoxes ?? []).some(
13074
+ (textBox) => boundsOverlap(movedBounds, getTextBoxBounds(textBox))
13075
+ )) {
13076
+ failed = true;
13077
+ break;
13078
+ }
13079
+ if (outputLabels.some(
13080
+ (_, otherIndex) => otherIndex !== labelIndex && boundsOverlap(movedBounds, getBounds3(finalLabelAt(otherIndex)))
13081
+ )) {
13082
+ failed = true;
13083
+ break;
13084
+ }
13085
+ if (outputTraces.some(
13086
+ (trace) => trace.globalConnNetId !== movedLabel.globalConnNetId && pathIntersectsBounds(trace.tracePath, movedBounds)
13087
+ )) {
13088
+ failed = true;
13089
+ break;
13090
+ }
13091
+ }
13092
+ if (failed) continue;
13093
+ const connectorUpdates = [];
13094
+ for (const [labelIndex, movedLabel] of proposals) {
13095
+ const label = outputLabels[labelIndex];
13096
+ const connectorIndex = findConnectorTraceIndex(label, outputTraces);
13097
+ if (connectorIndex === -1 && !canAddConnectorAtAnchor(label, outputTraces, pinMap)) {
13098
+ failed = true;
13099
+ break;
13100
+ }
13101
+ const connector = connectorIndex === -1 ? createConnectorTrace({
13102
+ label,
13103
+ labelIndex,
13104
+ newAnchor: movedLabel.anchorPoint,
13105
+ pinMap
13106
+ }) : moveConnectorEndpoint(
13107
+ outputTraces[connectorIndex],
13108
+ label.anchorPoint,
13109
+ movedLabel.anchorPoint
13110
+ );
13111
+ const connectorObstructed = inlineBounds.some(
13112
+ (bounds) => pathIntersectsBounds(connector.tracePath, bounds)
13113
+ ) || inputProblem.chips.some(
13114
+ (chip) => pathIntersectsBounds(connector.tracePath, {
13115
+ minX: chip.center.x - chip.width / 2,
13116
+ maxX: chip.center.x + chip.width / 2,
13117
+ minY: chip.center.y - chip.height / 2,
13118
+ maxY: chip.center.y + chip.height / 2
13119
+ })
13120
+ ) || (inputProblem.textBoxes ?? []).some(
13121
+ (textBox) => pathIntersectsBounds(connector.tracePath, getTextBoxBounds(textBox))
13122
+ ) || outputLabels.some(
13123
+ (_, otherIndex) => otherIndex !== labelIndex && pathIntersectsBounds(
13124
+ connector.tracePath,
13125
+ getBounds3(finalLabelAt(otherIndex))
13126
+ )
13127
+ );
13128
+ if (connectorObstructed) {
13129
+ failed = true;
13130
+ break;
13131
+ }
13132
+ connectorUpdates.push({ labelIndex, connectorIndex, trace: connector });
13133
+ }
13134
+ if (failed) continue;
13135
+ for (const [labelIndex, movedLabel] of proposals) {
13136
+ outputLabels[labelIndex] = movedLabel;
13137
+ movedLabelIndices.add(labelIndex);
13138
+ }
13139
+ for (const update of connectorUpdates) {
13140
+ if (update.connectorIndex === -1) outputTraces.push(update.trace);
13141
+ else outputTraces[update.connectorIndex] = update.trace;
13142
+ }
13143
+ }
13144
+ return {
13145
+ traces: outputTraces,
13146
+ netLabelPlacements: outputLabels,
13147
+ movedLabelCount: movedLabelIndices.size
13148
+ };
13149
+ };
13150
+
12779
13151
  // lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
12780
13152
  var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
12781
13153
  var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
@@ -12790,6 +13162,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
12790
13162
  queuedConnections;
12791
13163
  tracesByPinPairKey;
12792
13164
  hasAlignedPortOnlyStubs = false;
13165
+ postProcessedOutput;
12793
13166
  constructor(input) {
12794
13167
  super();
12795
13168
  this.inputProblem = input.inputProblem;
@@ -12850,6 +13223,10 @@ var InlineNetLabelSolver = class extends BaseSolver {
12850
13223
  this.hasAlignedPortOnlyStubs = true;
12851
13224
  return;
12852
13225
  }
13226
+ if (!this.postProcessedOutput) {
13227
+ this.postProcessedOutput = this.buildPostProcessedOutput();
13228
+ return;
13229
+ }
12853
13230
  this.solved = true;
12854
13231
  this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
12855
13232
  }
@@ -13096,19 +13473,29 @@ var InlineNetLabelSolver = class extends BaseSolver {
13096
13473
  (trace) => !(supersededNetLabelKeys.has(trace.globalConnNetId) && trace.mspPairId.startsWith("available-net-orientation-"))
13097
13474
  );
13098
13475
  }
13099
- getOutput() {
13476
+ buildPostProcessedOutput() {
13100
13477
  const superseded = this.getSupersededNetLabelKeys();
13478
+ const retainedNetLabelPlacements = this.inputNetLabelPlacements.filter(
13479
+ (placement) => !superseded.has(placement.globalConnNetId)
13480
+ );
13481
+ const outputTraces = this.getOutputTraces(superseded);
13482
+ const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
13483
+ inputProblem: this.inputProblem,
13484
+ traces: outputTraces,
13485
+ netLabelPlacements: retainedNetLabelPlacements,
13486
+ inlineNetLabelPlacements: this.inlineNetLabelPlacements
13487
+ });
13488
+ this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount;
13101
13489
  return {
13102
- // AvailableNetOrientationSolver may have routed an elbow from a port to
13103
- // the anchored label that this inline placement supersedes. Keep the
13104
- // actual net trace, but discard that now-orphaned label connector.
13105
- traces: this.getOutputTraces(superseded),
13106
- netLabelPlacements: this.inputNetLabelPlacements.filter(
13107
- (placement) => !superseded.has(placement.globalConnNetId)
13108
- ),
13490
+ traces: pushed.traces,
13491
+ netLabelPlacements: pushed.netLabelPlacements,
13109
13492
  inlineNetLabelPlacements: this.inlineNetLabelPlacements
13110
13493
  };
13111
13494
  }
13495
+ getOutput() {
13496
+ if (this.postProcessedOutput) return this.postProcessedOutput;
13497
+ return this.buildPostProcessedOutput();
13498
+ }
13112
13499
  visualize() {
13113
13500
  const graphics = visualizeInputProblem(this.inputProblem);
13114
13501
  graphics.lines ??= [];