@tscircuit/rectdiff 0.0.24 → 0.0.26
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/.github/workflows/bun-pver-release.yml +45 -24
- package/AGENTS.md +23 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +414 -191
- package/lib/RectDiffPipeline.ts +23 -0
- package/lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts +311 -0
- package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +23 -23
- package/lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts +9 -10
- package/lib/solvers/RectDiffSeedingSolver/computeEdgeCandidates3D.ts +22 -19
- package/lib/solvers/RectDiffSeedingSolver/longestFreeSpanAroundZ.ts +1 -1
- package/lib/types/srj-types.ts +1 -0
- package/lib/utils/expandRectFromSeed.ts +8 -10
- package/lib/utils/isFullyOccupiedAtPoint.ts +2 -2
- package/lib/utils/rectdiff-geometry.ts +13 -20
- package/package.json +3 -1
- package/pages/pour.page.tsx +18 -0
- package/scripts/benchmark-slow-problem.ts +94 -0
- package/test-assets/bugreport49-634662.json +412 -0
- package/test-assets/keyboard4.json +16165 -0
- package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +1 -1
- package/tests/solver/bugreport03-fe4a17/__snapshots__/bugreport03-fe4a17.snap.svg +1 -1
- package/tests/solver/bugreport08-e3ec95/__snapshots__/bugreport08-e3ec95.snap.svg +1 -1
- package/tests/solver/bugreport09-618e09/__snapshots__/bugreport09-618e09.snap.svg +1 -1
- package/tests/solver/bugreport10-71239a/__snapshots__/bugreport10-71239a.snap.svg +1 -1
- package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c.snap.svg +1 -1
- package/tests/solver/bugreport22-2a75ce/__snapshots__/bugreport22-2a75ce.snap.svg +1 -1
- package/tests/solver/bugreport24-05597c/__snapshots__/bugreport24-05597c.snap.svg +1 -1
- package/tests/solver/bugreport35-191db9/__snapshots__/bugreport35-191db9.snap.svg +1 -1
- package/tests/solver/bugreport36-bf8303/__snapshots__/bugreport36-bf8303.snap.svg +1 -1
- package/tests/solver/bugreport49-634662/__snapshots__/bugreport49-634662.snap.svg +44 -0
- package/tests/solver/bugreport49-634662/bugreport49-634662.test.ts +134 -0
package/dist/index.js
CHANGED
|
@@ -202,6 +202,7 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver {
|
|
|
202
202
|
}
|
|
203
203
|
this.edgeSpatialIndex.finish();
|
|
204
204
|
}
|
|
205
|
+
input;
|
|
205
206
|
allEdges;
|
|
206
207
|
unprocessedEdges = [];
|
|
207
208
|
segmentsWithAdjacentEmptySpace = [];
|
|
@@ -361,6 +362,7 @@ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver2 {
|
|
|
361
362
|
}))
|
|
362
363
|
);
|
|
363
364
|
}
|
|
365
|
+
input;
|
|
364
366
|
unprocessedSegments = [];
|
|
365
367
|
expandedSegments = [];
|
|
366
368
|
lastSegment = null;
|
|
@@ -665,15 +667,98 @@ var GapFillSolverPipeline = class extends BasePipelineSolver {
|
|
|
665
667
|
}
|
|
666
668
|
};
|
|
667
669
|
|
|
668
|
-
// lib/solvers/
|
|
669
|
-
import {
|
|
670
|
-
BasePipelineSolver as BasePipelineSolver2,
|
|
671
|
-
definePipelineStep as definePipelineStep2
|
|
672
|
-
} from "@tscircuit/solver-utils";
|
|
673
|
-
|
|
674
|
-
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
670
|
+
// lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts
|
|
675
671
|
import { BaseSolver as BaseSolver3 } from "@tscircuit/solver-utils";
|
|
676
672
|
|
|
673
|
+
// lib/solvers/RectDiffSeedingSolver/layers.ts
|
|
674
|
+
function layerSortKey(n) {
|
|
675
|
+
const L = n.toLowerCase();
|
|
676
|
+
if (L === "top") return -1e6;
|
|
677
|
+
if (L === "bottom") return 1e6;
|
|
678
|
+
const m = /^inner(\d+)$/i.exec(L);
|
|
679
|
+
if (m) return parseInt(m[1], 10) || 0;
|
|
680
|
+
return 100 + L.charCodeAt(0);
|
|
681
|
+
}
|
|
682
|
+
function canonicalizeLayerOrder(names) {
|
|
683
|
+
return Array.from(new Set(names)).sort((a, b) => {
|
|
684
|
+
const ka = layerSortKey(a);
|
|
685
|
+
const kb = layerSortKey(b);
|
|
686
|
+
if (ka !== kb) return ka - kb;
|
|
687
|
+
return a.localeCompare(b);
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
function buildZIndexMap(params) {
|
|
691
|
+
const names = canonicalizeLayerOrder(
|
|
692
|
+
(params.obstacles ?? []).flatMap((o) => o.layers ?? [])
|
|
693
|
+
);
|
|
694
|
+
const declaredLayerCount = Math.max(1, params.layerCount || names.length || 1);
|
|
695
|
+
const fallback = Array.from(
|
|
696
|
+
{ length: declaredLayerCount },
|
|
697
|
+
(_, i) => i === 0 ? "top" : i === declaredLayerCount - 1 ? "bottom" : `inner${i}`
|
|
698
|
+
);
|
|
699
|
+
const ordered = [];
|
|
700
|
+
const seen = /* @__PURE__ */ new Set();
|
|
701
|
+
const push = (n) => {
|
|
702
|
+
const key = n.toLowerCase();
|
|
703
|
+
if (seen.has(key)) return;
|
|
704
|
+
seen.add(key);
|
|
705
|
+
ordered.push(n);
|
|
706
|
+
};
|
|
707
|
+
fallback.forEach(push);
|
|
708
|
+
names.forEach(push);
|
|
709
|
+
const layerNames = ordered.slice(0, declaredLayerCount);
|
|
710
|
+
const clampIndex = (nameLower) => {
|
|
711
|
+
if (layerNames.length <= 1) return 0;
|
|
712
|
+
if (nameLower === "top") return 0;
|
|
713
|
+
if (nameLower === "bottom") return layerNames.length - 1;
|
|
714
|
+
const m = /^inner(\d+)$/i.exec(nameLower);
|
|
715
|
+
if (m) {
|
|
716
|
+
if (layerNames.length <= 2) return layerNames.length - 1;
|
|
717
|
+
const parsed = parseInt(m[1], 10);
|
|
718
|
+
const maxInner = layerNames.length - 2;
|
|
719
|
+
const clampedInner = Math.min(
|
|
720
|
+
maxInner,
|
|
721
|
+
Math.max(1, Number.isFinite(parsed) ? parsed : 1)
|
|
722
|
+
);
|
|
723
|
+
return clampedInner;
|
|
724
|
+
}
|
|
725
|
+
return 0;
|
|
726
|
+
};
|
|
727
|
+
const map = /* @__PURE__ */ new Map();
|
|
728
|
+
layerNames.forEach((n, i) => map.set(n.toLowerCase(), i));
|
|
729
|
+
ordered.slice(layerNames.length).forEach((n) => {
|
|
730
|
+
const key = n.toLowerCase();
|
|
731
|
+
map.set(key, clampIndex(key));
|
|
732
|
+
});
|
|
733
|
+
return { layerNames, zIndexByName: map };
|
|
734
|
+
}
|
|
735
|
+
function obstacleZs(ob, zIndexByName) {
|
|
736
|
+
if (ob.zLayers?.length)
|
|
737
|
+
return Array.from(new Set(ob.zLayers)).sort((a, b) => a - b);
|
|
738
|
+
const fromNames = (ob.layers ?? []).map((n) => zIndexByName.get(n.toLowerCase())).filter((v) => typeof v === "number");
|
|
739
|
+
return Array.from(new Set(fromNames)).sort((a, b) => a - b);
|
|
740
|
+
}
|
|
741
|
+
function obstacleToXYRect(ob) {
|
|
742
|
+
const w = ob.width;
|
|
743
|
+
const h = ob.height;
|
|
744
|
+
if (typeof w !== "number" || typeof h !== "number") return null;
|
|
745
|
+
return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h };
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// lib/utils/getColorForZLayer.ts
|
|
749
|
+
var getColorForZLayer = (zLayers) => {
|
|
750
|
+
const minZ = Math.min(...zLayers);
|
|
751
|
+
const colors = [
|
|
752
|
+
{ fill: "#dbeafe", stroke: "#3b82f6" },
|
|
753
|
+
{ fill: "#fef3c7", stroke: "#f59e0b" },
|
|
754
|
+
{ fill: "#d1fae5", stroke: "#10b981" },
|
|
755
|
+
{ fill: "#e9d5ff", stroke: "#a855f7" },
|
|
756
|
+
{ fill: "#fed7aa", stroke: "#f97316" },
|
|
757
|
+
{ fill: "#fecaca", stroke: "#ef4444" }
|
|
758
|
+
];
|
|
759
|
+
return colors[minZ % colors.length];
|
|
760
|
+
};
|
|
761
|
+
|
|
677
762
|
// lib/utils/rectdiff-geometry.ts
|
|
678
763
|
var EPS4 = 1e-9;
|
|
679
764
|
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
@@ -688,24 +773,18 @@ function containsPoint(r, p) {
|
|
|
688
773
|
return p.x >= r.x - EPS4 && p.x <= r.x + r.width + EPS4 && p.y >= r.y - EPS4 && p.y <= r.y + r.height + EPS4;
|
|
689
774
|
}
|
|
690
775
|
function distancePointToRectEdges(p, r) {
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
t = clamp(t, 0, 1);
|
|
704
|
-
const xx = x1 + t * C;
|
|
705
|
-
const yy = y1 + t * D;
|
|
706
|
-
best = Math.min(best, Math.hypot(p.x - xx, p.y - yy));
|
|
707
|
-
}
|
|
708
|
-
return best;
|
|
776
|
+
const minX = r.x;
|
|
777
|
+
const maxX = r.x + r.width;
|
|
778
|
+
const minY = r.y;
|
|
779
|
+
const maxY = r.y + r.height;
|
|
780
|
+
if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {
|
|
781
|
+
return Math.min(p.x - minX, maxX - p.x, p.y - minY, maxY - p.y);
|
|
782
|
+
}
|
|
783
|
+
const dx = p.x < minX ? minX - p.x : p.x > maxX ? p.x - maxX : 0;
|
|
784
|
+
const dy = p.y < minY ? minY - p.y : p.y > maxY ? p.y - maxY : 0;
|
|
785
|
+
if (dx === 0) return dy;
|
|
786
|
+
if (dy === 0) return dx;
|
|
787
|
+
return Math.hypot(dx, dy);
|
|
709
788
|
}
|
|
710
789
|
function intersect1D(r1, r2) {
|
|
711
790
|
const lo = Math.max(r1[0], r2[0]);
|
|
@@ -736,6 +815,235 @@ function subtractRect2D(A, B) {
|
|
|
736
815
|
return out.filter((r) => r.width > EPS4 && r.height > EPS4);
|
|
737
816
|
}
|
|
738
817
|
|
|
818
|
+
// lib/utils/padRect.ts
|
|
819
|
+
var padRect = (rect, clearance) => {
|
|
820
|
+
if (!clearance || clearance <= 0) return rect;
|
|
821
|
+
return {
|
|
822
|
+
x: rect.x - clearance,
|
|
823
|
+
y: rect.y - clearance,
|
|
824
|
+
width: rect.width + 2 * clearance,
|
|
825
|
+
height: rect.height + 2 * clearance
|
|
826
|
+
};
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
// lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts
|
|
830
|
+
var nodeToRect = (node) => ({
|
|
831
|
+
x: node.center.x - node.width / 2,
|
|
832
|
+
y: node.center.y - node.height / 2,
|
|
833
|
+
width: node.width,
|
|
834
|
+
height: node.height
|
|
835
|
+
});
|
|
836
|
+
var rectArea = (rect) => rect.width * rect.height;
|
|
837
|
+
var cloneNode = (node) => ({
|
|
838
|
+
...node,
|
|
839
|
+
center: { ...node.center },
|
|
840
|
+
availableZ: [...node.availableZ]
|
|
841
|
+
});
|
|
842
|
+
var cloneNodeWithRect = (node, rect, capacityMeshNodeId) => ({
|
|
843
|
+
...node,
|
|
844
|
+
capacityMeshNodeId,
|
|
845
|
+
center: {
|
|
846
|
+
x: rect.x + rect.width / 2,
|
|
847
|
+
y: rect.y + rect.height / 2
|
|
848
|
+
},
|
|
849
|
+
width: rect.width,
|
|
850
|
+
height: rect.height,
|
|
851
|
+
availableZ: [...node.availableZ],
|
|
852
|
+
layer: `z${node.availableZ.join(",")}`
|
|
853
|
+
});
|
|
854
|
+
var isFreeNode = (node) => !node._containsObstacle && !node._containsTarget;
|
|
855
|
+
var isSingletonOuterNode = (node, outerZ) => node.availableZ.length === 1 && node.availableZ[0] === outerZ;
|
|
856
|
+
var sameRect = (a, b) => Math.abs(a.x - b.x) <= EPS4 && Math.abs(a.y - b.y) <= EPS4 && Math.abs(a.width - b.width) <= EPS4 && Math.abs(a.height - b.height) <= EPS4;
|
|
857
|
+
var subtractRects = (target, cutters) => {
|
|
858
|
+
let remaining = [target];
|
|
859
|
+
for (const cutter of cutters) {
|
|
860
|
+
if (remaining.length === 0) return remaining;
|
|
861
|
+
const nextRemaining = [];
|
|
862
|
+
for (const piece of remaining) {
|
|
863
|
+
nextRemaining.push(...subtractRect2D(piece, cutter));
|
|
864
|
+
}
|
|
865
|
+
remaining = nextRemaining;
|
|
866
|
+
}
|
|
867
|
+
return remaining;
|
|
868
|
+
};
|
|
869
|
+
var isFullyCoveredByRects = (target, coveringRects) => {
|
|
870
|
+
return subtractRects(target, coveringRects).length === 0;
|
|
871
|
+
};
|
|
872
|
+
var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
873
|
+
constructor(input) {
|
|
874
|
+
super();
|
|
875
|
+
this.input = input;
|
|
876
|
+
}
|
|
877
|
+
input;
|
|
878
|
+
outputNodes = [];
|
|
879
|
+
promotedNodeIds = /* @__PURE__ */ new Set();
|
|
880
|
+
residualNodeIds = /* @__PURE__ */ new Set();
|
|
881
|
+
_setup() {
|
|
882
|
+
this.outputNodes = this.input.meshNodes.map(cloneNode);
|
|
883
|
+
this.promotedNodeIds.clear();
|
|
884
|
+
this.residualNodeIds.clear();
|
|
885
|
+
}
|
|
886
|
+
_step() {
|
|
887
|
+
this.outputNodes = this.processOuterLayerContainmentMerges();
|
|
888
|
+
this.solved = true;
|
|
889
|
+
}
|
|
890
|
+
processOuterLayerContainmentMerges() {
|
|
891
|
+
const srj = this.input.simpleRouteJson;
|
|
892
|
+
const layerCount = Math.max(1, srj.layerCount || 1);
|
|
893
|
+
if (layerCount < 3) {
|
|
894
|
+
return this.input.meshNodes.map(cloneNode);
|
|
895
|
+
}
|
|
896
|
+
const topZ = 0;
|
|
897
|
+
const bottomZ = layerCount - 1;
|
|
898
|
+
const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0);
|
|
899
|
+
const originalNodes = this.input.meshNodes.map(cloneNode);
|
|
900
|
+
const obstaclesByLayer = this.buildObstaclesByLayer(layerCount);
|
|
901
|
+
const mutableOuterNodes = originalNodes.filter(
|
|
902
|
+
(node) => isFreeNode(node) && (isSingletonOuterNode(node, topZ) || isSingletonOuterNode(node, bottomZ))
|
|
903
|
+
);
|
|
904
|
+
const immutableNodes = originalNodes.filter(
|
|
905
|
+
(node) => !mutableOuterNodes.includes(node)
|
|
906
|
+
);
|
|
907
|
+
const freeSupportRectsByOuterLayer = /* @__PURE__ */ new Map();
|
|
908
|
+
freeSupportRectsByOuterLayer.set(
|
|
909
|
+
topZ,
|
|
910
|
+
originalNodes.filter((node) => isFreeNode(node) && node.availableZ.includes(topZ)).map(nodeToRect)
|
|
911
|
+
);
|
|
912
|
+
freeSupportRectsByOuterLayer.set(
|
|
913
|
+
bottomZ,
|
|
914
|
+
originalNodes.filter((node) => isFreeNode(node) && node.availableZ.includes(bottomZ)).map(nodeToRect)
|
|
915
|
+
);
|
|
916
|
+
const promotedNodes = [];
|
|
917
|
+
const promotedRects = [];
|
|
918
|
+
const candidateNodes = mutableOuterNodes.filter(
|
|
919
|
+
(node) => node.width + EPS4 >= viaMinSize && node.height + EPS4 >= viaMinSize
|
|
920
|
+
).sort((a, b) => rectArea(nodeToRect(b)) - rectArea(nodeToRect(a)));
|
|
921
|
+
for (const candidate of candidateNodes) {
|
|
922
|
+
const candidateZ = candidate.availableZ[0];
|
|
923
|
+
const oppositeZ = candidateZ === topZ ? bottomZ : topZ;
|
|
924
|
+
const candidateRect = nodeToRect(candidate);
|
|
925
|
+
const oppositeSupportRects = freeSupportRectsByOuterLayer.get(oppositeZ) ?? [];
|
|
926
|
+
if (!this.isTransitCompatibleAcrossIntermediateLayers({
|
|
927
|
+
rect: candidateRect,
|
|
928
|
+
fromZ: candidateZ,
|
|
929
|
+
toZ: oppositeZ,
|
|
930
|
+
obstaclesByLayer
|
|
931
|
+
})) {
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
if (!isFullyCoveredByRects(candidateRect, oppositeSupportRects)) {
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
promotedNodes.push({
|
|
938
|
+
...candidate,
|
|
939
|
+
availableZ: [topZ, bottomZ],
|
|
940
|
+
layer: `z${topZ},${bottomZ}`
|
|
941
|
+
});
|
|
942
|
+
promotedRects.push(candidateRect);
|
|
943
|
+
this.promotedNodeIds.add(candidate.capacityMeshNodeId);
|
|
944
|
+
}
|
|
945
|
+
let nextResidualId = 0;
|
|
946
|
+
const residualNodes = [];
|
|
947
|
+
for (const node of mutableOuterNodes) {
|
|
948
|
+
if (this.promotedNodeIds.has(node.capacityMeshNodeId)) {
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
const nodeRect = nodeToRect(node);
|
|
952
|
+
const remainingPieces = subtractRects(nodeRect, promotedRects);
|
|
953
|
+
if (remainingPieces.length === 1 && sameRect(remainingPieces[0], nodeRect)) {
|
|
954
|
+
residualNodes.push(node);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
for (const piece of remainingPieces) {
|
|
958
|
+
const residualNode = cloneNodeWithRect(
|
|
959
|
+
node,
|
|
960
|
+
piece,
|
|
961
|
+
`${node.capacityMeshNodeId}-outer-merge-${nextResidualId++}`
|
|
962
|
+
);
|
|
963
|
+
residualNodes.push(residualNode);
|
|
964
|
+
this.residualNodeIds.add(residualNode.capacityMeshNodeId);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return [...immutableNodes, ...promotedNodes, ...residualNodes];
|
|
968
|
+
}
|
|
969
|
+
buildObstaclesByLayer(layerCount) {
|
|
970
|
+
const out = Array.from(
|
|
971
|
+
{ length: layerCount },
|
|
972
|
+
() => []
|
|
973
|
+
);
|
|
974
|
+
for (const obstacle of this.input.simpleRouteJson.obstacles ?? []) {
|
|
975
|
+
const baseRect = obstacleToXYRect(obstacle);
|
|
976
|
+
if (!baseRect) continue;
|
|
977
|
+
const rect = padRect(baseRect, this.input.obstacleClearance ?? 0);
|
|
978
|
+
const zLayers = obstacleZs(obstacle, this.input.zIndexByName);
|
|
979
|
+
for (const z of zLayers) {
|
|
980
|
+
if (z < 0 || z >= layerCount) continue;
|
|
981
|
+
out[z].push({ obstacle, rect });
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return out;
|
|
985
|
+
}
|
|
986
|
+
isTransitCompatibleAcrossIntermediateLayers(params) {
|
|
987
|
+
const { rect, fromZ, toZ, obstaclesByLayer } = params;
|
|
988
|
+
const lo = Math.min(fromZ, toZ);
|
|
989
|
+
const hi = Math.max(fromZ, toZ);
|
|
990
|
+
if (hi - lo < 2) return false;
|
|
991
|
+
for (let z = lo + 1; z < hi; z++) {
|
|
992
|
+
const overlapping = (obstaclesByLayer[z] ?? []).filter(
|
|
993
|
+
(entry) => overlaps(entry.rect, rect)
|
|
994
|
+
);
|
|
995
|
+
if (overlapping.length === 0) return false;
|
|
996
|
+
const nonCopperOverlap = overlapping.some(
|
|
997
|
+
(entry) => !entry.obstacle.isCopperPour
|
|
998
|
+
);
|
|
999
|
+
if (nonCopperOverlap) return false;
|
|
1000
|
+
const copperRects = overlapping.filter((entry) => entry.obstacle.isCopperPour).map((entry) => entry.rect);
|
|
1001
|
+
if (!isFullyCoveredByRects(rect, copperRects)) {
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
getOutput() {
|
|
1008
|
+
return { outputNodes: this.outputNodes };
|
|
1009
|
+
}
|
|
1010
|
+
visualize() {
|
|
1011
|
+
return {
|
|
1012
|
+
title: "OuterLayerContainmentMergeSolver",
|
|
1013
|
+
coordinateSystem: "cartesian",
|
|
1014
|
+
rects: this.outputNodes.map((node) => {
|
|
1015
|
+
const colors = getColorForZLayer(node.availableZ);
|
|
1016
|
+
const isPromoted = this.promotedNodeIds.has(node.capacityMeshNodeId);
|
|
1017
|
+
const isResidual = this.residualNodeIds.has(node.capacityMeshNodeId);
|
|
1018
|
+
return {
|
|
1019
|
+
center: node.center,
|
|
1020
|
+
width: node.width,
|
|
1021
|
+
height: node.height,
|
|
1022
|
+
stroke: isPromoted ? "rgba(22, 163, 74, 0.95)" : isResidual ? "rgba(37, 99, 235, 0.95)" : colors.stroke,
|
|
1023
|
+
fill: node._containsObstacle ? "rgba(239, 68, 68, 0.35)" : isPromoted ? "rgba(34, 197, 94, 0.28)" : isResidual ? "rgba(59, 130, 246, 0.18)" : colors.fill,
|
|
1024
|
+
layer: `z${node.availableZ.join(",")}`,
|
|
1025
|
+
label: [
|
|
1026
|
+
`node ${node.capacityMeshNodeId}`,
|
|
1027
|
+
`z:${node.availableZ.join(",")}`
|
|
1028
|
+
].join("\n")
|
|
1029
|
+
};
|
|
1030
|
+
}),
|
|
1031
|
+
points: [],
|
|
1032
|
+
lines: [],
|
|
1033
|
+
texts: []
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
1039
|
+
import {
|
|
1040
|
+
BasePipelineSolver as BasePipelineSolver2,
|
|
1041
|
+
definePipelineStep as definePipelineStep2
|
|
1042
|
+
} from "@tscircuit/solver-utils";
|
|
1043
|
+
|
|
1044
|
+
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
1045
|
+
import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
|
|
1046
|
+
|
|
739
1047
|
// lib/solvers/RectDiffSeedingSolver/isPointInPolygon.ts
|
|
740
1048
|
function isPointInPolygon(p, polygon) {
|
|
741
1049
|
let inside = false;
|
|
@@ -842,81 +1150,6 @@ function computeInverseRects(bounds, polygon) {
|
|
|
842
1150
|
return mergedVertical;
|
|
843
1151
|
}
|
|
844
1152
|
|
|
845
|
-
// lib/solvers/RectDiffSeedingSolver/layers.ts
|
|
846
|
-
function layerSortKey(n) {
|
|
847
|
-
const L = n.toLowerCase();
|
|
848
|
-
if (L === "top") return -1e6;
|
|
849
|
-
if (L === "bottom") return 1e6;
|
|
850
|
-
const m = /^inner(\d+)$/i.exec(L);
|
|
851
|
-
if (m) return parseInt(m[1], 10) || 0;
|
|
852
|
-
return 100 + L.charCodeAt(0);
|
|
853
|
-
}
|
|
854
|
-
function canonicalizeLayerOrder(names) {
|
|
855
|
-
return Array.from(new Set(names)).sort((a, b) => {
|
|
856
|
-
const ka = layerSortKey(a);
|
|
857
|
-
const kb = layerSortKey(b);
|
|
858
|
-
if (ka !== kb) return ka - kb;
|
|
859
|
-
return a.localeCompare(b);
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
function buildZIndexMap(params) {
|
|
863
|
-
const names = canonicalizeLayerOrder(
|
|
864
|
-
(params.obstacles ?? []).flatMap((o) => o.layers ?? [])
|
|
865
|
-
);
|
|
866
|
-
const declaredLayerCount = Math.max(1, params.layerCount || names.length || 1);
|
|
867
|
-
const fallback = Array.from(
|
|
868
|
-
{ length: declaredLayerCount },
|
|
869
|
-
(_, i) => i === 0 ? "top" : i === declaredLayerCount - 1 ? "bottom" : `inner${i}`
|
|
870
|
-
);
|
|
871
|
-
const ordered = [];
|
|
872
|
-
const seen = /* @__PURE__ */ new Set();
|
|
873
|
-
const push = (n) => {
|
|
874
|
-
const key = n.toLowerCase();
|
|
875
|
-
if (seen.has(key)) return;
|
|
876
|
-
seen.add(key);
|
|
877
|
-
ordered.push(n);
|
|
878
|
-
};
|
|
879
|
-
fallback.forEach(push);
|
|
880
|
-
names.forEach(push);
|
|
881
|
-
const layerNames = ordered.slice(0, declaredLayerCount);
|
|
882
|
-
const clampIndex = (nameLower) => {
|
|
883
|
-
if (layerNames.length <= 1) return 0;
|
|
884
|
-
if (nameLower === "top") return 0;
|
|
885
|
-
if (nameLower === "bottom") return layerNames.length - 1;
|
|
886
|
-
const m = /^inner(\d+)$/i.exec(nameLower);
|
|
887
|
-
if (m) {
|
|
888
|
-
if (layerNames.length <= 2) return layerNames.length - 1;
|
|
889
|
-
const parsed = parseInt(m[1], 10);
|
|
890
|
-
const maxInner = layerNames.length - 2;
|
|
891
|
-
const clampedInner = Math.min(
|
|
892
|
-
maxInner,
|
|
893
|
-
Math.max(1, Number.isFinite(parsed) ? parsed : 1)
|
|
894
|
-
);
|
|
895
|
-
return clampedInner;
|
|
896
|
-
}
|
|
897
|
-
return 0;
|
|
898
|
-
};
|
|
899
|
-
const map = /* @__PURE__ */ new Map();
|
|
900
|
-
layerNames.forEach((n, i) => map.set(n.toLowerCase(), i));
|
|
901
|
-
ordered.slice(layerNames.length).forEach((n) => {
|
|
902
|
-
const key = n.toLowerCase();
|
|
903
|
-
map.set(key, clampIndex(key));
|
|
904
|
-
});
|
|
905
|
-
return { layerNames, zIndexByName: map };
|
|
906
|
-
}
|
|
907
|
-
function obstacleZs(ob, zIndexByName) {
|
|
908
|
-
if (ob.zLayers?.length)
|
|
909
|
-
return Array.from(new Set(ob.zLayers)).sort((a, b) => a - b);
|
|
910
|
-
const fromNames = (ob.layers ?? []).map((n) => zIndexByName.get(n.toLowerCase())).filter((v) => typeof v === "number");
|
|
911
|
-
return Array.from(new Set(fromNames)).sort((a, b) => a - b);
|
|
912
|
-
}
|
|
913
|
-
function obstacleToXYRect(ob) {
|
|
914
|
-
const w = ob.width;
|
|
915
|
-
const h = ob.height;
|
|
916
|
-
if (typeof w !== "number" || typeof h !== "number") return null;
|
|
917
|
-
return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h };
|
|
918
|
-
}
|
|
919
|
-
|
|
920
1153
|
// lib/utils/isSelfRect.ts
|
|
921
1154
|
var EPS5 = 1e-9;
|
|
922
1155
|
var isSelfRect = (params) => Math.abs(params.rect.x + params.rect.width / 2 - params.startX) < EPS5 && Math.abs(params.rect.y + params.rect.height / 2 - params.startY) < EPS5 && Math.abs(params.rect.width - params.initialW) < EPS5 && Math.abs(params.rect.height - params.initialH) < EPS5;
|
|
@@ -1051,17 +1284,10 @@ function maxExpandUp(params) {
|
|
|
1051
1284
|
}
|
|
1052
1285
|
return Math.max(0, e);
|
|
1053
1286
|
}
|
|
1054
|
-
var toRect = (tree) => ({
|
|
1055
|
-
x: tree.minX,
|
|
1056
|
-
y: tree.minY,
|
|
1057
|
-
width: tree.maxX - tree.minX,
|
|
1058
|
-
height: tree.maxY - tree.minY
|
|
1059
|
-
});
|
|
1060
1287
|
var addBlocker = (params) => {
|
|
1061
1288
|
const { rect, seen, blockers } = params;
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
seen.add(key);
|
|
1289
|
+
if (seen.has(rect)) return;
|
|
1290
|
+
seen.add(rect);
|
|
1065
1291
|
blockers.push(rect);
|
|
1066
1292
|
};
|
|
1067
1293
|
var toQueryRect = (params) => {
|
|
@@ -1098,23 +1324,22 @@ function expandRectFromSeed(params) {
|
|
|
1098
1324
|
const blockersIndex = obsticalIndexByLayer[z];
|
|
1099
1325
|
if (blockersIndex) {
|
|
1100
1326
|
for (const entry of blockersIndex.search(query))
|
|
1101
|
-
addBlocker({ rect:
|
|
1327
|
+
addBlocker({ rect: entry, seen, blockers });
|
|
1102
1328
|
}
|
|
1103
1329
|
const placedLayer = placedIndexByLayer[z];
|
|
1104
1330
|
if (placedLayer) {
|
|
1105
1331
|
for (const entry of placedLayer.search(query)) {
|
|
1106
1332
|
const isFullStack = entry.zLayers.length >= totalLayers;
|
|
1107
1333
|
if (!isFullStack) continue;
|
|
1108
|
-
const rect = toRect(entry);
|
|
1109
1334
|
if (isSelfRect({
|
|
1110
|
-
rect,
|
|
1335
|
+
rect: entry,
|
|
1111
1336
|
startX,
|
|
1112
1337
|
startY,
|
|
1113
1338
|
initialW,
|
|
1114
1339
|
initialH
|
|
1115
1340
|
}))
|
|
1116
1341
|
continue;
|
|
1117
|
-
addBlocker({ rect, seen, blockers });
|
|
1342
|
+
addBlocker({ rect: entry, seen, blockers });
|
|
1118
1343
|
}
|
|
1119
1344
|
}
|
|
1120
1345
|
}
|
|
@@ -1205,9 +1430,9 @@ function isFullyOccupiedAtPoint(params) {
|
|
|
1205
1430
|
};
|
|
1206
1431
|
for (let z = 0; z < params.layerCount; z++) {
|
|
1207
1432
|
const obstacleIdx = params.obstacleIndexByLayer[z];
|
|
1208
|
-
const hasObstacle = !!obstacleIdx && obstacleIdx.
|
|
1433
|
+
const hasObstacle = !!obstacleIdx && obstacleIdx.collides(query);
|
|
1209
1434
|
const placedIdx = params.placedIndexByLayer[z];
|
|
1210
|
-
const hasPlaced = !!placedIdx && placedIdx.
|
|
1435
|
+
const hasPlaced = !!placedIdx && placedIdx.collides(query);
|
|
1211
1436
|
if (!hasObstacle && !hasPlaced) return false;
|
|
1212
1437
|
}
|
|
1213
1438
|
return true;
|
|
@@ -1233,7 +1458,7 @@ function longestFreeSpanAroundZ(params) {
|
|
|
1233
1458
|
maxY: y
|
|
1234
1459
|
};
|
|
1235
1460
|
const obstacleIdx = obstacleIndexByLayer[layer];
|
|
1236
|
-
if (obstacleIdx && obstacleIdx.
|
|
1461
|
+
if (obstacleIdx && obstacleIdx.collides(query)) return false;
|
|
1237
1462
|
const extras = additionalBlockersByLayer?.[layer] ?? [];
|
|
1238
1463
|
return !extras.some((b) => containsPoint(b, { x, y }));
|
|
1239
1464
|
};
|
|
@@ -1265,6 +1490,10 @@ function computeCandidates3D(params) {
|
|
|
1265
1490
|
hardPlacedByLayer
|
|
1266
1491
|
} = params;
|
|
1267
1492
|
const out = /* @__PURE__ */ new Map();
|
|
1493
|
+
const hardRectsByLayer = Array.from({ length: layerCount }, (_, z) => [
|
|
1494
|
+
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1495
|
+
...hardPlacedByLayer[z] ?? []
|
|
1496
|
+
]);
|
|
1268
1497
|
for (let x = bounds.x; x < bounds.x + bounds.width; x += gridSize) {
|
|
1269
1498
|
for (let y = bounds.y; y < bounds.y + bounds.height; y += gridSize) {
|
|
1270
1499
|
if (Math.abs(x - bounds.x) < EPS4 || Math.abs(y - bounds.y) < EPS4 || x > bounds.x + bounds.width - gridSize - EPS4 || y > bounds.y + bounds.height - gridSize - EPS4) {
|
|
@@ -1296,14 +1525,11 @@ function computeCandidates3D(params) {
|
|
|
1296
1525
|
}
|
|
1297
1526
|
}
|
|
1298
1527
|
const anchorZ = bestSpan.length ? bestSpan[Math.floor(bestSpan.length / 2)] : bestZ;
|
|
1299
|
-
const hardAtZ = [
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
distancePointToRectEdges({ x, y }, bounds),
|
|
1305
|
-
...hardAtZ.length ? hardAtZ.map((b) => distancePointToRectEdges({ x, y }, b)) : [Infinity]
|
|
1306
|
-
);
|
|
1528
|
+
const hardAtZ = hardRectsByLayer[anchorZ] ?? [];
|
|
1529
|
+
let d = distancePointToRectEdges({ x, y }, bounds);
|
|
1530
|
+
for (const blocker of hardAtZ) {
|
|
1531
|
+
d = Math.min(d, distancePointToRectEdges({ x, y }, blocker));
|
|
1532
|
+
}
|
|
1307
1533
|
const distance = quantize2(d);
|
|
1308
1534
|
const k = `${x.toFixed(6)}|${y.toFixed(6)}`;
|
|
1309
1535
|
const cand = {
|
|
@@ -1328,6 +1554,12 @@ function computeCandidates3D(params) {
|
|
|
1328
1554
|
|
|
1329
1555
|
// lib/solvers/RectDiffSeedingSolver/computeEdgeCandidates3D.ts
|
|
1330
1556
|
var quantize3 = (value, precision = 1e-6) => Math.round(value / precision) * precision;
|
|
1557
|
+
var toRect = (rect) => "minX" in rect ? {
|
|
1558
|
+
x: rect.minX,
|
|
1559
|
+
y: rect.minY,
|
|
1560
|
+
width: rect.maxX - rect.minX,
|
|
1561
|
+
height: rect.maxY - rect.minY
|
|
1562
|
+
} : rect;
|
|
1331
1563
|
function computeUncoveredSegments(params) {
|
|
1332
1564
|
const { lineStart, lineEnd, coveringIntervals, minSegmentLength } = params;
|
|
1333
1565
|
const lineStartQ = quantize3(lineStart);
|
|
@@ -1390,6 +1622,13 @@ function computeEdgeCandidates3D(params) {
|
|
|
1390
1622
|
const out = [];
|
|
1391
1623
|
const \u03B4 = Math.max(minSize * 0.15, EPS4 * 3);
|
|
1392
1624
|
const dedup = /* @__PURE__ */ new Set();
|
|
1625
|
+
const hardRectsByLayer = Array.from(
|
|
1626
|
+
{ length: layerCount },
|
|
1627
|
+
(_, z) => [
|
|
1628
|
+
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1629
|
+
...hardPlacedByLayer[z] ?? []
|
|
1630
|
+
].map(toRect)
|
|
1631
|
+
);
|
|
1393
1632
|
const key = (p) => `${p.z}|${p.x.toFixed(6)}|${p.y.toFixed(6)}`;
|
|
1394
1633
|
function fullyOcc(p) {
|
|
1395
1634
|
return isFullyOccupiedAtPoint({
|
|
@@ -1408,19 +1647,11 @@ function computeEdgeCandidates3D(params) {
|
|
|
1408
1647
|
if (x < bounds.x + EPS4 || y < bounds.y + EPS4 || x > bounds.x + bounds.width - EPS4 || y > bounds.y + bounds.height - EPS4)
|
|
1409
1648
|
return;
|
|
1410
1649
|
if (fullyOcc({ x, y })) return;
|
|
1411
|
-
const hard = [
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
y: quantize3(b.y),
|
|
1417
|
-
width: quantize3(b.width),
|
|
1418
|
-
height: quantize3(b.height)
|
|
1419
|
-
}));
|
|
1420
|
-
const d = Math.min(
|
|
1421
|
-
distancePointToRectEdges({ x, y }, bounds),
|
|
1422
|
-
...hard.length ? hard.map((b) => distancePointToRectEdges({ x, y }, b)) : [Infinity]
|
|
1423
|
-
);
|
|
1650
|
+
const hard = hardRectsByLayer[z] ?? [];
|
|
1651
|
+
let d = distancePointToRectEdges({ x, y }, bounds);
|
|
1652
|
+
for (const blocker of hard) {
|
|
1653
|
+
d = Math.min(d, distancePointToRectEdges({ x, y }, blocker));
|
|
1654
|
+
}
|
|
1424
1655
|
const distance = quantize3(d);
|
|
1425
1656
|
const k = key({ x, y, z });
|
|
1426
1657
|
if (dedup.has(k)) return;
|
|
@@ -1445,10 +1676,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1445
1676
|
});
|
|
1446
1677
|
}
|
|
1447
1678
|
for (let z = 0; z < layerCount; z++) {
|
|
1448
|
-
const blockers = [
|
|
1449
|
-
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1450
|
-
...hardPlacedByLayer[z] ?? []
|
|
1451
|
-
].map((b) => ({
|
|
1679
|
+
const blockers = (hardRectsByLayer[z] ?? []).map((b) => ({
|
|
1452
1680
|
x: quantize3(b.x),
|
|
1453
1681
|
y: quantize3(b.y),
|
|
1454
1682
|
width: quantize3(b.width),
|
|
@@ -1687,7 +1915,7 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1687
1915
|
}
|
|
1688
1916
|
}
|
|
1689
1917
|
}
|
|
1690
|
-
const
|
|
1918
|
+
const sameRect2 = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
1691
1919
|
removeIdx.sort((a, b) => b - a).forEach((idx) => {
|
|
1692
1920
|
const rem = params.placed.splice(idx, 1)[0];
|
|
1693
1921
|
if (params.placedIndexByLayer) {
|
|
@@ -1696,7 +1924,7 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1696
1924
|
if (tree)
|
|
1697
1925
|
tree.remove(
|
|
1698
1926
|
rectToTree(rem.rect, { zLayers: rem.zLayers }),
|
|
1699
|
-
|
|
1927
|
+
sameRect2
|
|
1700
1928
|
);
|
|
1701
1929
|
}
|
|
1702
1930
|
}
|
|
@@ -1714,27 +1942,14 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1714
1942
|
}
|
|
1715
1943
|
}
|
|
1716
1944
|
|
|
1717
|
-
// lib/utils/getColorForZLayer.ts
|
|
1718
|
-
var getColorForZLayer = (zLayers) => {
|
|
1719
|
-
const minZ = Math.min(...zLayers);
|
|
1720
|
-
const colors = [
|
|
1721
|
-
{ fill: "#dbeafe", stroke: "#3b82f6" },
|
|
1722
|
-
{ fill: "#fef3c7", stroke: "#f59e0b" },
|
|
1723
|
-
{ fill: "#d1fae5", stroke: "#10b981" },
|
|
1724
|
-
{ fill: "#e9d5ff", stroke: "#a855f7" },
|
|
1725
|
-
{ fill: "#fed7aa", stroke: "#f97316" },
|
|
1726
|
-
{ fill: "#fecaca", stroke: "#ef4444" }
|
|
1727
|
-
];
|
|
1728
|
-
return colors[minZ % colors.length];
|
|
1729
|
-
};
|
|
1730
|
-
|
|
1731
1945
|
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
1732
1946
|
import RBush3 from "rbush";
|
|
1733
|
-
var RectDiffSeedingSolver = class extends
|
|
1947
|
+
var RectDiffSeedingSolver = class extends BaseSolver4 {
|
|
1734
1948
|
constructor(input) {
|
|
1735
1949
|
super();
|
|
1736
1950
|
this.input = input;
|
|
1737
1951
|
}
|
|
1952
|
+
input;
|
|
1738
1953
|
// Engine fields (mirrors initState / engine.ts)
|
|
1739
1954
|
srj;
|
|
1740
1955
|
layerNames;
|
|
@@ -1746,6 +1961,7 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1746
1961
|
candidates;
|
|
1747
1962
|
placed;
|
|
1748
1963
|
placedIndexByLayer;
|
|
1964
|
+
hardPlacedByLayer;
|
|
1749
1965
|
expansionIndex;
|
|
1750
1966
|
edgeAnalysisDone;
|
|
1751
1967
|
totalSeedsThisGrid;
|
|
@@ -1801,6 +2017,7 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1801
2017
|
{ length: layerCount },
|
|
1802
2018
|
() => new RBush3()
|
|
1803
2019
|
);
|
|
2020
|
+
this.hardPlacedByLayer = Array.from({ length: layerCount }, () => []);
|
|
1804
2021
|
this.expansionIndex = 0;
|
|
1805
2022
|
this.edgeAnalysisDone = false;
|
|
1806
2023
|
this.totalSeedsThisGrid = 0;
|
|
@@ -1829,25 +2046,22 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1829
2046
|
maxMultiLayerSpan
|
|
1830
2047
|
} = this.options;
|
|
1831
2048
|
const grid = gridSizes[this.gridIndex];
|
|
1832
|
-
const hardPlacedByLayer = allLayerNode({
|
|
1833
|
-
layerCount: this.layerCount,
|
|
1834
|
-
placed: this.placed
|
|
1835
|
-
});
|
|
1836
2049
|
if (this.candidates.length === 0 && this.consumedSeedsThisGrid === 0) {
|
|
1837
2050
|
this.candidates = computeCandidates3D({
|
|
1838
2051
|
bounds: this.bounds,
|
|
1839
2052
|
gridSize: grid,
|
|
1840
2053
|
layerCount: this.layerCount,
|
|
1841
|
-
hardPlacedByLayer,
|
|
2054
|
+
hardPlacedByLayer: this.hardPlacedByLayer,
|
|
1842
2055
|
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1843
2056
|
placedIndexByLayer: this.placedIndexByLayer
|
|
1844
2057
|
});
|
|
1845
2058
|
this.totalSeedsThisGrid = this.candidates.length;
|
|
1846
2059
|
this.consumedSeedsThisGrid = 0;
|
|
1847
2060
|
}
|
|
1848
|
-
if (this.candidates.length
|
|
2061
|
+
if (this.consumedSeedsThisGrid >= this.candidates.length) {
|
|
1849
2062
|
if (this.gridIndex + 1 < gridSizes.length) {
|
|
1850
2063
|
this.gridIndex += 1;
|
|
2064
|
+
this.candidates = [];
|
|
1851
2065
|
this.totalSeedsThisGrid = 0;
|
|
1852
2066
|
this.consumedSeedsThisGrid = 0;
|
|
1853
2067
|
return;
|
|
@@ -1860,20 +2074,28 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1860
2074
|
layerCount: this.layerCount,
|
|
1861
2075
|
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1862
2076
|
placedIndexByLayer: this.placedIndexByLayer,
|
|
1863
|
-
hardPlacedByLayer
|
|
2077
|
+
hardPlacedByLayer: this.hardPlacedByLayer
|
|
1864
2078
|
});
|
|
1865
2079
|
this.edgeAnalysisDone = true;
|
|
1866
2080
|
this.totalSeedsThisGrid = this.candidates.length;
|
|
1867
2081
|
this.consumedSeedsThisGrid = 0;
|
|
1868
2082
|
return;
|
|
1869
2083
|
}
|
|
2084
|
+
this.candidates = [];
|
|
1870
2085
|
this.solved = true;
|
|
1871
2086
|
this.expansionIndex = 0;
|
|
1872
2087
|
return;
|
|
1873
2088
|
}
|
|
1874
2089
|
}
|
|
1875
|
-
const cand = this.candidates.
|
|
1876
|
-
|
|
2090
|
+
const cand = this.candidates[this.consumedSeedsThisGrid++];
|
|
2091
|
+
if (isFullyOccupiedAtPoint({
|
|
2092
|
+
layerCount: this.layerCount,
|
|
2093
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
2094
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
2095
|
+
point: { x: cand.x, y: cand.y }
|
|
2096
|
+
})) {
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
1877
2099
|
const span = longestFreeSpanAroundZ({
|
|
1878
2100
|
x: cand.x,
|
|
1879
2101
|
y: cand.y,
|
|
@@ -1882,7 +2104,7 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1882
2104
|
minSpan: minMulti.minLayers,
|
|
1883
2105
|
maxSpan: maxMultiLayerSpan,
|
|
1884
2106
|
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1885
|
-
additionalBlockersByLayer: hardPlacedByLayer
|
|
2107
|
+
additionalBlockersByLayer: this.hardPlacedByLayer
|
|
1886
2108
|
});
|
|
1887
2109
|
const attempts = [];
|
|
1888
2110
|
if (span.length >= minMulti.minLayers) {
|
|
@@ -1929,14 +2151,10 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1929
2151
|
},
|
|
1930
2152
|
newIndex
|
|
1931
2153
|
);
|
|
1932
|
-
this.
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
placedIndexByLayer: this.placedIndexByLayer,
|
|
1937
|
-
point: { x: c.x, y: c.y }
|
|
1938
|
-
})
|
|
1939
|
-
);
|
|
2154
|
+
this.hardPlacedByLayer = allLayerNode({
|
|
2155
|
+
layerCount: this.layerCount,
|
|
2156
|
+
placed: this.placed
|
|
2157
|
+
});
|
|
1940
2158
|
return;
|
|
1941
2159
|
}
|
|
1942
2160
|
}
|
|
@@ -2085,7 +2303,7 @@ z:${placement.zLayers.join(",")}`
|
|
|
2085
2303
|
};
|
|
2086
2304
|
|
|
2087
2305
|
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
2088
|
-
import { BaseSolver as
|
|
2306
|
+
import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
|
|
2089
2307
|
|
|
2090
2308
|
// lib/utils/finalizeRects.ts
|
|
2091
2309
|
function finalizeRects(params) {
|
|
@@ -2157,11 +2375,12 @@ import RBush4 from "rbush";
|
|
|
2157
2375
|
var sameTreeRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
2158
2376
|
|
|
2159
2377
|
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
2160
|
-
var RectDiffExpansionSolver = class extends
|
|
2378
|
+
var RectDiffExpansionSolver = class extends BaseSolver5 {
|
|
2161
2379
|
constructor(input) {
|
|
2162
2380
|
super();
|
|
2163
2381
|
this.input = input;
|
|
2164
2382
|
}
|
|
2383
|
+
input;
|
|
2165
2384
|
placedIndexByLayer = [];
|
|
2166
2385
|
_meshNodes = [];
|
|
2167
2386
|
_setup() {
|
|
@@ -2302,19 +2521,6 @@ import "rbush";
|
|
|
2302
2521
|
|
|
2303
2522
|
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2304
2523
|
import RBush5 from "rbush";
|
|
2305
|
-
|
|
2306
|
-
// lib/utils/padRect.ts
|
|
2307
|
-
var padRect = (rect, clearance) => {
|
|
2308
|
-
if (!clearance || clearance <= 0) return rect;
|
|
2309
|
-
return {
|
|
2310
|
-
x: rect.x - clearance,
|
|
2311
|
-
y: rect.y - clearance,
|
|
2312
|
-
width: rect.width + 2 * clearance,
|
|
2313
|
-
height: rect.height + 2 * clearance
|
|
2314
|
-
};
|
|
2315
|
-
};
|
|
2316
|
-
|
|
2317
|
-
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2318
2524
|
var buildObstacleIndexesByLayer = (params) => {
|
|
2319
2525
|
const { srj, boardVoidRects, obstacleClearance } = params;
|
|
2320
2526
|
const { layerNames, zIndexByName } = buildZIndexMap({
|
|
@@ -2658,6 +2864,7 @@ import { mergeGraphics as mergeGraphics2 } from "graphics-debug";
|
|
|
2658
2864
|
var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
2659
2865
|
rectDiffGridSolverPipeline;
|
|
2660
2866
|
gapFillSolver;
|
|
2867
|
+
outerLayerContainmentMergeSolver;
|
|
2661
2868
|
boardVoidRects;
|
|
2662
2869
|
zIndexByName;
|
|
2663
2870
|
layerNames;
|
|
@@ -2693,6 +2900,18 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2693
2900
|
}
|
|
2694
2901
|
}
|
|
2695
2902
|
]
|
|
2903
|
+
),
|
|
2904
|
+
definePipelineStep3(
|
|
2905
|
+
"outerLayerContainmentMergeSolver",
|
|
2906
|
+
OuterLayerContainmentMergeSolver,
|
|
2907
|
+
(rectDiffPipeline) => [
|
|
2908
|
+
{
|
|
2909
|
+
meshNodes: rectDiffPipeline.gapFillSolver?.getOutput().outputNodes ?? rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [],
|
|
2910
|
+
simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
|
|
2911
|
+
zIndexByName: rectDiffPipeline.zIndexByName ?? /* @__PURE__ */ new Map(),
|
|
2912
|
+
obstacleClearance: rectDiffPipeline.inputProblem.obstacleClearance
|
|
2913
|
+
}
|
|
2914
|
+
]
|
|
2696
2915
|
)
|
|
2697
2916
|
];
|
|
2698
2917
|
_setup() {
|
|
@@ -2718,6 +2937,10 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2718
2937
|
return [this.inputProblem];
|
|
2719
2938
|
}
|
|
2720
2939
|
getOutput() {
|
|
2940
|
+
const outerLayerMergeOutput = this.outerLayerContainmentMergeSolver?.getOutput();
|
|
2941
|
+
if (outerLayerMergeOutput) {
|
|
2942
|
+
return { meshNodes: outerLayerMergeOutput.outputNodes };
|
|
2943
|
+
}
|
|
2721
2944
|
const gapFillOutput = this.gapFillSolver?.getOutput();
|
|
2722
2945
|
if (gapFillOutput) {
|
|
2723
2946
|
return { meshNodes: gapFillOutput.outputNodes };
|