calculate-packing 0.0.46 → 0.0.48

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
@@ -409,6 +409,7 @@ declare class OutlineSegmentCandidatePointSolver extends BaseSolver {
409
409
  private createTemporaryPackedComponent;
410
410
  /**
411
411
  * Adjust position to avoid component bounds crossing to the inside of the outline
412
+ * and ensure the component stays within the boundary outline
412
413
  */
413
414
  private adjustPositionForOutlineCollision;
414
415
  visualize(): GraphicsObject;
package/dist/index.js CHANGED
@@ -1403,6 +1403,26 @@ function normalizeVector(direction) {
1403
1403
  };
1404
1404
  }
1405
1405
 
1406
+ // lib/math/isPointInPolygon.ts
1407
+ function isPointInPolygon(point, polygon) {
1408
+ if (polygon.length < 3) return false;
1409
+ let inside = false;
1410
+ const n = polygon.length;
1411
+ const poly = [...polygon];
1412
+ if (poly[0].x !== poly[n - 1].x || poly[0].y !== poly[n - 1].y) {
1413
+ poly.push({ ...poly[0] });
1414
+ }
1415
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
1416
+ const xi = poly[i].x;
1417
+ const yi = poly[i].y;
1418
+ const xj = poly[j].x;
1419
+ const yj = poly[j].y;
1420
+ const intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
1421
+ if (intersect) inside = !inside;
1422
+ }
1423
+ return inside;
1424
+ }
1425
+
1406
1426
  // lib/OutlineSegmentCandidatePointSolver/OutlineSegmentCandidatePointSolver.ts
1407
1427
  var OutlineSegmentCandidatePointSolver = class extends BaseSolver {
1408
1428
  outlineSegment;
@@ -1511,12 +1531,30 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver {
1511
1531
  Math.sign(this.outlineSegment[1].y - this.outlineSegment[0].y)
1512
1532
  )
1513
1533
  };
1514
- const viableBounds = {
1534
+ let viableBounds = {
1515
1535
  minX: largestRectBounds.minX - componentBounds.minX * segmentNormAbs.x,
1516
1536
  minY: largestRectBounds.minY - componentBounds.minY * segmentNormAbs.y,
1517
1537
  maxX: largestRectBounds.maxX - componentBounds.maxX * segmentNormAbs.x,
1518
1538
  maxY: largestRectBounds.maxY - componentBounds.maxY * segmentNormAbs.y
1519
1539
  };
1540
+ if (this.boundaryOutline && this.boundaryOutline.length >= 3) {
1541
+ let boundaryMinX = Infinity;
1542
+ let boundaryMinY = Infinity;
1543
+ let boundaryMaxX = -Infinity;
1544
+ let boundaryMaxY = -Infinity;
1545
+ for (const point of this.boundaryOutline) {
1546
+ boundaryMinX = Math.min(boundaryMinX, point.x);
1547
+ boundaryMinY = Math.min(boundaryMinY, point.y);
1548
+ boundaryMaxX = Math.max(boundaryMaxX, point.x);
1549
+ boundaryMaxY = Math.max(boundaryMaxY, point.y);
1550
+ }
1551
+ viableBounds = {
1552
+ minX: Math.max(viableBounds.minX, boundaryMinX - componentBounds.minX),
1553
+ minY: Math.max(viableBounds.minY, boundaryMinY - componentBounds.minY),
1554
+ maxX: Math.min(viableBounds.maxX, boundaryMaxX - componentBounds.maxX),
1555
+ maxY: Math.min(viableBounds.maxY, boundaryMaxY - componentBounds.maxY)
1556
+ };
1557
+ }
1520
1558
  this.viableBounds = viableBounds;
1521
1559
  const viableBoundsWidth = viableBounds.maxX - viableBounds.minX;
1522
1560
  const viableBoundsHeight = viableBounds.maxY - viableBounds.minY;
@@ -1676,6 +1714,7 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver {
1676
1714
  }
1677
1715
  /**
1678
1716
  * Adjust position to avoid component bounds crossing to the inside of the outline
1717
+ * and ensure the component stays within the boundary outline
1679
1718
  */
1680
1719
  adjustPositionForOutlineCollision(center) {
1681
1720
  const tempComponent = this.createTemporaryPackedComponent(center);
@@ -1686,37 +1725,66 @@ var OutlineSegmentCandidatePointSolver = class extends BaseSolver {
1686
1725
  );
1687
1726
  const isHorizontalNormal = Math.abs(outwardNormal.x) > Math.abs(outwardNormal.y);
1688
1727
  const isVerticalNormal = !isHorizontalNormal;
1728
+ let adjustedCenter = center;
1689
1729
  if (isHorizontalNormal) {
1690
1730
  const isXPlusFacing = outwardNormal.x > 0;
1691
1731
  const isXMinusFacing = !isXPlusFacing;
1692
1732
  if (isXPlusFacing) {
1693
- return {
1733
+ adjustedCenter = {
1694
1734
  x: bounds.maxX,
1695
1735
  y: center.y
1696
1736
  };
1697
1737
  } else if (isXMinusFacing) {
1698
- return {
1738
+ adjustedCenter = {
1699
1739
  x: bounds.minX,
1700
1740
  y: center.y
1701
1741
  };
1702
1742
  }
1703
- }
1704
- if (isVerticalNormal) {
1743
+ } else if (isVerticalNormal) {
1705
1744
  const isYPlusFacing = outwardNormal.y > 0;
1706
1745
  const isYMinusFacing = !isYPlusFacing;
1707
1746
  if (isYPlusFacing) {
1708
- return {
1747
+ adjustedCenter = {
1709
1748
  x: center.x,
1710
1749
  y: bounds.maxY
1711
1750
  };
1712
1751
  } else if (isYMinusFacing) {
1713
- return {
1752
+ adjustedCenter = {
1714
1753
  x: center.x,
1715
1754
  y: bounds.minY
1716
1755
  };
1717
1756
  }
1718
1757
  }
1719
- throw new Error("unreachable");
1758
+ if (this.boundaryOutline && this.boundaryOutline.length >= 3) {
1759
+ const adjustedComponent = this.createTemporaryPackedComponent(adjustedCenter);
1760
+ const adjustedBounds = getComponentBounds(adjustedComponent, 0);
1761
+ const allPadsInside = adjustedComponent.pads.every(
1762
+ (pad) => isPointInPolygon(pad.absoluteCenter, this.boundaryOutline)
1763
+ );
1764
+ const cornersInside = [
1765
+ { x: adjustedBounds.minX, y: adjustedBounds.minY },
1766
+ { x: adjustedBounds.minX, y: adjustedBounds.maxY },
1767
+ { x: adjustedBounds.maxX, y: adjustedBounds.minY },
1768
+ { x: adjustedBounds.maxX, y: adjustedBounds.maxY }
1769
+ ].every((corner) => isPointInPolygon(corner, this.boundaryOutline));
1770
+ if (!allPadsInside || !cornersInside) {
1771
+ if (this.viableBounds) {
1772
+ adjustedCenter = {
1773
+ x: clamp(
1774
+ adjustedCenter.x,
1775
+ this.viableBounds.minX,
1776
+ this.viableBounds.maxX
1777
+ ),
1778
+ y: clamp(
1779
+ adjustedCenter.y,
1780
+ this.viableBounds.minY,
1781
+ this.viableBounds.maxY
1782
+ )
1783
+ };
1784
+ }
1785
+ }
1786
+ }
1787
+ return adjustedCenter;
1720
1788
  }
1721
1789
  visualize() {
1722
1790
  const graphics = {
@@ -2128,6 +2196,20 @@ var SingleComponentPackSolver = class extends BaseSolver {
2128
2196
  return distance2 + 1e-6 < this.minGap;
2129
2197
  });
2130
2198
  });
2199
+ let outsideBoundaryOutline = false;
2200
+ if (this.boundaryOutline && this.boundaryOutline.length >= 3) {
2201
+ const componentBounds = getComponentBounds(candidateComponent, 0);
2202
+ const allPadsInside = candidateComponent.pads.every(
2203
+ (pad) => isPointInPolygon(pad.absoluteCenter, this.boundaryOutline)
2204
+ );
2205
+ const cornersInside = [
2206
+ { x: componentBounds.minX, y: componentBounds.minY },
2207
+ { x: componentBounds.minX, y: componentBounds.maxY },
2208
+ { x: componentBounds.maxX, y: componentBounds.minY },
2209
+ { x: componentBounds.maxX, y: componentBounds.maxY }
2210
+ ].every((corner) => isPointInPolygon(corner, this.boundaryOutline));
2211
+ outsideBoundaryOutline = !allPadsInside || !cornersInside;
2212
+ }
2131
2213
  distance = this.calculateDistance(optimalPosition, rotation);
2132
2214
  if (hasOverlap) {
2133
2215
  this.rejectedCandidates.push({
@@ -2149,6 +2231,17 @@ var SingleComponentPackSolver = class extends BaseSolver {
2149
2231
  rotationIndex: this.currentRotationIndex,
2150
2232
  gapDistance: minObstacleGapDistance
2151
2233
  });
2234
+ } else if (outsideBoundaryOutline) {
2235
+ this.rejectedCandidates.push({
2236
+ segment: queuedSegment.segment,
2237
+ rotation,
2238
+ optimalPosition,
2239
+ distance,
2240
+ segmentIndex: queuedSegment.segmentIndex,
2241
+ rotationIndex: this.currentRotationIndex,
2242
+ gapDistance: -1
2243
+ // Special marker for boundary violation
2244
+ });
2152
2245
  } else {
2153
2246
  this.candidateResults.push({
2154
2247
  segment: queuedSegment.segment,
@@ -2853,6 +2946,12 @@ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
2853
2946
  });
2854
2947
  const db = cju(circuitJson);
2855
2948
  let unnamedCounter = 0;
2949
+ const pcbBoard = circuitJson.find(
2950
+ (item) => item.type === "pcb_board"
2951
+ );
2952
+ if (pcbBoard && pcbBoard.outline) {
2953
+ packOutput.boundaryOutline = pcbBoard.outline;
2954
+ }
2856
2955
  const getNetworkId = (pcbPortId) => {
2857
2956
  if (pcbPortId) {
2858
2957
  const pcbPort = db.pcb_port.get(pcbPortId);
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.46",
5
+ "version": "0.0.48",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",