calculate-packing 0.0.56 → 0.0.58

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
@@ -138,17 +138,17 @@ type Rect = {
138
138
 
139
139
  type GlobalBounds = Bounds;
140
140
  declare class LargestRectOutsideOutlineFromPointSolver extends BaseSolver$1 {
141
- fullOutline: Point$1[];
141
+ ccwFullOutline: Point$1[];
142
142
  origin: Point$1;
143
143
  globalBounds: Bounds;
144
144
  largestRect: Rect | null;
145
145
  constructor(params: {
146
- fullOutline: Point$1[];
146
+ ccwFullOutline: Point$1[];
147
147
  origin: Point$1;
148
148
  globalBounds: Bounds;
149
149
  });
150
150
  getConstructorParams(): {
151
- fullOutline: Point$1[];
151
+ ccwFullOutline: Point$1[];
152
152
  origin: Point$1;
153
153
  globalBounds: Bounds;
154
154
  };
@@ -367,7 +367,7 @@ declare class TwoPhaseIrlsSolver extends BaseSolver {
367
367
  declare class OutlineSegmentCandidatePointSolver extends BaseSolver$1 {
368
368
  outlineSegment: [Point$2, Point$2];
369
369
  viableOutlineSegment: [Point$2, Point$2] | null;
370
- fullOutline: [Point$2, Point$2][];
370
+ ccwFullOutline: [Point$2, Point$2][];
371
371
  componentRotationDegrees: number;
372
372
  packStrategy: PackPlacementStrategy;
373
373
  minGap: number;
@@ -385,7 +385,7 @@ declare class OutlineSegmentCandidatePointSolver extends BaseSolver$1 {
385
385
  twoPhaseIrlsSolver?: TwoPhaseIrlsSolver;
386
386
  constructor(params: {
387
387
  outlineSegment: [Point$2, Point$2];
388
- fullOutline: [Point$2, Point$2][];
388
+ ccwFullOutline: [Point$2, Point$2][];
389
389
  componentRotationDegrees: number;
390
390
  packStrategy: PackPlacementStrategy;
391
391
  minGap: number;
@@ -434,7 +434,7 @@ interface QueuedOutlineSegment {
434
434
  segment: Segment;
435
435
  availableRotations: number[];
436
436
  segmentIndex: number;
437
- fullOutline: Segment[];
437
+ ccwFullOutline: Segment[];
438
438
  }
439
439
  interface CandidateResult {
440
440
  segment: Segment;
package/dist/index.js CHANGED
@@ -1014,7 +1014,7 @@ function pointInOutline(p, segments, rule = "even-odd") {
1014
1014
  }
1015
1015
 
1016
1016
  // lib/OutlineSegmentCandidatePointSolver/getOutwardNormal.ts
1017
- function getOutwardNormal(outlineSegment, fullOutline) {
1017
+ function getOutwardNormal(outlineSegment, ccwFullOutline) {
1018
1018
  const [p1, p2] = outlineSegment;
1019
1019
  const dx = p2.x - p1.x;
1020
1020
  const dy = p2.y - p1.y;
@@ -1030,7 +1030,7 @@ function getOutwardNormal(outlineSegment, fullOutline) {
1030
1030
  x: (p1.x + p2.x) / 2,
1031
1031
  y: (p1.y + p2.y) / 2
1032
1032
  };
1033
- const bbox = getOutlineBoundsWithMargin(fullOutline);
1033
+ const bbox = getOutlineBoundsWithMargin(ccwFullOutline);
1034
1034
  const scale = Math.max(bbox.maxX - bbox.minX, bbox.maxY - bbox.minY) || 1;
1035
1035
  const testDistance = Math.max(1e-4, 1e-3 * scale);
1036
1036
  const testLeft = {
@@ -1041,18 +1041,18 @@ function getOutwardNormal(outlineSegment, fullOutline) {
1041
1041
  x: mid.x + right.x * testDistance,
1042
1042
  y: mid.y + right.y * testDistance
1043
1043
  };
1044
- const locLeft = pointInOutline(testLeft, fullOutline);
1044
+ const locLeft = pointInOutline(testLeft, ccwFullOutline);
1045
1045
  if (locLeft === "outside") {
1046
1046
  return left;
1047
1047
  }
1048
- const locRight = pointInOutline(testRight, fullOutline);
1048
+ const locRight = pointInOutline(testRight, ccwFullOutline);
1049
1049
  if (locRight === "outside") {
1050
1050
  return right;
1051
1051
  }
1052
1052
  const verts = [];
1053
- if (fullOutline.length > 0) {
1054
- verts.push(fullOutline[0][0]);
1055
- for (const seg of fullOutline) {
1053
+ if (ccwFullOutline.length > 0) {
1054
+ verts.push(ccwFullOutline[0][0]);
1055
+ for (const seg of ccwFullOutline) {
1056
1056
  verts.push(seg[1]);
1057
1057
  }
1058
1058
  }
@@ -1077,12 +1077,12 @@ function getOutwardNormal(outlineSegment, fullOutline) {
1077
1077
  const dotRight = right.x * away.x + right.y * away.y;
1078
1078
  return dotRight >= dotLeft ? right : left;
1079
1079
  }
1080
- function getOutlineBoundsWithMargin(fullOutline, margin = 0) {
1080
+ function getOutlineBoundsWithMargin(ccwFullOutline, margin = 0) {
1081
1081
  let minX = Infinity;
1082
1082
  let minY = Infinity;
1083
1083
  let maxX = -Infinity;
1084
1084
  let maxY = -Infinity;
1085
- for (const [p1, p2] of fullOutline) {
1085
+ for (const [p1, p2] of ccwFullOutline) {
1086
1086
  minX = Math.min(minX, p1.x, p2.x);
1087
1087
  minY = Math.min(minY, p1.y, p2.y);
1088
1088
  maxX = Math.max(maxX, p1.x, p2.x);
@@ -1099,19 +1099,19 @@ function getOutlineBoundsWithMargin(fullOutline, margin = 0) {
1099
1099
  // lib/LargestRectOutsideOutlineFromPointSolver.ts
1100
1100
  import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
1101
1101
  var LargestRectOutsideOutlineFromPointSolver = class extends BaseSolver2 {
1102
- fullOutline;
1102
+ ccwFullOutline;
1103
1103
  origin;
1104
1104
  globalBounds;
1105
1105
  largestRect = null;
1106
1106
  constructor(params) {
1107
1107
  super();
1108
- this.fullOutline = params.fullOutline;
1108
+ this.ccwFullOutline = params.ccwFullOutline;
1109
1109
  this.origin = params.origin;
1110
1110
  this.globalBounds = params.globalBounds;
1111
1111
  }
1112
1112
  getConstructorParams() {
1113
1113
  return {
1114
- fullOutline: this.fullOutline,
1114
+ ccwFullOutline: this.ccwFullOutline,
1115
1115
  origin: this.origin,
1116
1116
  globalBounds: this.globalBounds
1117
1117
  };
@@ -1123,7 +1123,7 @@ var LargestRectOutsideOutlineFromPointSolver = class extends BaseSolver2 {
1123
1123
  this.solved = true;
1124
1124
  }
1125
1125
  computeLargestRectOutside() {
1126
- const edges = this.makeEdges(this.fullOutline);
1126
+ const edges = this.makeEdges(this.ccwFullOutline);
1127
1127
  const bounds = {
1128
1128
  x: this.globalBounds.minX,
1129
1129
  y: this.globalBounds.minY,
@@ -1318,9 +1318,9 @@ var LargestRectOutsideOutlineFromPointSolver = class extends BaseSolver2 {
1318
1318
  rects: [],
1319
1319
  circles: []
1320
1320
  };
1321
- for (let i = 0; i < this.fullOutline.length; i++) {
1322
- const p1 = this.fullOutline[i];
1323
- const p2 = this.fullOutline[(i + 1) % this.fullOutline.length];
1321
+ for (let i = 0; i < this.ccwFullOutline.length; i++) {
1322
+ const p1 = this.ccwFullOutline[i];
1323
+ const p2 = this.ccwFullOutline[(i + 1) % this.ccwFullOutline.length];
1324
1324
  if (p1 && p2) {
1325
1325
  graphics.lines.push({
1326
1326
  points: [p1, p2],
@@ -1329,7 +1329,7 @@ var LargestRectOutsideOutlineFromPointSolver = class extends BaseSolver2 {
1329
1329
  }
1330
1330
  }
1331
1331
  graphics.lines.push({
1332
- points: [...this.fullOutline, this.fullOutline[0]],
1332
+ points: [...this.ccwFullOutline, this.ccwFullOutline[0]],
1333
1333
  strokeColor: "rgba(200, 200, 200, 0.5)",
1334
1334
  strokeDash: [10, 5]
1335
1335
  });
@@ -1446,7 +1446,7 @@ function isPointInPolygon(point, polygon) {
1446
1446
  var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1447
1447
  outlineSegment;
1448
1448
  viableOutlineSegment = null;
1449
- fullOutline;
1449
+ ccwFullOutline;
1450
1450
  // The entire outline containing the segment
1451
1451
  componentRotationDegrees;
1452
1452
  packStrategy;
@@ -1463,7 +1463,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1463
1463
  constructor(params) {
1464
1464
  super();
1465
1465
  this.outlineSegment = params.outlineSegment;
1466
- this.fullOutline = params.fullOutline;
1466
+ this.ccwFullOutline = params.ccwFullOutline;
1467
1467
  this.componentRotationDegrees = params.componentRotationDegrees;
1468
1468
  this.packStrategy = params.packStrategy;
1469
1469
  this.minGap = params.minGap;
@@ -1476,7 +1476,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1476
1476
  getConstructorParams() {
1477
1477
  return {
1478
1478
  outlineSegment: this.outlineSegment,
1479
- fullOutline: this.fullOutline,
1479
+ ccwFullOutline: this.ccwFullOutline,
1480
1480
  componentRotationDegrees: this.componentRotationDegrees,
1481
1481
  packStrategy: this.packStrategy,
1482
1482
  minGap: this.minGap,
@@ -1492,7 +1492,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1492
1492
  let minY = Infinity;
1493
1493
  let maxX = -Infinity;
1494
1494
  let maxY = -Infinity;
1495
- for (const [p1, p2] of this.fullOutline) {
1495
+ for (const [p1, p2] of this.ccwFullOutline) {
1496
1496
  minX = Math.min(minX, p1.x, p2.x);
1497
1497
  minY = Math.min(minY, p1.y, p2.y);
1498
1498
  maxX = Math.max(maxX, p1.x, p2.x);
@@ -1518,7 +1518,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1518
1518
  };
1519
1519
  const outwardNormal = getOutwardNormal(
1520
1520
  this.outlineSegment,
1521
- this.fullOutline
1521
+ this.ccwFullOutline
1522
1522
  );
1523
1523
  const componentBounds = getInputComponentBounds(this.componentToPack, {
1524
1524
  rotationDegrees: this.componentRotationDegrees
@@ -1530,7 +1530,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1530
1530
  ) * 2 + this.minGap * 2
1531
1531
  });
1532
1532
  const largestRectSolverParams = {
1533
- fullOutline: this.fullOutline.flatMap(([p]) => p),
1533
+ ccwFullOutline: this.ccwFullOutline.flatMap(([p]) => p),
1534
1534
  globalBounds: packedComponentBoundsWithMargin,
1535
1535
  origin: {
1536
1536
  x: (p1.x + p2.x) / 2 + outwardNormal.x * 1e-4,
@@ -1740,7 +1740,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1740
1740
  const bounds = getComponentBounds(tempComponent, 0);
1741
1741
  const outwardNormal = getOutwardNormal(
1742
1742
  this.outlineSegment,
1743
- this.fullOutline
1743
+ this.ccwFullOutline
1744
1744
  );
1745
1745
  const isHorizontalNormal = Math.abs(outwardNormal.x) > Math.abs(outwardNormal.y);
1746
1746
  const isVerticalNormal = !isHorizontalNormal;
@@ -1873,7 +1873,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1873
1873
  strokeColor: "rgba(255,0,0,1)",
1874
1874
  strokeDash: "3 3"
1875
1875
  });
1876
- for (const [p1, p2] of this.fullOutline) {
1876
+ for (const [p1, p2] of this.ccwFullOutline) {
1877
1877
  graphics.lines.push({
1878
1878
  points: [p1, p2],
1879
1879
  strokeColor: "rgba(0,0,0,0.5)",
@@ -2177,7 +2177,7 @@ var SingleComponentPackSolver = class extends BaseSolver4 {
2177
2177
  availableRotations: [...availableRotations],
2178
2178
  segmentIndex: segmentIndex * 1e3 + i,
2179
2179
  // Unique index across all outlines
2180
- fullOutline: outline
2180
+ ccwFullOutline: outline
2181
2181
  // Pass the entire outline containing this segment
2182
2182
  });
2183
2183
  }
@@ -2311,7 +2311,7 @@ var SingleComponentPackSolver = class extends BaseSolver4 {
2311
2311
  const rotation = queuedSegment.availableRotations[this.currentRotationIndex];
2312
2312
  this.activeSubSolver = new OutlineSegmentCandidatePointSolver({
2313
2313
  outlineSegment: queuedSegment.segment,
2314
- fullOutline: queuedSegment.fullOutline,
2314
+ ccwFullOutline: queuedSegment.ccwFullOutline,
2315
2315
  componentRotationDegrees: rotation,
2316
2316
  packStrategy: this.packPlacementStrategy,
2317
2317
  minGap: this.minGap,
@@ -3110,12 +3110,30 @@ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
3110
3110
  return `unnamed${unnamedCounter++}`;
3111
3111
  };
3112
3112
  const topLevelNodes = tree.childNodes ?? [];
3113
+ const collectRelativeToGroupAnchorComponents = (node) => {
3114
+ const relativeComponents2 = [];
3115
+ if (node.nodeType === "component") {
3116
+ const pcbComponent = node.otherChildElements.find(
3117
+ (e) => e.type === "pcb_component"
3118
+ );
3119
+ if (pcbComponent && pcbComponent.position_mode === "relative_to_group_anchor") {
3120
+ relativeComponents2.push(pcbComponent);
3121
+ }
3122
+ }
3123
+ for (const child of node.childNodes ?? []) {
3124
+ relativeComponents2.push(...collectRelativeToGroupAnchorComponents(child));
3125
+ }
3126
+ return relativeComponents2;
3127
+ };
3113
3128
  for (const node of topLevelNodes) {
3114
3129
  if (node.nodeType === "component") {
3115
3130
  const pcbComponent = node.otherChildElements.find(
3116
3131
  (e) => e.type === "pcb_component"
3117
3132
  );
3118
3133
  if (!pcbComponent) continue;
3134
+ if (pcbComponent.position_mode === "relative_to_group_anchor") {
3135
+ continue;
3136
+ }
3119
3137
  let shouldAddInnerObstaclesForComp = opts.shouldAddInnerObstacles;
3120
3138
  if (pcbComponent.obstructs_within_bounds === false) {
3121
3139
  shouldAddInnerObstaclesForComp = false;
@@ -3146,6 +3164,32 @@ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
3146
3164
  );
3147
3165
  }
3148
3166
  }
3167
+ const relativeComponents = topLevelNodes.flatMap(
3168
+ (node) => collectRelativeToGroupAnchorComponents(node)
3169
+ );
3170
+ for (const pcbComponent of relativeComponents) {
3171
+ const padInfos = extractPadInfos(pcbComponent, db, getNetworkId);
3172
+ if (padInfos.length === 0) continue;
3173
+ let minX = Infinity;
3174
+ let minY = Infinity;
3175
+ let maxX = -Infinity;
3176
+ let maxY = -Infinity;
3177
+ for (const pad of padInfos) {
3178
+ minX = Math.min(minX, pad.absoluteCenter.x - pad.size.x / 2);
3179
+ maxX = Math.max(maxX, pad.absoluteCenter.x + pad.size.x / 2);
3180
+ minY = Math.min(minY, pad.absoluteCenter.y - pad.size.y / 2);
3181
+ maxY = Math.max(maxY, pad.absoluteCenter.y + pad.size.y / 2);
3182
+ }
3183
+ const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
3184
+ const width = maxX - minX;
3185
+ const height = maxY - minY;
3186
+ packOutput.obstacles.push({
3187
+ obstacleId: pcbComponent.pcb_component_id,
3188
+ absoluteCenter: center,
3189
+ width,
3190
+ height
3191
+ });
3192
+ }
3149
3193
  for (const element of elementsOutsideTree) {
3150
3194
  if (element.type === "pcb_plated_hole" && element.shape === "circular_hole_with_rect_pad") {
3151
3195
  const { rect_pad_height, rect_pad_width, x, y } = element;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "calculate-packing",
3
3
  "main": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.0.56",
5
+ "version": "0.0.58",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",