@tscircuit/schematic-trace-solver 0.0.49 → 0.0.50

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.js CHANGED
@@ -271,19 +271,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
271
271
  chipMap
272
272
  });
273
273
  if (restrictedCenterLines.size === 0) return false;
274
- const EPS6 = 1e-9;
274
+ const EPS7 = 1e-9;
275
275
  const crossesSegment = (a, b) => {
276
276
  for (const [, rcl] of restrictedCenterLines) {
277
277
  if (rcl.axes.has("x") && typeof rcl.x === "number") {
278
- if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS6) return true;
278
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS7) return true;
279
279
  }
280
280
  if (rcl.axes.has("y") && typeof rcl.y === "number") {
281
- if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS6) return true;
281
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS7) return true;
282
282
  }
283
283
  }
284
284
  return false;
285
285
  };
286
- if (Math.abs(p1.x - p2.x) < EPS6 || Math.abs(p1.y - p2.y) < EPS6) {
286
+ if (Math.abs(p1.x - p2.x) < EPS7 || Math.abs(p1.y - p2.y) < EPS7) {
287
287
  return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
288
288
  }
289
289
  const elbowHV = { x: p2.x, y: p1.y };
@@ -810,8 +810,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
810
810
  if (!collision) {
811
811
  const first = path[0];
812
812
  const last = path[path.length - 1];
813
- const EPS6 = 1e-9;
814
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS6 && Math.abs(p.y - q.y) < EPS6;
813
+ const EPS7 = 1e-9;
814
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS7 && Math.abs(p.y - q.y) < EPS7;
815
815
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
816
816
  this.solvedTracePath = path;
817
817
  this.solved = true;
@@ -1010,13 +1010,13 @@ var applyJogToTerminalSegment = ({
1010
1010
  segmentIndex: si,
1011
1011
  offset,
1012
1012
  JOG_SIZE,
1013
- EPS: EPS6 = 1e-6
1013
+ EPS: EPS7 = 1e-6
1014
1014
  }) => {
1015
1015
  if (si !== 0 && si !== pts.length - 2) return;
1016
1016
  const start = pts[si];
1017
1017
  const end = pts[si + 1];
1018
- const isVertical3 = Math.abs(start.x - end.x) < EPS6;
1019
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS6;
1018
+ const isVertical3 = Math.abs(start.x - end.x) < EPS7;
1019
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS7;
1020
1020
  if (!isVertical3 && !isHorizontal2) return;
1021
1021
  const segDir = isVertical3 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1022
1022
  if (si === 0) {
@@ -1083,13 +1083,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1083
1083
  }
1084
1084
  }
1085
1085
  _step() {
1086
- const EPS6 = 1e-6;
1086
+ const EPS7 = 1e-6;
1087
1087
  const offsets = this.overlappingTraceSegments.map((_, idx) => {
1088
1088
  const n = Math.floor(idx / 2) + 1;
1089
1089
  const signed = idx % 2 === 0 ? -n : n;
1090
1090
  return signed * this.SHIFT_DISTANCE;
1091
1091
  });
1092
- const eq = (a, b) => Math.abs(a - b) < EPS6;
1092
+ const eq = (a, b) => Math.abs(a - b) < EPS7;
1093
1093
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1094
1094
  this.overlappingTraceSegments.forEach((group, gidx) => {
1095
1095
  const offset = offsets[gidx];
@@ -1115,13 +1115,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1115
1115
  segmentIndex: si,
1116
1116
  offset,
1117
1117
  JOG_SIZE,
1118
- EPS: EPS6
1118
+ EPS: EPS7
1119
1119
  });
1120
1120
  } else {
1121
1121
  const start = pts[si];
1122
1122
  const end = pts[si + 1];
1123
- const isVertical3 = Math.abs(start.x - end.x) < EPS6;
1124
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS6;
1123
+ const isVertical3 = Math.abs(start.x - end.x) < EPS7;
1124
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS7;
1125
1125
  if (!isVertical3 && !isHorizontal2) continue;
1126
1126
  if (isVertical3) {
1127
1127
  start.x += offset;
@@ -1218,7 +1218,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1218
1218
  return islands;
1219
1219
  }
1220
1220
  findNextOverlapIssue() {
1221
- const EPS6 = 2e-3;
1221
+ const EPS7 = 2e-3;
1222
1222
  const netIds = Object.keys(this.traceNetIslands);
1223
1223
  for (let i = 0; i < netIds.length; i++) {
1224
1224
  for (let j = i + 1; j < netIds.length; j++) {
@@ -1236,7 +1236,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1236
1236
  const minB = Math.min(b1, b2);
1237
1237
  const maxB = Math.max(b1, b2);
1238
1238
  const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
1239
- return overlap > EPS6;
1239
+ return overlap > EPS7;
1240
1240
  };
1241
1241
  for (let pa = 0; pa < pathsA.length; pa++) {
1242
1242
  const pathA = pathsA[pa];
@@ -1244,8 +1244,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1244
1244
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
1245
1245
  const a1 = ptsA[sa];
1246
1246
  const a2 = ptsA[sa + 1];
1247
- const aVert = Math.abs(a1.x - a2.x) < EPS6;
1248
- const aHorz = Math.abs(a1.y - a2.y) < EPS6;
1247
+ const aVert = Math.abs(a1.x - a2.x) < EPS7;
1248
+ const aHorz = Math.abs(a1.y - a2.y) < EPS7;
1249
1249
  if (!aVert && !aHorz) continue;
1250
1250
  for (let pb = 0; pb < pathsB.length; pb++) {
1251
1251
  const pathB = pathsB[pb];
@@ -1253,11 +1253,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1253
1253
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
1254
1254
  const b1 = ptsB[sb];
1255
1255
  const b2 = ptsB[sb + 1];
1256
- const bVert = Math.abs(b1.x - b2.x) < EPS6;
1257
- const bHorz = Math.abs(b1.y - b2.y) < EPS6;
1256
+ const bVert = Math.abs(b1.x - b2.x) < EPS7;
1257
+ const bHorz = Math.abs(b1.y - b2.y) < EPS7;
1258
1258
  if (!bVert && !bHorz) continue;
1259
1259
  if (aVert && bVert) {
1260
- if (Math.abs(a1.x - b1.x) < EPS6) {
1260
+ if (Math.abs(a1.x - b1.x) < EPS7) {
1261
1261
  if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
1262
1262
  const keyA = `${pa}:${sa}`;
1263
1263
  const keyB = `${pb}:${sb}`;
@@ -1278,7 +1278,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1278
1278
  }
1279
1279
  }
1280
1280
  } else if (aHorz && bHorz) {
1281
- if (Math.abs(a1.y - b1.y) < EPS6) {
1281
+ if (Math.abs(a1.y - b1.y) < EPS7) {
1282
1282
  if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
1283
1283
  const keyA = `${pa}:${sa}`;
1284
1284
  const keyB = `${pb}:${sb}`;
@@ -1316,14 +1316,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1316
1316
  return null;
1317
1317
  }
1318
1318
  findNextDiagonalSegment() {
1319
- const EPS6 = 2e-3;
1319
+ const EPS7 = 2e-3;
1320
1320
  for (const mspPairId in this.correctedTraceMap) {
1321
1321
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
1322
1322
  for (let i = 0; i < tracePath.length - 1; i++) {
1323
1323
  const p1 = tracePath[i];
1324
1324
  const p2 = tracePath[i + 1];
1325
- const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS6;
1326
- const isVertical3 = Math.abs(p1.x - p2.x) < EPS6;
1325
+ const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS7;
1326
+ const isVertical3 = Math.abs(p1.x - p2.x) < EPS7;
1327
1327
  if (!isHorizontal2 && !isVertical3) {
1328
1328
  return { mspPairId, tracePath, i, p1, p2 };
1329
1329
  }
@@ -1337,13 +1337,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1337
1337
  return false;
1338
1338
  }
1339
1339
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
1340
- const EPS6 = 2e-3;
1340
+ const EPS7 = 2e-3;
1341
1341
  const p0 = i > 0 ? tracePath[i - 1] : null;
1342
1342
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
1343
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS6 : false;
1344
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS6 : false;
1345
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS6 : false;
1346
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS6 : false;
1343
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS7 : false;
1344
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS7 : false;
1345
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS7 : false;
1346
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS7 : false;
1347
1347
  const elbow1 = { x: p1.x, y: p2.y };
1348
1348
  const elbow2 = { x: p2.x, y: p1.y };
1349
1349
  let score1 = 0;
@@ -1507,24 +1507,24 @@ function getRectBounds(center, w, h) {
1507
1507
  }
1508
1508
 
1509
1509
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
1510
- function segmentIntersectsRect2(p1, p2, rect, EPS6 = 1e-9) {
1511
- const isVert = Math.abs(p1.x - p2.x) < EPS6;
1512
- const isHorz = Math.abs(p1.y - p2.y) < EPS6;
1510
+ function segmentIntersectsRect2(p1, p2, rect, EPS7 = 1e-9) {
1511
+ const isVert = Math.abs(p1.x - p2.x) < EPS7;
1512
+ const isHorz = Math.abs(p1.y - p2.y) < EPS7;
1513
1513
  if (!isVert && !isHorz) return false;
1514
1514
  if (isVert) {
1515
1515
  const x = p1.x;
1516
- if (x < rect.minX - EPS6 || x > rect.maxX + EPS6) return false;
1516
+ if (x < rect.minX - EPS7 || x > rect.maxX + EPS7) return false;
1517
1517
  const segMinY = Math.min(p1.y, p2.y);
1518
1518
  const segMaxY = Math.max(p1.y, p2.y);
1519
1519
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
1520
- return overlap > EPS6;
1520
+ return overlap > EPS7;
1521
1521
  } else {
1522
1522
  const y = p1.y;
1523
- if (y < rect.minY - EPS6 || y > rect.maxY + EPS6) return false;
1523
+ if (y < rect.minY - EPS7 || y > rect.maxY + EPS7) return false;
1524
1524
  const segMinX = Math.min(p1.x, p2.x);
1525
1525
  const segMaxX = Math.max(p1.x, p2.x);
1526
1526
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
1527
- return overlap > EPS6;
1527
+ return overlap > EPS7;
1528
1528
  }
1529
1529
  }
1530
1530
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -1902,14 +1902,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1902
1902
  };
1903
1903
  let bestCandidate = null;
1904
1904
  let bestScore = -Infinity;
1905
- const EPS6 = 1e-6;
1905
+ const EPS7 = 1e-6;
1906
1906
  for (const curr of tracesToScan) {
1907
1907
  const pts = curr.tracePath.slice();
1908
1908
  for (let si = 0; si < pts.length - 1; si++) {
1909
1909
  const a = pts[si];
1910
1910
  const b = pts[si + 1];
1911
- const isH = Math.abs(a.y - b.y) < EPS6;
1912
- const isV = Math.abs(a.x - b.x) < EPS6;
1911
+ const isH = Math.abs(a.y - b.y) < EPS7;
1912
+ const isV = Math.abs(a.x - b.x) < EPS7;
1913
1913
  if (!isH && !isV) continue;
1914
1914
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
1915
1915
  const candidateOrients = orientations.filter(
@@ -5345,6 +5345,715 @@ var Example28Solver = class extends BaseSolver {
5345
5345
  }
5346
5346
  };
5347
5347
 
5348
+ // lib/solvers/AvailableNetOrientationSolver/constants.ts
5349
+ var LABEL_SEARCH_STEP = 0.05;
5350
+ var WICK_CLEARANCE = 1e-3;
5351
+ var EPS6 = 1e-9;
5352
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS6;
5353
+ var CANDIDATE_SELECTED_COLOR2 = "blue";
5354
+ var CANDIDATE_REJECTED_COLOR2 = "red";
5355
+
5356
+ // lib/solvers/AvailableNetOrientationSolver/geometry.ts
5357
+ var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
5358
+ var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
5359
+ var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS6 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS6;
5360
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS6;
5361
+ var traceCrossesBoundsInterior = (bounds, traceMap) => {
5362
+ for (const trace of Object.values(traceMap)) {
5363
+ const points = trace.tracePath;
5364
+ for (let i = 0; i < points.length - 1; i++) {
5365
+ if (segmentCrossesBoundsInterior(points[i], points[i + 1], bounds)) {
5366
+ return true;
5367
+ }
5368
+ }
5369
+ }
5370
+ return false;
5371
+ };
5372
+ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
5373
+ const interiorBounds = {
5374
+ minX: bounds.minX + TRACE_BOUNDARY_TOLERANCE,
5375
+ minY: bounds.minY + TRACE_BOUNDARY_TOLERANCE,
5376
+ maxX: bounds.maxX - TRACE_BOUNDARY_TOLERANCE,
5377
+ maxY: bounds.maxY - TRACE_BOUNDARY_TOLERANCE
5378
+ };
5379
+ if (interiorBounds.minX >= interiorBounds.maxX || interiorBounds.minY >= interiorBounds.maxY) {
5380
+ return false;
5381
+ }
5382
+ if (sameX(p1, p2)) {
5383
+ if (p1.x <= interiorBounds.minX + EPS6 || p1.x >= interiorBounds.maxX - EPS6) {
5384
+ return false;
5385
+ }
5386
+ return rangesOverlap(
5387
+ Math.min(p1.y, p2.y),
5388
+ Math.max(p1.y, p2.y),
5389
+ interiorBounds.minY,
5390
+ interiorBounds.maxY
5391
+ );
5392
+ }
5393
+ if (sameY(p1, p2)) {
5394
+ if (p1.y <= interiorBounds.minY + EPS6 || p1.y >= interiorBounds.maxY - EPS6) {
5395
+ return false;
5396
+ }
5397
+ return rangesOverlap(
5398
+ Math.min(p1.x, p2.x),
5399
+ Math.max(p1.x, p2.x),
5400
+ interiorBounds.minX,
5401
+ interiorBounds.maxX
5402
+ );
5403
+ }
5404
+ return false;
5405
+ };
5406
+ var tracePathCrossesAnyTrace = (tracePath, traceMap) => {
5407
+ for (const trace of Object.values(traceMap)) {
5408
+ const points = trace.tracePath;
5409
+ for (let i = 0; i < tracePath.length - 1; i++) {
5410
+ for (let j = 0; j < points.length - 1; j++) {
5411
+ if (segmentsStrictlyCross(
5412
+ tracePath[i],
5413
+ tracePath[i + 1],
5414
+ points[j],
5415
+ points[j + 1]
5416
+ )) {
5417
+ return true;
5418
+ }
5419
+ }
5420
+ }
5421
+ }
5422
+ return false;
5423
+ };
5424
+ var segmentsStrictlyCross = (a1, a2, b1, b2) => {
5425
+ if (sameX(a1, a2) && sameY(b1, b2)) {
5426
+ return a1.x > Math.min(b1.x, b2.x) + EPS6 && a1.x < Math.max(b1.x, b2.x) - EPS6 && b1.y > Math.min(a1.y, a2.y) + EPS6 && b1.y < Math.max(a1.y, a2.y) - EPS6;
5427
+ }
5428
+ if (sameY(a1, a2) && sameX(b1, b2)) {
5429
+ return b1.x > Math.min(a1.x, a2.x) + EPS6 && b1.x < Math.max(a1.x, a2.x) - EPS6 && a1.y > Math.min(b1.y, b2.y) + EPS6 && a1.y < Math.max(b1.y, b2.y) - EPS6;
5430
+ }
5431
+ return false;
5432
+ };
5433
+ var getSideDistances = (point, bounds) => [
5434
+ ["left", Math.abs(point.x - bounds.minX)],
5435
+ ["right", Math.abs(point.x - bounds.maxX)],
5436
+ ["bottom", Math.abs(point.y - bounds.minY)],
5437
+ ["top", Math.abs(point.y - bounds.maxY)]
5438
+ ];
5439
+ var getConnectorTracePath = (source, target, orientation) => simplifyOrthogonalPath(
5440
+ isYOrientation(orientation) ? [source, { x: target.x, y: source.y }, target] : [source, { x: source.x, y: target.y }, target]
5441
+ );
5442
+ var simplifyOrthogonalPath = (path) => {
5443
+ const deduped = path.filter(
5444
+ (point, index) => index === 0 || !pointsEqual(point, path[index - 1])
5445
+ );
5446
+ if (deduped.length < 3) return deduped;
5447
+ const simplified = [deduped[0]];
5448
+ for (let i = 1; i < deduped.length - 1; i++) {
5449
+ const prev = simplified[simplified.length - 1];
5450
+ const point = deduped[i];
5451
+ const next = deduped[i + 1];
5452
+ if (sameX(prev, point) && sameX(point, next) || sameY(prev, point) && sameY(point, next)) {
5453
+ continue;
5454
+ }
5455
+ simplified.push(point);
5456
+ }
5457
+ simplified.push(deduped[deduped.length - 1]);
5458
+ return simplified;
5459
+ };
5460
+ var pointsEqual = (a, b) => sameX(a, b) && sameY(a, b);
5461
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS6;
5462
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS6;
5463
+ var getMaxSearchDistance = (inputProblem) => {
5464
+ const maxChipWidth = Math.max(
5465
+ ...inputProblem.chips.map((chip) => chip.width),
5466
+ 1
5467
+ );
5468
+ return maxChipWidth * 3;
5469
+ };
5470
+
5471
+ // lib/solvers/AvailableNetOrientationSolver/traces.ts
5472
+ var getPinMap = (inputProblem) => {
5473
+ const pinMap = {};
5474
+ for (const chip of inputProblem.chips) {
5475
+ for (const pin of chip.pins) {
5476
+ pinMap[pin.pinId] = { ...pin, chipId: chip.chipId };
5477
+ }
5478
+ }
5479
+ return pinMap;
5480
+ };
5481
+ var getTracePins = (label, pinMap) => {
5482
+ const pins = label.pinIds.flatMap((pinId) => {
5483
+ const pin = pinMap[pinId];
5484
+ return pin ? [pin] : [];
5485
+ });
5486
+ if (pins.length >= 2) return [pins[0], pins[1]];
5487
+ if (pins.length === 1) return [pins[0], pins[0]];
5488
+ const syntheticPin = {
5489
+ pinId: `${label.globalConnNetId}-netlabel-anchor`,
5490
+ x: label.anchorPoint.x,
5491
+ y: label.anchorPoint.y,
5492
+ chipId: "available-net-orientation"
5493
+ };
5494
+ return [syntheticPin, syntheticPin];
5495
+ };
5496
+ var toNetLabelPlacementPatch = (candidate) => ({
5497
+ orientation: candidate.orientation,
5498
+ anchorPoint: candidate.anchorPoint,
5499
+ center: candidate.center,
5500
+ width: candidate.width,
5501
+ height: candidate.height
5502
+ });
5503
+
5504
+ // lib/solvers/AvailableNetOrientationSolver/visualize.ts
5505
+ var visualizeAvailableNetOrientationSolver = (params) => {
5506
+ const graphics = visualizeInputProblem(params.inputProblem);
5507
+ ensureGraphicsArrays(graphics);
5508
+ drawTraces2(graphics, params.traces);
5509
+ drawNetLabels2(graphics, params.outputNetLabelPlacements);
5510
+ drawCurrentLabel(graphics, params.currentLabel, params.solved);
5511
+ drawCurrentCandidates2(graphics, params.currentCandidateResults, params.solved);
5512
+ return graphics;
5513
+ };
5514
+ var drawTraces2 = (graphics, traces) => {
5515
+ for (const trace of traces) {
5516
+ graphics.lines.push({
5517
+ points: trace.tracePath,
5518
+ strokeColor: "purple"
5519
+ });
5520
+ }
5521
+ };
5522
+ var drawNetLabels2 = (graphics, labels) => {
5523
+ for (const label of labels) {
5524
+ graphics.rects.push({
5525
+ center: label.center,
5526
+ width: label.width,
5527
+ height: label.height,
5528
+ fill: getColorFromString(label.globalConnNetId, 0.35),
5529
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
5530
+ label: `netId: ${label.netId}
5531
+ globalConnNetId: ${label.globalConnNetId}`
5532
+ });
5533
+ graphics.points.push({
5534
+ x: label.anchorPoint.x,
5535
+ y: label.anchorPoint.y,
5536
+ color: getColorFromString(label.globalConnNetId, 0.9),
5537
+ label: `anchorPoint
5538
+ orientation: ${label.orientation}`
5539
+ });
5540
+ }
5541
+ };
5542
+ var drawCurrentLabel = (graphics, currentLabel, solved) => {
5543
+ if (!currentLabel || solved) return;
5544
+ graphics.rects.push({
5545
+ center: currentLabel.center,
5546
+ width: currentLabel.width,
5547
+ height: currentLabel.height,
5548
+ fill: "rgba(255, 0, 0, 0.2)",
5549
+ strokeColor: CANDIDATE_REJECTED_COLOR2,
5550
+ label: `available orientation target
5551
+ ${currentLabel.netId ?? currentLabel.globalConnNetId}`
5552
+ });
5553
+ };
5554
+ var drawCurrentCandidates2 = (graphics, candidates, solved) => {
5555
+ if (solved) return;
5556
+ for (const candidate of candidates) {
5557
+ const color = candidate.selected ? CANDIDATE_SELECTED_COLOR2 : CANDIDATE_REJECTED_COLOR2;
5558
+ const distanceLabel = candidate.distance === void 0 ? "" : `
5559
+ distance: ${candidate.distance.toFixed(3)}`;
5560
+ const outwardDistanceLabel = candidate.outwardDistance === void 0 || candidate.outwardDistance === 0 ? "" : `
5561
+ outward distance: ${candidate.outwardDistance.toFixed(3)}`;
5562
+ graphics.rects.push({
5563
+ center: candidate.center,
5564
+ width: candidate.width,
5565
+ height: candidate.height,
5566
+ fill: candidate.selected ? "rgba(0, 0, 255, 0.2)" : "rgba(255, 0, 0, 0.15)",
5567
+ strokeColor: color,
5568
+ strokeDash: candidate.selected ? void 0 : "4 2",
5569
+ label: `${candidate.selected ? "selected" : candidate.status} available orientation
5570
+ phase: ${candidate.phase}
5571
+ orientation: ${candidate.orientation}${distanceLabel}${outwardDistanceLabel}`
5572
+ });
5573
+ graphics.points.push({
5574
+ x: candidate.anchorPoint.x,
5575
+ y: candidate.anchorPoint.y,
5576
+ color,
5577
+ label: `candidate anchor
5578
+ ${candidate.phase}
5579
+ ${candidate.orientation}`
5580
+ });
5581
+ }
5582
+ };
5583
+ var ensureGraphicsArrays = (graphics) => {
5584
+ if (!graphics.lines) graphics.lines = [];
5585
+ if (!graphics.points) graphics.points = [];
5586
+ if (!graphics.rects) graphics.rects = [];
5587
+ if (!graphics.circles) graphics.circles = [];
5588
+ if (!graphics.texts) graphics.texts = [];
5589
+ };
5590
+
5591
+ // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
5592
+ var AvailableNetOrientationSolver = class extends BaseSolver {
5593
+ inputProblem;
5594
+ traces;
5595
+ netLabelPlacements;
5596
+ outputNetLabelPlacements;
5597
+ queuedLabelIndices = [];
5598
+ currentLabelIndex = null;
5599
+ currentLabel = null;
5600
+ currentCandidateResults = [];
5601
+ traceMap;
5602
+ chipObstacleSpatialIndex;
5603
+ maxSearchDistance;
5604
+ pinMap;
5605
+ constructor(params) {
5606
+ super();
5607
+ this.inputProblem = params.inputProblem;
5608
+ this.traces = [...params.traces];
5609
+ this.netLabelPlacements = params.netLabelPlacements;
5610
+ this.outputNetLabelPlacements = [...params.netLabelPlacements];
5611
+ this.traceMap = Object.fromEntries(
5612
+ this.traces.map((trace) => [trace.mspPairId, trace])
5613
+ );
5614
+ this.pinMap = getPinMap(params.inputProblem);
5615
+ this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
5616
+ this.maxSearchDistance = getMaxSearchDistance(params.inputProblem);
5617
+ this.queuedLabelIndices = this.getProcessableLabelIndices();
5618
+ this.setCurrentLabel(this.queuedLabelIndices[0] ?? null);
5619
+ }
5620
+ getConstructorParams() {
5621
+ return {
5622
+ inputProblem: this.inputProblem,
5623
+ traces: this.traces,
5624
+ netLabelPlacements: this.netLabelPlacements
5625
+ };
5626
+ }
5627
+ _step() {
5628
+ const labelIndex = this.queuedLabelIndices.shift();
5629
+ if (labelIndex === void 0) {
5630
+ this.finish();
5631
+ return;
5632
+ }
5633
+ this.processLabel(labelIndex);
5634
+ }
5635
+ getOutput() {
5636
+ return {
5637
+ traces: this.traces,
5638
+ netLabelPlacements: this.outputNetLabelPlacements
5639
+ };
5640
+ }
5641
+ getProcessableLabelIndices() {
5642
+ const indices = [];
5643
+ for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
5644
+ if (this.shouldProcessLabel(this.outputNetLabelPlacements[i], i)) {
5645
+ indices.push(i);
5646
+ }
5647
+ }
5648
+ return indices;
5649
+ }
5650
+ shouldProcessLabel(label, labelIndex) {
5651
+ const orientations = this.getAvailableOrientations(label);
5652
+ if (orientations.length === 0) return false;
5653
+ return !(orientations.includes(label.orientation) && this.isLabelPlacementValid(label, labelIndex));
5654
+ }
5655
+ processLabel(labelIndex) {
5656
+ const label = this.outputNetLabelPlacements[labelIndex];
5657
+ this.setCurrentLabel(labelIndex);
5658
+ this.currentCandidateResults = [];
5659
+ const candidate = this.findCorrectedCandidate(label, labelIndex);
5660
+ if (!candidate) return;
5661
+ this.applyCandidate(label, candidate, labelIndex);
5662
+ }
5663
+ applyCandidate(label, candidate, labelIndex) {
5664
+ this.outputNetLabelPlacements[labelIndex] = {
5665
+ ...label,
5666
+ ...toNetLabelPlacementPatch(candidate)
5667
+ };
5668
+ this.addConnectorTrace(label, candidate, labelIndex);
5669
+ }
5670
+ addConnectorTrace(label, candidate, labelIndex) {
5671
+ const tracePath = getConnectorTracePath(
5672
+ label.anchorPoint,
5673
+ candidate.anchorPoint,
5674
+ candidate.orientation
5675
+ );
5676
+ if (tracePath.length < 2) return;
5677
+ const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
5678
+ const connectorTrace = {
5679
+ mspPairId,
5680
+ dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
5681
+ globalConnNetId: label.globalConnNetId,
5682
+ userNetId: label.netId,
5683
+ pins: getTracePins(label, this.pinMap),
5684
+ tracePath,
5685
+ mspConnectionPairIds: [mspPairId],
5686
+ pinIds: label.pinIds
5687
+ };
5688
+ this.traces.push(connectorTrace);
5689
+ this.traceMap[mspPairId] = connectorTrace;
5690
+ }
5691
+ finish() {
5692
+ this.currentLabelIndex = null;
5693
+ this.currentLabel = null;
5694
+ this.currentCandidateResults = [];
5695
+ this.solved = true;
5696
+ }
5697
+ setCurrentLabel(labelIndex) {
5698
+ this.currentLabelIndex = labelIndex;
5699
+ this.currentLabel = labelIndex === null ? null : this.outputNetLabelPlacements[labelIndex];
5700
+ }
5701
+ findCorrectedCandidate(label, labelIndex) {
5702
+ const orientations = this.getAvailableOrientations(label);
5703
+ const rotatedCandidate = this.findValidRotatedCandidate(
5704
+ label,
5705
+ labelIndex,
5706
+ orientations
5707
+ );
5708
+ return rotatedCandidate ?? this.findValidShiftedCandidate(label, orientations[0], labelIndex);
5709
+ }
5710
+ findValidRotatedCandidate(label, labelIndex, orientations) {
5711
+ for (const orientation of orientations) {
5712
+ const candidate = this.createCandidate(
5713
+ label,
5714
+ this.getSearchStartAnchor(label, orientation),
5715
+ orientation
5716
+ );
5717
+ const result = this.evaluateCandidate(
5718
+ candidate,
5719
+ label,
5720
+ labelIndex,
5721
+ "rotate"
5722
+ );
5723
+ this.currentCandidateResults.push(result);
5724
+ if (result.status === "valid") {
5725
+ result.selected = true;
5726
+ return result;
5727
+ }
5728
+ }
5729
+ return null;
5730
+ }
5731
+ getAvailableOrientations(label) {
5732
+ const effectiveNetId = label.netId ?? label.globalConnNetId;
5733
+ return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
5734
+ }
5735
+ findValidShiftedCandidate(label, orientation, labelIndex) {
5736
+ const direction = dir(orientation);
5737
+ const initialBaseAnchor = this.getSearchStartAnchor(label, orientation);
5738
+ const outwardDirection = this.getPerpendicularOutwardDirection(
5739
+ label.anchorPoint,
5740
+ orientation
5741
+ );
5742
+ const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
5743
+ const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
5744
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS6; outwardDistance += LABEL_SEARCH_STEP) {
5745
+ const baseAnchor = {
5746
+ x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
5747
+ y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
5748
+ };
5749
+ const candidate = this.findValidCandidateInShiftColumn({
5750
+ label,
5751
+ labelIndex,
5752
+ orientation,
5753
+ direction,
5754
+ baseAnchor,
5755
+ maxSearchDistance,
5756
+ outwardDistance
5757
+ });
5758
+ if (candidate) return candidate;
5759
+ }
5760
+ return null;
5761
+ }
5762
+ findValidCandidateInShiftColumn(params) {
5763
+ const {
5764
+ label,
5765
+ labelIndex,
5766
+ orientation,
5767
+ direction,
5768
+ baseAnchor,
5769
+ maxSearchDistance,
5770
+ outwardDistance
5771
+ } = params;
5772
+ for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS6; distance3 += LABEL_SEARCH_STEP) {
5773
+ const anchorPoint = {
5774
+ x: baseAnchor.x + direction.x * distance3,
5775
+ y: baseAnchor.y + direction.y * distance3
5776
+ };
5777
+ const candidate = this.createCandidate(label, anchorPoint, orientation);
5778
+ const result = this.evaluateCandidate(
5779
+ candidate,
5780
+ label,
5781
+ labelIndex,
5782
+ "shift",
5783
+ distance3,
5784
+ outwardDistance
5785
+ );
5786
+ this.currentCandidateResults.push(result);
5787
+ if (result.status === "valid") {
5788
+ result.selected = true;
5789
+ return result;
5790
+ }
5791
+ if (result.status === "trace-collision") break;
5792
+ }
5793
+ return null;
5794
+ }
5795
+ evaluateCandidate(candidate, label, labelIndex, phase, distance3, outwardDistance) {
5796
+ return {
5797
+ ...candidate,
5798
+ phase,
5799
+ distance: distance3,
5800
+ outwardDistance,
5801
+ selected: false,
5802
+ status: this.getCandidateStatus(candidate, label, labelIndex)
5803
+ };
5804
+ }
5805
+ getSearchStartAnchor(label, orientation) {
5806
+ const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
5807
+ const { width, height } = getDimsForOrientation({
5808
+ orientation,
5809
+ netLabelWidth: this.getNetLabelWidth(label)
5810
+ });
5811
+ return this.getSideOffsetAnchor({
5812
+ anchorPoint,
5813
+ labelAnchorPoint: label.anchorPoint,
5814
+ orientation,
5815
+ width,
5816
+ height
5817
+ });
5818
+ }
5819
+ getWickOffsetAnchor(anchorPoint, orientation) {
5820
+ const direction = dir(orientation);
5821
+ return {
5822
+ x: anchorPoint.x + direction.x * WICK_CLEARANCE,
5823
+ y: anchorPoint.y + direction.y * WICK_CLEARANCE
5824
+ };
5825
+ }
5826
+ getSideOffsetAnchor(params) {
5827
+ const { anchorPoint, labelAnchorPoint, orientation, width, height } = params;
5828
+ const chipSide = this.getChipSideForPoint(labelAnchorPoint);
5829
+ if (isYOrientation(orientation) && chipSide === "left") {
5830
+ return {
5831
+ x: anchorPoint.x - width / 2 - WICK_CLEARANCE,
5832
+ y: anchorPoint.y
5833
+ };
5834
+ }
5835
+ if (isYOrientation(orientation) && chipSide === "right") {
5836
+ return {
5837
+ x: anchorPoint.x + width / 2 + WICK_CLEARANCE,
5838
+ y: anchorPoint.y
5839
+ };
5840
+ }
5841
+ if (isXOrientation(orientation) && chipSide === "bottom") {
5842
+ return {
5843
+ x: anchorPoint.x,
5844
+ y: anchorPoint.y - height / 2 - WICK_CLEARANCE
5845
+ };
5846
+ }
5847
+ if (isXOrientation(orientation) && chipSide === "top") {
5848
+ return {
5849
+ x: anchorPoint.x,
5850
+ y: anchorPoint.y + height / 2 + WICK_CLEARANCE
5851
+ };
5852
+ }
5853
+ return anchorPoint;
5854
+ }
5855
+ getSearchDistanceLimit(label, orientation) {
5856
+ const { width, height } = getDimsForOrientation({
5857
+ orientation,
5858
+ netLabelWidth: this.getNetLabelWidth(label)
5859
+ });
5860
+ const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
5861
+ return Math.min(this.maxSearchDistance, labelLength * 2);
5862
+ }
5863
+ createCandidate(label, anchorPoint, orientation) {
5864
+ const { width, height } = getDimsForOrientation({
5865
+ orientation,
5866
+ netLabelWidth: this.getNetLabelWidth(label)
5867
+ });
5868
+ return {
5869
+ orientation,
5870
+ anchorPoint,
5871
+ width,
5872
+ height,
5873
+ center: getCenterFromAnchor(anchorPoint, orientation, width, height)
5874
+ };
5875
+ }
5876
+ getNetLabelWidth(label) {
5877
+ if (!label.netId) return void 0;
5878
+ return this.inputProblem.netConnections.find(
5879
+ (connection) => connection.netId === label.netId
5880
+ )?.netLabelWidth;
5881
+ }
5882
+ isLabelPlacementValid(label, labelIndex) {
5883
+ return this.isCandidateValidWithOffset(
5884
+ {
5885
+ orientation: label.orientation,
5886
+ anchorPoint: label.anchorPoint,
5887
+ center: label.center,
5888
+ width: label.width,
5889
+ height: label.height
5890
+ },
5891
+ labelIndex
5892
+ );
5893
+ }
5894
+ isCandidateValidWithOffset(candidate, labelIndex) {
5895
+ const bounds = this.getOffsetCollisionBounds(candidate);
5896
+ return this.getBoundsStatus(bounds, labelIndex) === "valid";
5897
+ }
5898
+ getCandidateStatus(candidate, label, labelIndex) {
5899
+ const boundsStatus = this.getBoundsStatus(
5900
+ getRectBounds(candidate.center, candidate.width, candidate.height),
5901
+ labelIndex
5902
+ );
5903
+ if (boundsStatus !== "valid") return boundsStatus;
5904
+ if (tracePathCrossesAnyTrace(
5905
+ getConnectorTracePath(
5906
+ label.anchorPoint,
5907
+ candidate.anchorPoint,
5908
+ candidate.orientation
5909
+ ),
5910
+ this.traceMap
5911
+ )) {
5912
+ return "trace-collision";
5913
+ }
5914
+ return "valid";
5915
+ }
5916
+ getBoundsStatus(bounds, labelIndex) {
5917
+ if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
5918
+ return "chip-collision";
5919
+ }
5920
+ if (this.sharesChipBoundary(bounds)) {
5921
+ return "chip-collision";
5922
+ }
5923
+ if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
5924
+ return "trace-collision";
5925
+ }
5926
+ if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
5927
+ return "netlabel-collision";
5928
+ }
5929
+ return "valid";
5930
+ }
5931
+ getOffsetCollisionBounds(candidate) {
5932
+ const direction = dir(candidate.orientation);
5933
+ const collisionTestOffset = 1e-4;
5934
+ const testCenter = {
5935
+ x: candidate.center.x + direction.x * collisionTestOffset,
5936
+ y: candidate.center.y + direction.y * collisionTestOffset
5937
+ };
5938
+ return getRectBounds(testCenter, candidate.width, candidate.height);
5939
+ }
5940
+ intersectsAnyOtherNetLabel(bounds, labelIndex) {
5941
+ for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
5942
+ if (i === labelIndex) continue;
5943
+ const label = this.outputNetLabelPlacements[i];
5944
+ const otherBounds = getRectBounds(label.center, label.width, label.height);
5945
+ if (rectsOverlap(bounds, otherBounds)) return true;
5946
+ }
5947
+ return false;
5948
+ }
5949
+ sharesChipBoundary(bounds) {
5950
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
5951
+ const chipBounds = chip.bounds;
5952
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS6 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS6;
5953
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS6 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS6;
5954
+ if (adjacentToVerticalSide && rangesOverlap(
5955
+ bounds.minY,
5956
+ bounds.maxY,
5957
+ chipBounds.minY,
5958
+ chipBounds.maxY
5959
+ )) {
5960
+ return true;
5961
+ }
5962
+ if (adjacentToHorizontalSide && rangesOverlap(
5963
+ bounds.minX,
5964
+ bounds.maxX,
5965
+ chipBounds.minX,
5966
+ chipBounds.maxX
5967
+ )) {
5968
+ return true;
5969
+ }
5970
+ }
5971
+ return false;
5972
+ }
5973
+ getChipSideForPoint(point) {
5974
+ const containingSide = this.getContainingChipSide(point);
5975
+ if (containingSide) return containingSide;
5976
+ const side = this.getOutsideChipSide(point);
5977
+ if (side) return side;
5978
+ return this.getNearestChipSide(point);
5979
+ }
5980
+ getPerpendicularOutwardDirection(point, orientation) {
5981
+ const chipSide = this.getChipSideForPoint(point);
5982
+ if (isYOrientation(orientation) && chipSide === "left") {
5983
+ return { x: -1, y: 0 };
5984
+ }
5985
+ if (isYOrientation(orientation) && chipSide === "right") {
5986
+ return { x: 1, y: 0 };
5987
+ }
5988
+ if (isXOrientation(orientation) && chipSide === "bottom") {
5989
+ return { x: 0, y: -1 };
5990
+ }
5991
+ if (isXOrientation(orientation) && chipSide === "top") {
5992
+ return { x: 0, y: 1 };
5993
+ }
5994
+ return { x: 0, y: 0 };
5995
+ }
5996
+ getContainingChipSide(point) {
5997
+ let nearestSide = null;
5998
+ let nearestDistance = Number.POSITIVE_INFINITY;
5999
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
6000
+ const bounds = chip.bounds;
6001
+ if (point.x < bounds.minX - EPS6 || point.x > bounds.maxX + EPS6 || point.y < bounds.minY - EPS6 || point.y > bounds.maxY + EPS6) {
6002
+ continue;
6003
+ }
6004
+ for (const [side, distance3] of getSideDistances(point, bounds)) {
6005
+ if (distance3 < nearestDistance) {
6006
+ nearestSide = side;
6007
+ nearestDistance = distance3;
6008
+ }
6009
+ }
6010
+ }
6011
+ return nearestSide;
6012
+ }
6013
+ getOutsideChipSide(point) {
6014
+ let nearestSide = null;
6015
+ let nearestDistanceSq = Number.POSITIVE_INFINITY;
6016
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
6017
+ const bounds = chip.bounds;
6018
+ const dx = point.x < bounds.minX - EPS6 ? bounds.minX - point.x : point.x > bounds.maxX + EPS6 ? point.x - bounds.maxX : 0;
6019
+ const dy = point.y < bounds.minY - EPS6 ? bounds.minY - point.y : point.y > bounds.maxY + EPS6 ? point.y - bounds.maxY : 0;
6020
+ if (dx === 0 && dy === 0) continue;
6021
+ const distanceSq = dx ** 2 + dy ** 2;
6022
+ if (distanceSq >= nearestDistanceSq) continue;
6023
+ nearestDistanceSq = distanceSq;
6024
+ if (dx > 0) {
6025
+ nearestSide = point.x < bounds.minX ? "left" : "right";
6026
+ } else {
6027
+ nearestSide = point.y < bounds.minY ? "bottom" : "top";
6028
+ }
6029
+ }
6030
+ return nearestSide;
6031
+ }
6032
+ getNearestChipSide(point) {
6033
+ let nearestSide = null;
6034
+ let nearestDistance = Number.POSITIVE_INFINITY;
6035
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
6036
+ for (const [side, distance3] of getSideDistances(point, chip.bounds)) {
6037
+ if (distance3 < nearestDistance) {
6038
+ nearestSide = side;
6039
+ nearestDistance = distance3;
6040
+ }
6041
+ }
6042
+ }
6043
+ return nearestSide;
6044
+ }
6045
+ visualize() {
6046
+ return visualizeAvailableNetOrientationSolver({
6047
+ inputProblem: this.inputProblem,
6048
+ traces: this.traces,
6049
+ outputNetLabelPlacements: this.outputNetLabelPlacements,
6050
+ currentLabel: this.currentLabel,
6051
+ currentCandidateResults: this.currentCandidateResults,
6052
+ solved: this.solved
6053
+ });
6054
+ }
6055
+ };
6056
+
5348
6057
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
5349
6058
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
5350
6059
  return {
@@ -5366,6 +6075,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
5366
6075
  traceLabelOverlapAvoidanceSolver;
5367
6076
  traceCleanupSolver;
5368
6077
  example28Solver;
6078
+ availableNetOrientationSolver;
5369
6079
  startTimeOfPhase;
5370
6080
  endTimeOfPhase;
5371
6081
  timeSpentOnPhase;
@@ -5511,7 +6221,18 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
5511
6221
  netLabelPlacements: instance.netLabelPlacementSolver.netLabelPlacements
5512
6222
  }
5513
6223
  ];
5514
- })
6224
+ }),
6225
+ definePipelineStep(
6226
+ "availableNetOrientationSolver",
6227
+ AvailableNetOrientationSolver,
6228
+ (instance) => [
6229
+ {
6230
+ inputProblem: instance.inputProblem,
6231
+ traces: instance.example28Solver.outputTraces,
6232
+ netLabelPlacements: instance.example28Solver.outputNetLabelPlacements
6233
+ }
6234
+ ]
6235
+ )
5515
6236
  ];
5516
6237
  constructor(inputProblem) {
5517
6238
  super();