calculate-packing 0.0.70 → 0.0.72

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
@@ -29,6 +29,14 @@ interface OutputPad extends InputPad {
29
29
  y: number;
30
30
  };
31
31
  }
32
+ interface ComponentCourtyard {
33
+ offsetFromCenter: {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ width: number;
38
+ height: number;
39
+ }
32
40
  interface InputComponent {
33
41
  componentId: string;
34
42
  /** Components marked as static are not moved by the packer */
@@ -45,6 +53,8 @@ interface InputComponent {
45
53
  /** Preconfigured rotation (degrees CCW) for static components */
46
54
  ccwRotationOffset?: number;
47
55
  pads: InputPad[];
56
+ /** Optional courtyard defining the component's physical boundary */
57
+ courtyard?: ComponentCourtyard;
48
58
  }
49
59
  interface PackedComponent extends InputComponent {
50
60
  center: {
@@ -588,4 +598,4 @@ declare class PackSolver2 extends BaseSolver$1 {
588
598
  };
589
599
  }
590
600
 
591
- export { type ComponentId, type GlobalBounds, type InputComponent, type InputObstacle, type InputPad, LargestRectOutsideOutlineFromPointSolver, type NetworkId, type OutputPad, type PackInput, type PackOutput, type PackPlacementStrategy, PackSolver2, type PackedComponent, type PadId, type Point$1 as Point, type Rect, convertCircuitJsonToPackOutput, convertPackOutputToPackInput, getGraphicsFromPackOutput, pack };
601
+ export { type ComponentCourtyard, type ComponentId, type GlobalBounds, type InputComponent, type InputObstacle, type InputPad, LargestRectOutsideOutlineFromPointSolver, type NetworkId, type OutputPad, type PackInput, type PackOutput, type PackPlacementStrategy, PackSolver2, type PackedComponent, type PadId, type Point$1 as Point, type Rect, convertCircuitJsonToPackOutput, convertPackOutputToPackInput, getGraphicsFromPackOutput, pack };
package/dist/index.js CHANGED
@@ -68,6 +68,30 @@ var combineBounds = (bounds) => {
68
68
  return { minX, minY, maxX, maxY };
69
69
  };
70
70
 
71
+ // lib/geometry/expandRotatedRectIntoBounds.ts
72
+ function expandRotatedRectIntoBounds(opts) {
73
+ const { bounds, center, width, height, angleRad } = opts;
74
+ const tx = opts.translate?.x ?? 0;
75
+ const ty = opts.translate?.y ?? 0;
76
+ const hw = width / 2;
77
+ const hh = height / 2;
78
+ const corners = [
79
+ { x: center.x - hw, y: center.y - hh },
80
+ { x: center.x + hw, y: center.y - hh },
81
+ { x: center.x + hw, y: center.y + hh },
82
+ { x: center.x - hw, y: center.y + hh }
83
+ ];
84
+ for (const corner of corners) {
85
+ const rotated = rotatePoint(corner, angleRad);
86
+ const x = rotated.x + tx;
87
+ const y = rotated.y + ty;
88
+ bounds.minX = Math.min(bounds.minX, x);
89
+ bounds.maxX = Math.max(bounds.maxX, x);
90
+ bounds.minY = Math.min(bounds.minY, y);
91
+ bounds.maxY = Math.max(bounds.maxY, y);
92
+ }
93
+ }
94
+
71
95
  // lib/geometry/getComponentBounds.ts
72
96
  var getComponentBounds = (component, minGap = 0) => {
73
97
  const bounds = {
@@ -76,28 +100,27 @@ var getComponentBounds = (component, minGap = 0) => {
76
100
  minY: Infinity,
77
101
  maxY: -Infinity
78
102
  };
79
- component.pads.forEach((pad) => {
80
- const hw = pad.size.x / 2;
81
- const hh = pad.size.y / 2;
82
- const localCorners = [
83
- { x: pad.offset.x - hw, y: pad.offset.y - hh },
84
- { x: pad.offset.x + hw, y: pad.offset.y - hh },
85
- { x: pad.offset.x + hw, y: pad.offset.y + hh },
86
- { x: pad.offset.x - hw, y: pad.offset.y + hh }
87
- ];
88
- localCorners.forEach((corner) => {
89
- const world = rotatePoint(
90
- corner,
91
- component.ccwRotationOffset * Math.PI / 180
92
- );
93
- const x = world.x + component.center.x;
94
- const y = world.y + component.center.y;
95
- bounds.minX = Math.min(bounds.minX, x);
96
- bounds.maxX = Math.max(bounds.maxX, x);
97
- bounds.minY = Math.min(bounds.minY, y);
98
- bounds.maxY = Math.max(bounds.maxY, y);
103
+ const angleRad = component.ccwRotationOffset * Math.PI / 180;
104
+ for (const pad of component.pads) {
105
+ expandRotatedRectIntoBounds({
106
+ bounds,
107
+ center: pad.offset,
108
+ width: pad.size.x,
109
+ height: pad.size.y,
110
+ angleRad,
111
+ translate: component.center
99
112
  });
100
- });
113
+ }
114
+ if (component.courtyard) {
115
+ expandRotatedRectIntoBounds({
116
+ bounds,
117
+ center: component.courtyard.offsetFromCenter,
118
+ width: component.courtyard.width,
119
+ height: component.courtyard.height,
120
+ angleRad,
121
+ translate: component.center
122
+ });
123
+ }
101
124
  return {
102
125
  minX: bounds.minX - minGap,
103
126
  maxX: bounds.maxX + minGap,
@@ -251,6 +274,50 @@ function parseFlattenPolygonSegments(polygon) {
251
274
  }
252
275
 
253
276
  // lib/constructOutlinesFromPackedComponents.ts
277
+ var createCourtyardPolygon = (opts) => {
278
+ const { component, courtyard, minGap } = opts;
279
+ const hw = courtyard.width / 2 + minGap;
280
+ const hh = courtyard.height / 2 + minGap;
281
+ const localCorners = [
282
+ {
283
+ x: courtyard.offsetFromCenter.x - hw,
284
+ y: courtyard.offsetFromCenter.y - hh
285
+ },
286
+ {
287
+ x: courtyard.offsetFromCenter.x + hw,
288
+ y: courtyard.offsetFromCenter.y - hh
289
+ },
290
+ {
291
+ x: courtyard.offsetFromCenter.x + hw,
292
+ y: courtyard.offsetFromCenter.y + hh
293
+ },
294
+ {
295
+ x: courtyard.offsetFromCenter.x - hw,
296
+ y: courtyard.offsetFromCenter.y + hh
297
+ }
298
+ ];
299
+ const worldCorners = localCorners.map((corner) => {
300
+ const rotated = rotatePoint(
301
+ corner,
302
+ component.ccwRotationOffset * Math.PI / 180
303
+ );
304
+ return {
305
+ x: rotated.x + component.center.x,
306
+ y: rotated.y + component.center.y
307
+ };
308
+ });
309
+ const arr = worldCorners.map(({ x, y }) => [x, y]);
310
+ const poly = new Flatten.Polygon(arr);
311
+ const xs = worldCorners.map((p) => p.x);
312
+ const ys = worldCorners.map((p) => p.y);
313
+ const bbox = {
314
+ minX: Math.min(...xs),
315
+ minY: Math.min(...ys),
316
+ maxX: Math.max(...xs),
317
+ maxY: Math.max(...ys)
318
+ };
319
+ return { poly, bbox };
320
+ };
254
321
  var createPadPolygons = (component, minGap) => {
255
322
  return component.pads.map((pad) => {
256
323
  const hw = pad.size.x / 2 + minGap;
@@ -322,8 +389,18 @@ var constructOutlinesFromPackedComponents = (components, opts = {}) => {
322
389
  const bounds = combineBounds([...componentBounds, ...obstacleBounds]);
323
390
  const allPadShapes = [];
324
391
  for (const component of components) {
325
- const padShapes = createPadPolygons(component, minGap);
326
- allPadShapes.push(...padShapes);
392
+ if (component.courtyard) {
393
+ allPadShapes.push(
394
+ createCourtyardPolygon({
395
+ component,
396
+ courtyard: component.courtyard,
397
+ minGap
398
+ })
399
+ );
400
+ } else {
401
+ const padShapes = createPadPolygons(component, minGap);
402
+ allPadShapes.push(...padShapes);
403
+ }
327
404
  }
328
405
  const obstacleShapes = createObstaclePolygons(obstacles, minGap);
329
406
  allPadShapes.push(...obstacleShapes);
@@ -1363,31 +1440,26 @@ var getInputComponentBounds = (component, { rotationDegrees = 0 }) => {
1363
1440
  minY: Infinity,
1364
1441
  maxY: -Infinity
1365
1442
  };
1443
+ const angleRad = rotationDegrees * Math.PI / 180;
1366
1444
  for (const pad of component.pads) {
1367
- const hw = pad.size.x / 2;
1368
- const hh = pad.size.y / 2;
1369
- const localCorners = [
1370
- { x: pad.offset.x - hw, y: pad.offset.y - hh },
1371
- { x: pad.offset.x + hw, y: pad.offset.y - hh },
1372
- { x: pad.offset.x + hw, y: pad.offset.y + hh },
1373
- { x: pad.offset.x - hw, y: pad.offset.y + hh }
1374
- ];
1375
- for (const corner of localCorners) {
1376
- const world = rotatePoint(corner, rotationDegrees * Math.PI / 180);
1377
- const x = world.x;
1378
- const y = world.y;
1379
- bounds.minX = Math.min(bounds.minX, x);
1380
- bounds.maxX = Math.max(bounds.maxX, x);
1381
- bounds.minY = Math.min(bounds.minY, y);
1382
- bounds.maxY = Math.max(bounds.maxY, y);
1383
- }
1445
+ expandRotatedRectIntoBounds({
1446
+ bounds,
1447
+ center: pad.offset,
1448
+ width: pad.size.x,
1449
+ height: pad.size.y,
1450
+ angleRad
1451
+ });
1384
1452
  }
1385
- return {
1386
- minX: bounds.minX,
1387
- maxX: bounds.maxX,
1388
- minY: bounds.minY,
1389
- maxY: bounds.maxY
1390
- };
1453
+ if (component.courtyard) {
1454
+ expandRotatedRectIntoBounds({
1455
+ bounds,
1456
+ center: component.courtyard.offsetFromCenter,
1457
+ width: component.courtyard.width,
1458
+ height: component.courtyard.height,
1459
+ angleRad
1460
+ });
1461
+ }
1462
+ return bounds;
1391
1463
  };
1392
1464
 
1393
1465
  // lib/math/expandSegment.ts
@@ -1757,7 +1829,8 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver3 {
1757
1829
  y: center.y + rotatedOffset.y
1758
1830
  }
1759
1831
  };
1760
- })
1832
+ }),
1833
+ courtyard: this.componentToPack.courtyard
1761
1834
  };
1762
1835
  }
1763
1836
  /**
@@ -2114,28 +2187,54 @@ var getGraphicsFromPackOutput = (packOutput) => {
2114
2187
 
2115
2188
  // lib/PackSolver2/checkOverlapWithPackedComponents.ts
2116
2189
  import { computeDistanceBetweenBoxes } from "@tscircuit/math-utils";
2190
+
2191
+ // lib/PackSolver2/getComponentCollisionBoxes.ts
2192
+ function getComponentCollisionBoxes(component) {
2193
+ if (component.courtyard) {
2194
+ const courtyard = component.courtyard;
2195
+ const angleRad = component.ccwRotationOffset * Math.PI / 180;
2196
+ const rotatedOffset = rotatePoint(courtyard.offsetFromCenter, angleRad);
2197
+ const normalizedDeg = (component.ccwRotationOffset % 360 + 360) % 360;
2198
+ const swapDims = normalizedDeg === 90 || normalizedDeg === 270;
2199
+ let width = courtyard.width;
2200
+ let height = courtyard.height;
2201
+ if (swapDims) {
2202
+ width = courtyard.height;
2203
+ height = courtyard.width;
2204
+ }
2205
+ return [
2206
+ {
2207
+ center: {
2208
+ x: component.center.x + rotatedOffset.x,
2209
+ y: component.center.y + rotatedOffset.y
2210
+ },
2211
+ width,
2212
+ height
2213
+ }
2214
+ ];
2215
+ }
2216
+ return component.pads.map((p) => ({
2217
+ center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
2218
+ width: p.size.x,
2219
+ height: p.size.y
2220
+ }));
2221
+ }
2222
+
2223
+ // lib/PackSolver2/checkOverlapWithPackedComponents.ts
2117
2224
  function checkOverlapWithPackedComponents({
2118
2225
  component,
2119
2226
  packedComponents,
2120
2227
  minGap
2121
2228
  }) {
2122
- const allPackedPadBoxes = packedComponents.flatMap(
2123
- (c) => c.pads.map((p) => ({
2124
- center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
2125
- width: p.size.x,
2126
- height: p.size.y
2127
- }))
2229
+ const allPackedBoxes = packedComponents.flatMap(
2230
+ (c) => getComponentCollisionBoxes(c)
2128
2231
  );
2129
- const newComponentPadBoxes = component.pads.map((p) => ({
2130
- center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
2131
- width: p.size.x,
2132
- height: p.size.y
2133
- }));
2134
- for (const newComponentPadBox of newComponentPadBoxes) {
2135
- for (const packedPadBox of allPackedPadBoxes) {
2232
+ const newComponentBoxes = getComponentCollisionBoxes(component);
2233
+ for (const newBox of newComponentBoxes) {
2234
+ for (const packedBox of allPackedBoxes) {
2136
2235
  const { distance: boxDist } = computeDistanceBetweenBoxes(
2137
- newComponentPadBox,
2138
- packedPadBox
2236
+ newBox,
2237
+ packedBox
2139
2238
  );
2140
2239
  if (boxDist + 1e-6 < minGap) {
2141
2240
  return {
@@ -2216,19 +2315,15 @@ var SingleComponentPackSolver = class extends BaseSolver4 {
2216
2315
  const position = { x: 0, y: 0 };
2217
2316
  const rotation = availableRotations2[0] ?? 0;
2218
2317
  const candidate = this.createPackedComponent(position, rotation);
2318
+ const candidateBoxes = getComponentCollisionBoxes(candidate);
2219
2319
  const tooCloseToObstacles = (this.obstacles ?? []).some((obs) => {
2220
2320
  const obsBox = {
2221
2321
  center: { x: obs.absoluteCenter.x, y: obs.absoluteCenter.y },
2222
2322
  width: obs.width,
2223
2323
  height: obs.height
2224
2324
  };
2225
- return candidate.pads.some((p) => {
2226
- const padBox = {
2227
- center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
2228
- width: p.size.x,
2229
- height: p.size.y
2230
- };
2231
- const { distance } = computeDistanceBetweenBoxes2(padBox, obsBox);
2325
+ return candidateBoxes.some((box) => {
2326
+ const { distance } = computeDistanceBetweenBoxes2(box, obsBox);
2232
2327
  return distance + 1e-6 < this.minGap;
2233
2328
  });
2234
2329
  });
@@ -2333,19 +2428,15 @@ var SingleComponentPackSolver = class extends BaseSolver4 {
2333
2428
  minGap: this.minGap
2334
2429
  });
2335
2430
  let minObstacleGapDistance = Infinity;
2431
+ const candidateCollisionBoxes = getComponentCollisionBoxes(candidateComponent);
2336
2432
  const tooCloseToObstacles = (this.obstacles ?? []).some((obs) => {
2337
2433
  const obsBox = {
2338
2434
  center: { x: obs.absoluteCenter.x, y: obs.absoluteCenter.y },
2339
2435
  width: obs.width,
2340
2436
  height: obs.height
2341
2437
  };
2342
- return candidateComponent.pads.some((p) => {
2343
- const padBox = {
2344
- center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
2345
- width: p.size.x,
2346
- height: p.size.y
2347
- };
2348
- const { distance: distance2 } = computeDistanceBetweenBoxes2(padBox, obsBox);
2438
+ return candidateCollisionBoxes.some((box) => {
2439
+ const { distance: distance2 } = computeDistanceBetweenBoxes2(box, obsBox);
2349
2440
  minObstacleGapDistance = Math.min(minObstacleGapDistance, distance2);
2350
2441
  return distance2 + 1e-6 < this.minGap;
2351
2442
  });
@@ -2795,19 +2886,15 @@ var PackSolver2 = class extends BaseSolver5 {
2795
2886
  };
2796
2887
  setPackedComponentPadCenters(newPackedComponent);
2797
2888
  const obstacles = this.packInput.obstacles ?? [];
2889
+ const newComponentBoxes = getComponentCollisionBoxes(newPackedComponent);
2798
2890
  const tooCloseToObstacles = obstacles.some((obs) => {
2799
2891
  const obsBox = {
2800
2892
  center: { x: obs.absoluteCenter.x, y: obs.absoluteCenter.y },
2801
2893
  width: obs.width,
2802
2894
  height: obs.height
2803
2895
  };
2804
- return newPackedComponent.pads.some((p) => {
2805
- const padBox = {
2806
- center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
2807
- width: p.size.x,
2808
- height: p.size.y
2809
- };
2810
- const { distance } = computeDistanceBetweenBoxes3(padBox, obsBox);
2896
+ return newComponentBoxes.some((box) => {
2897
+ const { distance } = computeDistanceBetweenBoxes3(box, obsBox);
2811
2898
  return distance + 1e-6 < this.packInput.minGap;
2812
2899
  });
2813
2900
  });
@@ -3202,10 +3289,94 @@ var getElementOutsideTree = (db, tree) => {
3202
3289
  outside.push(ph);
3203
3290
  }
3204
3291
  }
3292
+ for (const hole of db.pcb_hole.list({})) {
3293
+ outside.push(hole);
3294
+ }
3205
3295
  return outside;
3206
3296
  };
3207
3297
 
3298
+ // lib/plumbing/getObstacleFromElement.ts
3299
+ var getObstacleFromElement = (element) => {
3300
+ if (element.type === "pcb_plated_hole" && element.shape === "circular_hole_with_rect_pad") {
3301
+ const { rect_pad_height, rect_pad_width, x, y } = element;
3302
+ return {
3303
+ obstacleId: element.pcb_plated_hole_id,
3304
+ absoluteCenter: { x, y },
3305
+ width: rect_pad_width,
3306
+ height: rect_pad_height
3307
+ };
3308
+ }
3309
+ if (element.type === "pcb_hole") {
3310
+ const { x, y, pcb_hole_id } = element;
3311
+ const width = "hole_diameter" in element ? element.hole_diameter : element.hole_width;
3312
+ const height = "hole_diameter" in element ? element.hole_diameter : element.hole_height;
3313
+ return {
3314
+ obstacleId: pcb_hole_id,
3315
+ absoluteCenter: { x, y },
3316
+ width,
3317
+ height
3318
+ };
3319
+ }
3320
+ return void 0;
3321
+ };
3322
+
3208
3323
  // lib/plumbing/convertCircuitJsonToPackOutput.ts
3324
+ var extractCourtyardForComponent = (opts) => {
3325
+ const { db, pcbComponentIds, componentCenter } = opts;
3326
+ const idSet = new Set(pcbComponentIds);
3327
+ let minX = Infinity;
3328
+ let minY = Infinity;
3329
+ let maxX = -Infinity;
3330
+ let maxY = -Infinity;
3331
+ let found = false;
3332
+ for (const rect of db.pcb_courtyard_rect.list()) {
3333
+ if (!idSet.has(rect.pcb_component_id)) continue;
3334
+ minX = Math.min(minX, rect.center.x - rect.width / 2);
3335
+ maxX = Math.max(maxX, rect.center.x + rect.width / 2);
3336
+ minY = Math.min(minY, rect.center.y - rect.height / 2);
3337
+ maxY = Math.max(maxY, rect.center.y + rect.height / 2);
3338
+ found = true;
3339
+ }
3340
+ for (const polygon of db.pcb_courtyard_polygon.list()) {
3341
+ if (!idSet.has(polygon.pcb_component_id)) continue;
3342
+ for (const pt of polygon.points) {
3343
+ minX = Math.min(minX, pt.x);
3344
+ maxX = Math.max(maxX, pt.x);
3345
+ minY = Math.min(minY, pt.y);
3346
+ maxY = Math.max(maxY, pt.y);
3347
+ }
3348
+ found = true;
3349
+ }
3350
+ for (const outline of db.pcb_courtyard_outline.list()) {
3351
+ if (!idSet.has(outline.pcb_component_id)) continue;
3352
+ for (const pt of outline.outline) {
3353
+ minX = Math.min(minX, pt.x);
3354
+ maxX = Math.max(maxX, pt.x);
3355
+ minY = Math.min(minY, pt.y);
3356
+ maxY = Math.max(maxY, pt.y);
3357
+ }
3358
+ found = true;
3359
+ }
3360
+ for (const circle of db.pcb_courtyard_circle.list()) {
3361
+ if (!idSet.has(circle.pcb_component_id)) continue;
3362
+ minX = Math.min(minX, circle.center.x - circle.radius);
3363
+ maxX = Math.max(maxX, circle.center.x + circle.radius);
3364
+ minY = Math.min(minY, circle.center.y - circle.radius);
3365
+ maxY = Math.max(maxY, circle.center.y + circle.radius);
3366
+ found = true;
3367
+ }
3368
+ if (!found) return void 0;
3369
+ const courtyardCenterX = (minX + maxX) / 2;
3370
+ const courtyardCenterY = (minY + maxY) / 2;
3371
+ return {
3372
+ offsetFromCenter: {
3373
+ x: courtyardCenterX - componentCenter.x,
3374
+ y: courtyardCenterY - componentCenter.y
3375
+ },
3376
+ width: maxX - minX,
3377
+ height: maxY - minY
3378
+ };
3379
+ };
3209
3380
  var buildPackedComponent = (pcbComponents, componentId, db, getNetworkId, shouldAddInnerObstacles, sourcePortToPadIds = /* @__PURE__ */ new Map(), chipMarginsMap = {}, isStatic = false) => {
3210
3381
  const padInfos = pcbComponents.flatMap((pc) => {
3211
3382
  const pads2 = extractPadInfos(pc, db, getNetworkId);
@@ -3264,12 +3435,18 @@ var buildPackedComponent = (pcbComponents, componentId, db, getNetworkId, should
3264
3435
  };
3265
3436
  pads.push(innerPad);
3266
3437
  }
3438
+ const courtyard = extractCourtyardForComponent({
3439
+ db,
3440
+ pcbComponentIds: pcbComponents.map((pc) => pc.pcb_component_id),
3441
+ componentCenter: center
3442
+ });
3267
3443
  return {
3268
3444
  componentId,
3269
3445
  isStatic,
3270
3446
  center,
3271
3447
  ccwRotationOffset: 0,
3272
- pads
3448
+ pads,
3449
+ courtyard
3273
3450
  };
3274
3451
  };
3275
3452
  var collectPcbComponents = (node, db) => {
@@ -3297,7 +3474,7 @@ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
3297
3474
  const pcbBoard = circuitJson.find(
3298
3475
  (item) => item.type === "pcb_board"
3299
3476
  );
3300
- if (pcbBoard && pcbBoard.outline) {
3477
+ if (pcbBoard?.outline) {
3301
3478
  packOutput.boundaryOutline = pcbBoard.outline;
3302
3479
  }
3303
3480
  const getNetworkId = (pcbPortId) => {
@@ -3401,14 +3578,9 @@ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
3401
3578
  });
3402
3579
  }
3403
3580
  for (const element of elementsOutsideTree) {
3404
- if (element.type === "pcb_plated_hole" && element.shape === "circular_hole_with_rect_pad") {
3405
- const { rect_pad_height, rect_pad_width, x, y } = element;
3406
- packOutput.obstacles.push({
3407
- obstacleId: element.pcb_plated_hole_id,
3408
- absoluteCenter: { x, y },
3409
- width: rect_pad_width,
3410
- height: rect_pad_height
3411
- });
3581
+ const obstacle = getObstacleFromElement(element);
3582
+ if (obstacle) {
3583
+ packOutput.obstacles.push(obstacle);
3412
3584
  }
3413
3585
  }
3414
3586
  const weightedConnections = [];
@@ -3451,7 +3623,8 @@ var convertPackOutputToPackInput = (packed) => {
3451
3623
  componentId: pc.componentId,
3452
3624
  availableRotationDegrees: pc.availableRotationDegrees,
3453
3625
  // Preserve rotation constraints
3454
- pads: pc.pads.map(({ absoluteCenter: _ac, ...rest }) => rest)
3626
+ pads: pc.pads.map(({ absoluteCenter: _ac, ...rest }) => rest),
3627
+ courtyard: pc.courtyard
3455
3628
  }
3456
3629
  }));
3457
3630
  return {
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.70",
5
+ "version": "0.0.72",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",
@@ -19,16 +19,16 @@
19
19
  "@biomejs/biome": "^2.1.1",
20
20
  "@flatten-js/core": "^1.6.2",
21
21
  "@react-hook/resize-observer": "^2.0.2",
22
- "@tscircuit/circuit-json-util": "^0.0.66",
22
+ "@tscircuit/circuit-json-util": "^0.0.94",
23
23
  "@tscircuit/footprinter": "^0.0.203",
24
- "@tscircuit/math-utils": "^0.0.25",
24
+ "@tscircuit/math-utils": "^0.0.36",
25
25
  "@tscircuit/solver-utils": "^0.0.14",
26
26
  "@types/bun": "latest",
27
27
  "@types/react": "^19.1.8",
28
28
  "@types/react-dom": "^19.1.6",
29
29
  "@vitejs/plugin-react": "^5.0.0",
30
30
  "bun-match-svg": "^0.0.12",
31
- "circuit-json": "^0.0.309",
31
+ "circuit-json": "^0.0.421",
32
32
  "framer-motion": "^12.23.12",
33
33
  "graphics-debug": "^0.0.78",
34
34
  "react": "^19.1.0",