calculate-packing 0.0.37 → 0.0.38
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 +1 -0
- package/dist/index.js +145 -15
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -92,6 +92,7 @@ declare const convertCircuitJsonToPackOutput: (circuitJson: CircuitJson, opts?:
|
|
|
92
92
|
top: number;
|
|
93
93
|
bottom: number;
|
|
94
94
|
}>;
|
|
95
|
+
obstacles?: InputObstacle[];
|
|
95
96
|
}) => PackOutput;
|
|
96
97
|
|
|
97
98
|
declare const getGraphicsFromPackOutput: (packOutput: PackOutput) => GraphicsObject;
|
package/dist/index.js
CHANGED
|
@@ -203,17 +203,49 @@ var createPadPolygons = (component, minGap) => {
|
|
|
203
203
|
return { poly, bbox };
|
|
204
204
|
});
|
|
205
205
|
};
|
|
206
|
+
var createObstaclePolygons = (obstacles, minGap) => {
|
|
207
|
+
return obstacles.map((obs) => {
|
|
208
|
+
const hw = obs.width / 2 + minGap;
|
|
209
|
+
const hh = obs.height / 2 + minGap;
|
|
210
|
+
const cx = obs.absoluteCenter.x;
|
|
211
|
+
const cy = obs.absoluteCenter.y;
|
|
212
|
+
const worldCorners = [
|
|
213
|
+
{ x: cx - hw, y: cy - hh },
|
|
214
|
+
{ x: cx + hw, y: cy - hh },
|
|
215
|
+
{ x: cx + hw, y: cy + hh },
|
|
216
|
+
{ x: cx - hw, y: cy + hh }
|
|
217
|
+
];
|
|
218
|
+
const arr = worldCorners.map(({ x, y }) => [x, y]);
|
|
219
|
+
const poly = new Flatten.Polygon(arr);
|
|
220
|
+
const xs = worldCorners.map((p) => p.x);
|
|
221
|
+
const ys = worldCorners.map((p) => p.y);
|
|
222
|
+
const bbox = {
|
|
223
|
+
minX: Math.min(...xs),
|
|
224
|
+
minY: Math.min(...ys),
|
|
225
|
+
maxX: Math.max(...xs),
|
|
226
|
+
maxY: Math.max(...ys)
|
|
227
|
+
};
|
|
228
|
+
return { poly, bbox };
|
|
229
|
+
});
|
|
230
|
+
};
|
|
206
231
|
var constructOutlinesFromPackedComponents = (components, opts = {}) => {
|
|
207
|
-
const { minGap = 0 } = opts;
|
|
208
|
-
if (components.length === 0) return [];
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
232
|
+
const { minGap = 0, obstacles = [] } = opts;
|
|
233
|
+
if (components.length === 0 && obstacles.length === 0) return [];
|
|
234
|
+
const componentBounds = components.map((c) => getComponentBounds(c, minGap));
|
|
235
|
+
const obstacleBounds = obstacles.map((o) => ({
|
|
236
|
+
minX: o.absoluteCenter.x - o.width / 2 - minGap,
|
|
237
|
+
minY: o.absoluteCenter.y - o.height / 2 - minGap,
|
|
238
|
+
maxX: o.absoluteCenter.x + o.width / 2 + minGap,
|
|
239
|
+
maxY: o.absoluteCenter.y + o.height / 2 + minGap
|
|
240
|
+
}));
|
|
241
|
+
const bounds = combineBounds([...componentBounds, ...obstacleBounds]);
|
|
212
242
|
const allPadShapes = [];
|
|
213
243
|
for (const component of components) {
|
|
214
244
|
const padShapes = createPadPolygons(component, minGap);
|
|
215
245
|
allPadShapes.push(...padShapes);
|
|
216
246
|
}
|
|
247
|
+
const obstacleShapes = createObstaclePolygons(obstacles, minGap);
|
|
248
|
+
allPadShapes.push(...obstacleShapes);
|
|
217
249
|
if (allPadShapes.length === 0) return [];
|
|
218
250
|
const areaOfBox = (b) => Math.max(0, b.maxX - b.minX) * Math.max(0, b.maxY - b.minY);
|
|
219
251
|
const containsBox = (outer, inner, eps = 1e-9) => outer.minX - eps <= inner.minX && outer.minY - eps <= inner.minY && outer.maxX + eps >= inner.maxX && outer.maxY + eps >= inner.maxY;
|
|
@@ -1814,6 +1846,18 @@ var getGraphicsFromPackOutput = (packOutput) => {
|
|
|
1814
1846
|
)
|
|
1815
1847
|
);
|
|
1816
1848
|
const colorMap = createColorMapFromStrings(allNetworkIds);
|
|
1849
|
+
if (packOutput.obstacles && packOutput.obstacles.length > 0) {
|
|
1850
|
+
for (const obstacle of packOutput.obstacles) {
|
|
1851
|
+
rects.push({
|
|
1852
|
+
center: { x: obstacle.absoluteCenter.x, y: obstacle.absoluteCenter.y },
|
|
1853
|
+
width: obstacle.width,
|
|
1854
|
+
height: obstacle.height,
|
|
1855
|
+
fill: "rgba(0,0,0,0.1)",
|
|
1856
|
+
stroke: "#555",
|
|
1857
|
+
label: obstacle.obstacleId
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1817
1861
|
for (const component of packOutput.components) {
|
|
1818
1862
|
const bounds = getComponentBounds(component);
|
|
1819
1863
|
const width = bounds.maxX - bounds.minX;
|
|
@@ -1900,6 +1944,7 @@ function checkOverlapWithPackedComponents({
|
|
|
1900
1944
|
}
|
|
1901
1945
|
|
|
1902
1946
|
// lib/SingleComponentPackSolver/SingleComponentPackSolver.ts
|
|
1947
|
+
import { computeDistanceBetweenBoxes as computeDistanceBetweenBoxes2 } from "@tscircuit/math-utils";
|
|
1903
1948
|
var SingleComponentPackSolver = class extends BaseSolver {
|
|
1904
1949
|
componentToPack;
|
|
1905
1950
|
packedComponents;
|
|
@@ -1954,17 +1999,34 @@ var SingleComponentPackSolver = class extends BaseSolver {
|
|
|
1954
1999
|
const availableRotations2 = this.componentToPack.availableRotationDegrees ?? [0, 90, 180, 270];
|
|
1955
2000
|
const position = { x: 0, y: 0 };
|
|
1956
2001
|
const rotation = availableRotations2[0] ?? 0;
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2002
|
+
const candidate = this.createPackedComponent(position, rotation);
|
|
2003
|
+
const tooCloseToObstacles = (this.obstacles ?? []).some((obs) => {
|
|
2004
|
+
const obsBox = {
|
|
2005
|
+
center: { x: obs.absoluteCenter.x, y: obs.absoluteCenter.y },
|
|
2006
|
+
width: obs.width,
|
|
2007
|
+
height: obs.height
|
|
2008
|
+
};
|
|
2009
|
+
return candidate.pads.some((p) => {
|
|
2010
|
+
const padBox = {
|
|
2011
|
+
center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
|
|
2012
|
+
width: p.size.x,
|
|
2013
|
+
height: p.size.y
|
|
2014
|
+
};
|
|
2015
|
+
const { distance } = computeDistanceBetweenBoxes2(padBox, obsBox);
|
|
2016
|
+
return distance + 1e-6 < this.minGap;
|
|
2017
|
+
});
|
|
2018
|
+
});
|
|
2019
|
+
if (!tooCloseToObstacles) {
|
|
2020
|
+
this.outputPackedComponent = candidate;
|
|
2021
|
+
this.solved = true;
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
1963
2024
|
}
|
|
1964
2025
|
this.outlines = constructOutlinesFromPackedComponents(
|
|
1965
2026
|
this.packedComponents,
|
|
1966
2027
|
{
|
|
1967
|
-
minGap: this.minGap
|
|
2028
|
+
minGap: this.minGap,
|
|
2029
|
+
obstacles: this.obstacles
|
|
1968
2030
|
}
|
|
1969
2031
|
);
|
|
1970
2032
|
const availableRotations = this.componentToPack.availableRotationDegrees ?? [0, 90, 180, 270];
|
|
@@ -1994,11 +2056,33 @@ var SingleComponentPackSolver = class extends BaseSolver {
|
|
|
1994
2056
|
let optimalPosition;
|
|
1995
2057
|
if (this.activeSubSolver.solved && this.activeSubSolver.optimalPosition) {
|
|
1996
2058
|
optimalPosition = this.activeSubSolver.optimalPosition;
|
|
2059
|
+
const candidateComponent = this.createPackedComponent(
|
|
2060
|
+
optimalPosition,
|
|
2061
|
+
rotation
|
|
2062
|
+
);
|
|
1997
2063
|
const { hasOverlap, gapDistance } = checkOverlapWithPackedComponents({
|
|
1998
|
-
component:
|
|
2064
|
+
component: candidateComponent,
|
|
1999
2065
|
packedComponents: this.packedComponents,
|
|
2000
2066
|
minGap: this.minGap
|
|
2001
2067
|
});
|
|
2068
|
+
let minObstacleGapDistance = Infinity;
|
|
2069
|
+
const tooCloseToObstacles = (this.obstacles ?? []).some((obs) => {
|
|
2070
|
+
const obsBox = {
|
|
2071
|
+
center: { x: obs.absoluteCenter.x, y: obs.absoluteCenter.y },
|
|
2072
|
+
width: obs.width,
|
|
2073
|
+
height: obs.height
|
|
2074
|
+
};
|
|
2075
|
+
return candidateComponent.pads.some((p) => {
|
|
2076
|
+
const padBox = {
|
|
2077
|
+
center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
|
|
2078
|
+
width: p.size.x,
|
|
2079
|
+
height: p.size.y
|
|
2080
|
+
};
|
|
2081
|
+
const { distance: distance2 } = computeDistanceBetweenBoxes2(padBox, obsBox);
|
|
2082
|
+
minObstacleGapDistance = Math.min(minObstacleGapDistance, distance2);
|
|
2083
|
+
return distance2 + 1e-6 < this.minGap;
|
|
2084
|
+
});
|
|
2085
|
+
});
|
|
2002
2086
|
distance = this.calculateDistance(optimalPosition, rotation);
|
|
2003
2087
|
if (hasOverlap) {
|
|
2004
2088
|
this.rejectedCandidates.push({
|
|
@@ -2010,6 +2094,16 @@ var SingleComponentPackSolver = class extends BaseSolver {
|
|
|
2010
2094
|
rotationIndex: this.currentRotationIndex,
|
|
2011
2095
|
gapDistance
|
|
2012
2096
|
});
|
|
2097
|
+
} else if (tooCloseToObstacles) {
|
|
2098
|
+
this.rejectedCandidates.push({
|
|
2099
|
+
segment: queuedSegment.segment,
|
|
2100
|
+
rotation,
|
|
2101
|
+
optimalPosition,
|
|
2102
|
+
distance,
|
|
2103
|
+
segmentIndex: queuedSegment.segmentIndex,
|
|
2104
|
+
rotationIndex: this.currentRotationIndex,
|
|
2105
|
+
gapDistance: minObstacleGapDistance
|
|
2106
|
+
});
|
|
2013
2107
|
} else {
|
|
2014
2108
|
this.candidateResults.push({
|
|
2015
2109
|
segment: queuedSegment.segment,
|
|
@@ -2242,6 +2336,7 @@ gap_distance=${candidate.gapDistance}`,
|
|
|
2242
2336
|
};
|
|
2243
2337
|
|
|
2244
2338
|
// lib/PackSolver2/PackSolver2.ts
|
|
2339
|
+
import { computeDistanceBetweenBoxes as computeDistanceBetweenBoxes3 } from "@tscircuit/math-utils";
|
|
2245
2340
|
var PackSolver2 = class extends BaseSolver {
|
|
2246
2341
|
packInput;
|
|
2247
2342
|
unpackedComponentQueue = [];
|
|
@@ -2275,7 +2370,41 @@ var PackSolver2 = class extends BaseSolver {
|
|
|
2275
2370
|
}))
|
|
2276
2371
|
};
|
|
2277
2372
|
setPackedComponentPadCenters(newPackedComponent);
|
|
2278
|
-
this.
|
|
2373
|
+
const obstacles = this.packInput.obstacles ?? [];
|
|
2374
|
+
const tooCloseToObstacles = obstacles.some((obs) => {
|
|
2375
|
+
const obsBox = {
|
|
2376
|
+
center: { x: obs.absoluteCenter.x, y: obs.absoluteCenter.y },
|
|
2377
|
+
width: obs.width,
|
|
2378
|
+
height: obs.height
|
|
2379
|
+
};
|
|
2380
|
+
return newPackedComponent.pads.some((p) => {
|
|
2381
|
+
const padBox = {
|
|
2382
|
+
center: { x: p.absoluteCenter.x, y: p.absoluteCenter.y },
|
|
2383
|
+
width: p.size.x,
|
|
2384
|
+
height: p.size.y
|
|
2385
|
+
};
|
|
2386
|
+
const { distance } = computeDistanceBetweenBoxes3(padBox, obsBox);
|
|
2387
|
+
return distance + 1e-6 < this.packInput.minGap;
|
|
2388
|
+
});
|
|
2389
|
+
});
|
|
2390
|
+
if (!tooCloseToObstacles) {
|
|
2391
|
+
this.packedComponents.push(newPackedComponent);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const fallbackSolver = new SingleComponentPackSolver({
|
|
2395
|
+
packedComponents: [],
|
|
2396
|
+
componentToPack: firstComponentToPack,
|
|
2397
|
+
packPlacementStrategy: this.packInput.packPlacementStrategy,
|
|
2398
|
+
minGap: this.packInput.minGap,
|
|
2399
|
+
obstacles
|
|
2400
|
+
});
|
|
2401
|
+
fallbackSolver.solve();
|
|
2402
|
+
const result = fallbackSolver.getResult();
|
|
2403
|
+
if (result) {
|
|
2404
|
+
this.packedComponents.push(result);
|
|
2405
|
+
} else {
|
|
2406
|
+
this.packedComponents.push(newPackedComponent);
|
|
2407
|
+
}
|
|
2279
2408
|
}
|
|
2280
2409
|
_step() {
|
|
2281
2410
|
if (this.solved || this.failed) return;
|
|
@@ -2612,7 +2741,8 @@ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
|
|
|
2612
2741
|
components: [],
|
|
2613
2742
|
minGap: 0,
|
|
2614
2743
|
packOrderStrategy: "largest_to_smallest",
|
|
2615
|
-
packPlacementStrategy: "shortest_connection_along_outline"
|
|
2744
|
+
packPlacementStrategy: "shortest_connection_along_outline",
|
|
2745
|
+
obstacles: opts.obstacles
|
|
2616
2746
|
};
|
|
2617
2747
|
const tree = getCircuitJsonTree(circuitJson, {
|
|
2618
2748
|
source_group_id: opts.source_group_id
|
package/package.json
CHANGED