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 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 bounds = combineBounds(
210
- components.map((c) => getComponentBounds(c, minGap))
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
- this.outputPackedComponent = this.createPackedComponent(
1958
- position,
1959
- rotation
1960
- );
1961
- this.solved = true;
1962
- return;
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: this.createPackedComponent(optimalPosition, rotation),
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.packedComponents.push(newPackedComponent);
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
@@ -2,7 +2,7 @@
2
2
  "name": "calculate-packing",
3
3
  "main": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.0.37",
5
+ "version": "0.0.38",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",