calculate-packing 0.0.71 → 0.0.73
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 +11 -1
- package/dist/index.js +237 -85
- package/package.json +4 -4
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.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
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
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
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
|
|
2123
|
-
(c) => c
|
|
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
|
|
2130
|
-
|
|
2131
|
-
|
|
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
|
-
|
|
2138
|
-
|
|
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
|
|
2226
|
-
const
|
|
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
|
|
2343
|
-
const
|
|
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
|
|
2805
|
-
const
|
|
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
|
});
|
|
@@ -3234,6 +3321,64 @@ var getObstacleFromElement = (element) => {
|
|
|
3234
3321
|
};
|
|
3235
3322
|
|
|
3236
3323
|
// lib/plumbing/convertCircuitJsonToPackOutput.ts
|
|
3324
|
+
var extractCourtyardForComponent = (opts) => {
|
|
3325
|
+
const { db, pcbComponentIds, componentCenter } = opts;
|
|
3326
|
+
const uniquePcbComponentIds = Array.from(new Set(pcbComponentIds));
|
|
3327
|
+
if (uniquePcbComponentIds.length !== 1) return void 0;
|
|
3328
|
+
const idSet = new Set(pcbComponentIds);
|
|
3329
|
+
let minX = Infinity;
|
|
3330
|
+
let minY = Infinity;
|
|
3331
|
+
let maxX = -Infinity;
|
|
3332
|
+
let maxY = -Infinity;
|
|
3333
|
+
let found = false;
|
|
3334
|
+
for (const rect of db.pcb_courtyard_rect.list()) {
|
|
3335
|
+
if (!idSet.has(rect.pcb_component_id)) continue;
|
|
3336
|
+
minX = Math.min(minX, rect.center.x - rect.width / 2);
|
|
3337
|
+
maxX = Math.max(maxX, rect.center.x + rect.width / 2);
|
|
3338
|
+
minY = Math.min(minY, rect.center.y - rect.height / 2);
|
|
3339
|
+
maxY = Math.max(maxY, rect.center.y + rect.height / 2);
|
|
3340
|
+
found = true;
|
|
3341
|
+
}
|
|
3342
|
+
for (const polygon of db.pcb_courtyard_polygon.list()) {
|
|
3343
|
+
if (!idSet.has(polygon.pcb_component_id)) continue;
|
|
3344
|
+
for (const pt of polygon.points) {
|
|
3345
|
+
minX = Math.min(minX, pt.x);
|
|
3346
|
+
maxX = Math.max(maxX, pt.x);
|
|
3347
|
+
minY = Math.min(minY, pt.y);
|
|
3348
|
+
maxY = Math.max(maxY, pt.y);
|
|
3349
|
+
}
|
|
3350
|
+
found = true;
|
|
3351
|
+
}
|
|
3352
|
+
for (const outline of db.pcb_courtyard_outline.list()) {
|
|
3353
|
+
if (!idSet.has(outline.pcb_component_id)) continue;
|
|
3354
|
+
for (const pt of outline.outline) {
|
|
3355
|
+
minX = Math.min(minX, pt.x);
|
|
3356
|
+
maxX = Math.max(maxX, pt.x);
|
|
3357
|
+
minY = Math.min(minY, pt.y);
|
|
3358
|
+
maxY = Math.max(maxY, pt.y);
|
|
3359
|
+
}
|
|
3360
|
+
found = true;
|
|
3361
|
+
}
|
|
3362
|
+
for (const circle of db.pcb_courtyard_circle.list()) {
|
|
3363
|
+
if (!idSet.has(circle.pcb_component_id)) continue;
|
|
3364
|
+
minX = Math.min(minX, circle.center.x - circle.radius);
|
|
3365
|
+
maxX = Math.max(maxX, circle.center.x + circle.radius);
|
|
3366
|
+
minY = Math.min(minY, circle.center.y - circle.radius);
|
|
3367
|
+
maxY = Math.max(maxY, circle.center.y + circle.radius);
|
|
3368
|
+
found = true;
|
|
3369
|
+
}
|
|
3370
|
+
if (!found) return void 0;
|
|
3371
|
+
const courtyardCenterX = (minX + maxX) / 2;
|
|
3372
|
+
const courtyardCenterY = (minY + maxY) / 2;
|
|
3373
|
+
return {
|
|
3374
|
+
offsetFromCenter: {
|
|
3375
|
+
x: courtyardCenterX - componentCenter.x,
|
|
3376
|
+
y: courtyardCenterY - componentCenter.y
|
|
3377
|
+
},
|
|
3378
|
+
width: maxX - minX,
|
|
3379
|
+
height: maxY - minY
|
|
3380
|
+
};
|
|
3381
|
+
};
|
|
3237
3382
|
var buildPackedComponent = (pcbComponents, componentId, db, getNetworkId, shouldAddInnerObstacles, sourcePortToPadIds = /* @__PURE__ */ new Map(), chipMarginsMap = {}, isStatic = false) => {
|
|
3238
3383
|
const padInfos = pcbComponents.flatMap((pc) => {
|
|
3239
3384
|
const pads2 = extractPadInfos(pc, db, getNetworkId);
|
|
@@ -3292,12 +3437,18 @@ var buildPackedComponent = (pcbComponents, componentId, db, getNetworkId, should
|
|
|
3292
3437
|
};
|
|
3293
3438
|
pads.push(innerPad);
|
|
3294
3439
|
}
|
|
3440
|
+
const courtyard = extractCourtyardForComponent({
|
|
3441
|
+
db,
|
|
3442
|
+
pcbComponentIds: pcbComponents.map((pc) => pc.pcb_component_id),
|
|
3443
|
+
componentCenter: center
|
|
3444
|
+
});
|
|
3295
3445
|
return {
|
|
3296
3446
|
componentId,
|
|
3297
3447
|
isStatic,
|
|
3298
3448
|
center,
|
|
3299
3449
|
ccwRotationOffset: 0,
|
|
3300
|
-
pads
|
|
3450
|
+
pads,
|
|
3451
|
+
courtyard
|
|
3301
3452
|
};
|
|
3302
3453
|
};
|
|
3303
3454
|
var collectPcbComponents = (node, db) => {
|
|
@@ -3474,7 +3625,8 @@ var convertPackOutputToPackInput = (packed) => {
|
|
|
3474
3625
|
componentId: pc.componentId,
|
|
3475
3626
|
availableRotationDegrees: pc.availableRotationDegrees,
|
|
3476
3627
|
// Preserve rotation constraints
|
|
3477
|
-
pads: pc.pads.map(({ absoluteCenter: _ac, ...rest }) => rest)
|
|
3628
|
+
pads: pc.pads.map(({ absoluteCenter: _ac, ...rest }) => rest),
|
|
3629
|
+
courtyard: pc.courtyard
|
|
3478
3630
|
}
|
|
3479
3631
|
}));
|
|
3480
3632
|
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.
|
|
5
|
+
"version": "0.0.73",
|
|
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.
|
|
22
|
+
"@tscircuit/circuit-json-util": "^0.0.94",
|
|
23
23
|
"@tscircuit/footprinter": "^0.0.203",
|
|
24
|
-
"@tscircuit/math-utils": "^0.0.
|
|
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.
|
|
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",
|