Skip to content

Commit b93febe

Browse files
committed
WIP
1 parent 17f218b commit b93febe

File tree

10 files changed

+465
-223
lines changed

10 files changed

+465
-223
lines changed

lib/solvers/RectDiffSolver.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,23 @@ export class RectDiffSolver extends BaseSolver {
219219
})
220220
}
221221

222-
// obstacles (rect & oval as bounding boxes)
222+
// obstacles (rect & oval as bounding boxes) with layer information
223223
for (const obstacle of this.srj.obstacles ?? []) {
224224
if (obstacle.type === "rect" || obstacle.type === "oval") {
225+
// Get layer information if available
226+
const layerInfo =
227+
obstacle.layers && obstacle.layers.length > 0
228+
? `\nz:${obstacle.layers.join(",")}`
229+
: ""
230+
225231
rects.push({
226232
center: { x: obstacle.center.x, y: obstacle.center.y },
227233
width: obstacle.width,
228234
height: obstacle.height,
229235
fill: "#fee2e2",
230236
stroke: "#ef4444",
231237
layer: "obstacle",
232-
label: "obstacle",
238+
label: `obstacle ${layerInfo}`,
233239
})
234240
}
235241
}

lib/solvers/rectdiff/edge-expansion-gapfill/computeProgress.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ export function computeProgress(state: EdgeExpansionGapFillState): number {
66
return 1
77
}
88

9-
const totalObstacles = state.obstacles[0]?.length ?? 0
9+
// Find the first non-empty layer to determine obstacle count
10+
let totalObstacles = 0
11+
for (let z = 0; z < state.layerCount; z++) {
12+
if (state.obstacles[z] && state.obstacles[z]!.length > 0) {
13+
totalObstacles = state.obstacles[z]!.length
14+
break
15+
}
16+
}
17+
1018
if (totalObstacles === 0) {
1119
return 1
1220
}
Lines changed: 124 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,150 @@
11
// lib/solvers/rectdiff/edge-expansion-gapfill/createNodesFromObstacle.ts
22
import type { XYRect } from "../types"
33
import type { GapFillNode } from "./types"
4+
import { rectsOverlap } from "./rectsOverlap"
45

56
export function createNodesFromObstacle(params: {
67
obstacle: XYRect
78
obstacleIndex: number
8-
zLayers: number[]
9+
layerCount: number
10+
obstaclesByLayer: XYRect[][]
911
minTraceWidth: number
1012
}): GapFillNode[] {
11-
const { obstacle, obstacleIndex, zLayers, minTraceWidth } = params
13+
const {
14+
obstacle,
15+
obstacleIndex,
16+
layerCount,
17+
obstaclesByLayer,
18+
minTraceWidth,
19+
} = params
1220

1321
const EDGE_THICKNESS = minTraceWidth * 0.01
1422
const CORNER_SIZE = minTraceWidth * 0.03
1523

1624
const nodes: GapFillNode[] = []
1725

18-
// Top edge (horizontal line above obstacle)
19-
nodes.push({
20-
id: `obs${obstacleIndex}_top`,
21-
rect: {
22-
x: obstacle.x,
23-
y: obstacle.y + obstacle.height,
24-
width: obstacle.width,
25-
height: EDGE_THICKNESS,
26+
// Define the 8 positions (4 edges + 4 corners)
27+
const positions = [
28+
{
29+
name: "top",
30+
rect: {
31+
x: obstacle.x,
32+
y: obstacle.y + obstacle.height,
33+
width: obstacle.width,
34+
height: EDGE_THICKNESS,
35+
},
36+
direction: "up" as const,
37+
nodeType: "edge" as const,
2638
},
27-
zLayers: [...zLayers],
28-
direction: "up",
29-
nodeType: "edge",
30-
obstacleIndex,
31-
canExpand: true,
32-
})
33-
34-
// Bottom edge (horizontal line below obstacle)
35-
nodes.push({
36-
id: `obs${obstacleIndex}_bottom`,
37-
rect: {
38-
x: obstacle.x,
39-
y: obstacle.y - EDGE_THICKNESS,
40-
width: obstacle.width,
41-
height: EDGE_THICKNESS,
39+
{
40+
name: "bottom",
41+
rect: {
42+
x: obstacle.x,
43+
y: obstacle.y - EDGE_THICKNESS,
44+
width: obstacle.width,
45+
height: EDGE_THICKNESS,
46+
},
47+
direction: "down" as const,
48+
nodeType: "edge" as const,
4249
},
43-
zLayers: [...zLayers],
44-
direction: "down",
45-
nodeType: "edge",
46-
obstacleIndex,
47-
canExpand: true,
48-
})
49-
50-
// Right edge (vertical line to right of obstacle)
51-
nodes.push({
52-
id: `obs${obstacleIndex}_right`,
53-
rect: {
54-
x: obstacle.x + obstacle.width,
55-
y: obstacle.y,
56-
width: EDGE_THICKNESS,
57-
height: obstacle.height,
50+
{
51+
name: "right",
52+
rect: {
53+
x: obstacle.x + obstacle.width,
54+
y: obstacle.y,
55+
width: EDGE_THICKNESS,
56+
height: obstacle.height,
57+
},
58+
direction: "right" as const,
59+
nodeType: "edge" as const,
5860
},
59-
zLayers: [...zLayers],
60-
direction: "right",
61-
nodeType: "edge",
62-
obstacleIndex,
63-
canExpand: true,
64-
})
65-
66-
// Left edge (vertical line to left of obstacle)
67-
nodes.push({
68-
id: `obs${obstacleIndex}_left`,
69-
rect: {
70-
x: obstacle.x - EDGE_THICKNESS,
71-
y: obstacle.y,
72-
width: EDGE_THICKNESS,
73-
height: obstacle.height,
61+
{
62+
name: "left",
63+
rect: {
64+
x: obstacle.x - EDGE_THICKNESS,
65+
y: obstacle.y,
66+
width: EDGE_THICKNESS,
67+
height: obstacle.height,
68+
},
69+
direction: "left" as const,
70+
nodeType: "edge" as const,
7471
},
75-
zLayers: [...zLayers],
76-
direction: "left",
77-
nodeType: "edge",
78-
obstacleIndex,
79-
canExpand: true,
80-
})
81-
82-
// Top-right corner
83-
nodes.push({
84-
id: `obs${obstacleIndex}_corner_tr`,
85-
rect: {
86-
x: obstacle.x + obstacle.width,
87-
y: obstacle.y + obstacle.height,
88-
width: CORNER_SIZE,
89-
height: CORNER_SIZE,
72+
{
73+
name: "corner_tr",
74+
rect: {
75+
x: obstacle.x + obstacle.width,
76+
y: obstacle.y + obstacle.height,
77+
width: CORNER_SIZE,
78+
height: CORNER_SIZE,
79+
},
80+
direction: "up" as const,
81+
nodeType: "corner" as const,
9082
},
91-
zLayers: [...zLayers],
92-
direction: "up",
93-
nodeType: "corner",
94-
obstacleIndex,
95-
canExpand: true,
96-
})
97-
98-
// Top-left corner
99-
nodes.push({
100-
id: `obs${obstacleIndex}_corner_tl`,
101-
rect: {
102-
x: obstacle.x - CORNER_SIZE,
103-
y: obstacle.y + obstacle.height,
104-
width: CORNER_SIZE,
105-
height: CORNER_SIZE,
83+
{
84+
name: "corner_tl",
85+
rect: {
86+
x: obstacle.x - CORNER_SIZE,
87+
y: obstacle.y + obstacle.height,
88+
width: CORNER_SIZE,
89+
height: CORNER_SIZE,
90+
},
91+
direction: "up" as const,
92+
nodeType: "corner" as const,
10693
},
107-
zLayers: [...zLayers],
108-
direction: "up",
109-
nodeType: "corner",
110-
obstacleIndex,
111-
canExpand: true,
112-
})
113-
114-
// Bottom-right corner
115-
nodes.push({
116-
id: `obs${obstacleIndex}_corner_br`,
117-
rect: {
118-
x: obstacle.x + obstacle.width,
119-
y: obstacle.y - CORNER_SIZE,
120-
width: CORNER_SIZE,
121-
height: CORNER_SIZE,
94+
{
95+
name: "corner_br",
96+
rect: {
97+
x: obstacle.x + obstacle.width,
98+
y: obstacle.y - CORNER_SIZE,
99+
width: CORNER_SIZE,
100+
height: CORNER_SIZE,
101+
},
102+
direction: "down" as const,
103+
nodeType: "corner" as const,
122104
},
123-
zLayers: [...zLayers],
124-
direction: "down",
125-
nodeType: "corner",
126-
obstacleIndex,
127-
canExpand: true,
128-
})
129-
130-
// Bottom-left corner
131-
nodes.push({
132-
id: `obs${obstacleIndex}_corner_bl`,
133-
rect: {
134-
x: obstacle.x - CORNER_SIZE,
135-
y: obstacle.y - CORNER_SIZE,
136-
width: CORNER_SIZE,
137-
height: CORNER_SIZE,
105+
{
106+
name: "corner_bl",
107+
rect: {
108+
x: obstacle.x - CORNER_SIZE,
109+
y: obstacle.y - CORNER_SIZE,
110+
width: CORNER_SIZE,
111+
height: CORNER_SIZE,
112+
},
113+
direction: "down" as const,
114+
nodeType: "corner" as const,
138115
},
139-
zLayers: [...zLayers],
140-
direction: "down",
141-
nodeType: "corner",
142-
obstacleIndex,
143-
canExpand: true,
144-
})
116+
]
117+
118+
// For each position, create a single-layer node for each layer
119+
for (const position of positions) {
120+
for (let z = 0; z < layerCount; z++) {
121+
// Check if this position is blocked by an obstacle on this layer
122+
let isBlocked = false
123+
124+
if (obstaclesByLayer[z]) {
125+
for (const obs of obstaclesByLayer[z]!) {
126+
if (rectsOverlap(position.rect, obs)) {
127+
isBlocked = true
128+
break
129+
}
130+
}
131+
}
132+
133+
// Only create node if not blocked
134+
if (!isBlocked) {
135+
nodes.push({
136+
id: `obs${obstacleIndex}_${position.name}_z${z}`,
137+
rect: { ...position.rect },
138+
zLayers: [z],
139+
direction: position.direction,
140+
nodeType: position.nodeType,
141+
obstacleIndex,
142+
canExpand: true,
143+
hasEverExpanded: false,
144+
})
145+
}
146+
}
147+
}
145148

146149
return nodes
147150
}

lib/solvers/rectdiff/edge-expansion-gapfill/filterOverlappingNodes.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,46 @@
11
// lib/solvers/rectdiff/edge-expansion-gapfill/filterOverlappingNodes.ts
22
import type { GapFillNode } from "./types"
3-
import type { XYRect } from "../types"
3+
import type { XYRect, Placed3D } from "../types"
44
import { rectsOverlap } from "./rectsOverlap"
55

66
export function filterOverlappingNodes(params: {
77
nodes: GapFillNode[]
8-
existingRects: XYRect[]
9-
obstacles: XYRect[]
8+
existingPlacedByLayer: XYRect[][]
9+
obstaclesByLayer: XYRect[][]
10+
newPlaced: Placed3D[]
1011
}): GapFillNode[] {
11-
const { nodes, existingRects, obstacles } = params
12+
const { nodes, existingPlacedByLayer, obstaclesByLayer, newPlaced } = params
1213

1314
return nodes.filter((node) => {
14-
// Check overlap with existing capacity nodes
15-
for (const existing of existingRects) {
16-
if (rectsOverlap(node.rect, existing)) {
17-
return false
15+
// For each layer this node occupies, check for overlaps
16+
for (const z of node.zLayers) {
17+
// Check overlap with existing capacity nodes on this layer
18+
if (existingPlacedByLayer[z]) {
19+
for (const existing of existingPlacedByLayer[z]!) {
20+
if (rectsOverlap(node.rect, existing)) {
21+
return false
22+
}
23+
}
24+
}
25+
26+
// Check overlap with obstacles on this layer
27+
if (obstaclesByLayer[z]) {
28+
for (const obstacle of obstaclesByLayer[z]!) {
29+
if (rectsOverlap(node.rect, obstacle)) {
30+
return false
31+
}
32+
}
1833
}
1934
}
2035

21-
// Check overlap with other obstacles
22-
for (const obstacle of obstacles) {
23-
if (rectsOverlap(node.rect, obstacle)) {
36+
// Check overlap with previously placed gap-fill nodes
37+
for (const placed of newPlaced) {
38+
// Check if they share any layers
39+
const hasCommonLayer = node.zLayers.some((nz) =>
40+
placed.zLayers.includes(nz),
41+
)
42+
43+
if (hasCommonLayer && rectsOverlap(node.rect, placed.rect)) {
2444
return false
2545
}
2646
}

lib/solvers/rectdiff/edge-expansion-gapfill/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ export { calculatePotentialArea } from "./calculatePotentialArea"
1111
export { expandNode } from "./expandNode"
1212
export { rectsOverlap } from "./rectsOverlap"
1313
export { canExpandToLayers } from "./canExpandToLayers"
14+
export { mergeAdjacentLayerNodes } from "./mergeAdjacentLayerNodes"
15+
export { validateNoOverlaps } from "./validateNoOverlaps"

0 commit comments

Comments
 (0)