Skip to content

Commit 901d500

Browse files
committed
Render bounding box
1 parent 91ba62e commit 901d500

9 files changed

Lines changed: 370 additions & 237 deletions

File tree

src/bounding-box.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
export class BoundingBox {
2+
private minX: number = Infinity;
3+
private maxX: number = -Infinity;
4+
private minY: number = Infinity;
5+
private maxY: number = -Infinity;
6+
private minZ: number = Infinity;
7+
private maxZ: number = -Infinity;
8+
9+
constructor() {
10+
// console.log('BoundingBox initialized');
11+
}
12+
13+
/**
14+
* Updates the bounding box with the given coordinates.
15+
* @param x - The X coordinate.
16+
* @param y - The Y coordinate.
17+
* @param z - The Z coordinate.
18+
*/
19+
public update(x: number, y: number, z: number): void {
20+
this.minX = Math.min(this.minX, x);
21+
this.maxX = Math.max(this.maxX, x);
22+
this.minY = Math.min(this.minY, y);
23+
this.maxY = Math.max(this.maxY, y);
24+
this.minZ = Math.min(this.minZ, z);
25+
this.maxZ = Math.max(this.maxZ, z);
26+
// console.log(`Updated bounding box: minX=${this.minX}, maxX=${this.maxX}, minY=${this.minY}, maxY=${this.maxY}, minZ=${this.minZ}, maxZ=${this.maxZ}`);
27+
}
28+
29+
/**
30+
* Checks if the bounding box has been updated with any points.
31+
* @returns True if at least one point has been added, false otherwise.
32+
*/
33+
public get isValid(): boolean {
34+
return this.minX !== Infinity;
35+
}
36+
37+
/**
38+
* Gets the size of the bounding box.
39+
* @returns An object with x, y, and z dimensions, or null if the bounding box is not valid.
40+
*/
41+
public get size(): { x: number; y: number; z: number } | null {
42+
if (!this.isValid) {
43+
return null;
44+
}
45+
return {
46+
x: this.maxX - this.minX,
47+
y: this.maxY - this.minY,
48+
z: this.maxZ - this.minZ,
49+
};
50+
}
51+
52+
/**
53+
* Gets the center coordinates of the bounding box.
54+
* @returns An object with x, y, and z center coordinates, or null if the bounding box is not valid.
55+
*/
56+
public get center(): { x: number; y: number; z: number } | null {
57+
if (!this.isValid) {
58+
return null;
59+
}
60+
return {
61+
x: this.minX + (this.maxX - this.minX) / 2,
62+
y: this.minY + (this.maxY - this.minY) / 2,
63+
z: this.minZ + (this.maxZ - this.minZ) / 2,
64+
};
65+
}
66+
}

src/build-volume.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class BuildVolume {
6666
* @returns Configured LineBox instance
6767
*/
6868
createLineBox(): LineBox {
69-
const lineBox = new LineBox(this.x, this.z, this.y, this.color);
69+
const lineBox = new LineBox(this.x, this.z, this.y, this.color, false);
7070
this.disposables.push(lineBox);
7171
return lineBox;
7272
}

src/helpers/line-box.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BufferGeometry, Float32BufferAttribute, Color, LineSegments, LineDashedMaterial } from 'three';
1+
import { BufferGeometry, Float32BufferAttribute, Color, LineSegments, LineDashedMaterial, LineBasicMaterial } from 'three';
22

33
/**
44
* A helper class that creates a 3D box outline with dashed lines using Three.js LineSegments.
@@ -12,19 +12,23 @@ class LineBox extends LineSegments {
1212
* @param z - Depth of the box along the Z axis
1313
* @param color - Color of the box lines (can be Color, hex number, or CSS color string)
1414
*/
15-
constructor(x: number, y: number, z: number, color: Color | number | string) {
15+
constructor(x: number, y: number, z: number, color: Color | number | string, dashed = true) {
1616
// Create geometry for the box
1717
const geometryBox = LineBox.createBoxGeometry(x, y, z);
1818

1919
// Create material for the lines with dashed effect
20-
const material = new LineDashedMaterial({ color: new Color(color), dashSize: 3, gapSize: 1 });
20+
const material = dashed
21+
? new LineDashedMaterial({ color: new Color(color), dashSize: 3, gapSize: 1 })
22+
: new LineBasicMaterial({ color: new Color(color) });
2123

2224
// Initialize the LineSegments with the geometry and material
2325
super(geometryBox, material);
2426

2527
// Compute line distances for the dashed effect
26-
this.computeLineDistances();
2728

29+
if (dashed) {
30+
this.computeLineDistances();
31+
}
2832
// Align the bottom of the box to Y position
2933
this.position.setY(y / 2);
3034
}

src/indexers.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { Path, PathType } from './path';
2+
import { Layer } from './layer'; // Assuming Layer is now in its own file
3+
4+
/**
5+
* Base error class for indexer-related errors
6+
*/
7+
class NonApplicableIndexer extends Error {}
8+
9+
/**
10+
* Base class for path indexers
11+
* @remarks
12+
* Indexers organize paths into different structures (layers, tools, etc.)
13+
*/
14+
export class Indexer {
15+
/** The indexes being managed by this indexer */
16+
protected indexes: unknown;
17+
18+
/**
19+
* Creates a new Indexer instance
20+
* @param indexes - The indexes to manage
21+
*/
22+
constructor(indexes: unknown) {
23+
this.indexes = indexes;
24+
}
25+
26+
/**
27+
* Sorts a path into the appropriate index
28+
* @param path - Path to sort
29+
* @throws Error if not implemented in subclass
30+
*/
31+
sortIn(path: Path): void {
32+
path;
33+
throw new Error('Method not implemented.');
34+
}
35+
}
36+
37+
/**
38+
* Indexer that organizes paths by travel type (extrusion vs travel moves)
39+
*/
40+
export class TravelTypeIndexer extends Indexer {
41+
/** Indexes containing arrays of paths for each travel type */
42+
protected declare indexes: Record<string, Path[]>;
43+
44+
/**
45+
* Creates a new TravelTypeIndexer
46+
* @param indexes - Object containing arrays for travel and extrusion paths
47+
*/
48+
constructor(indexes: Record<string, Path[]>) {
49+
super(indexes);
50+
}
51+
52+
/**
53+
* Sorts a path into either extrusion or travel paths
54+
* @param path - Path to sort
55+
*/
56+
sortIn(path: Path): void {
57+
if (path.travelType === PathType.Extrusion) {
58+
this.indexes.extrusion.push(path);
59+
} else {
60+
this.indexes.travel.push(path);
61+
}
62+
}
63+
}
64+
65+
/**
66+
* Error thrown when attempting to index a non-planar path
67+
*/
68+
export class NonPlanarPathError extends NonApplicableIndexer {
69+
constructor() {
70+
super("Non-planar paths can't be indexed by layer");
71+
}
72+
}
73+
74+
/**
75+
* Indexer that organizes paths into layers based on Z height
76+
*/
77+
export class LayersIndexer extends Indexer {
78+
/** Default tolerance for layer height differences */
79+
static readonly DEFAULT_TOLERANCE = 0.05;
80+
81+
/** Array of layers being managed */
82+
protected declare indexes: Layer[];
83+
84+
/** Tolerance for layer height differences */
85+
private tolerance: number;
86+
87+
/**
88+
* Creates a new LayersIndexer
89+
* @param indexes - Array to store layers
90+
* @param tolerance - Height tolerance for layer detection (default: DEFAULT_TOLERANCE)
91+
*/
92+
constructor(indexes: Layer[], tolerance: number = LayersIndexer.DEFAULT_TOLERANCE) {
93+
super(indexes);
94+
this.tolerance = tolerance;
95+
}
96+
97+
/**
98+
* Sorts a path into the appropriate layer
99+
* @param path - Path to sort
100+
* @throws NonPlanarPathError if path is non-planar
101+
*/
102+
sortIn(path: Path): void {
103+
if (
104+
path.travelType === PathType.Extrusion &&
105+
path.vertices.some((_, i, arr) => i > 3 && i % 3 === 2 && Math.abs(arr[i] - arr[i - 3]) > this.tolerance)
106+
) {
107+
throw new NonPlanarPathError();
108+
}
109+
110+
if (this.indexes[this.indexes.length - 1] === undefined) {
111+
this.createLayer(path.vertices[2]);
112+
}
113+
114+
if (
115+
path.travelType === PathType.Extrusion &&
116+
this.lastLayer().paths.some((p) => p.travelType === PathType.Extrusion)
117+
) {
118+
if (path.vertices[2] - (this.lastLayer().z || 0) > this.tolerance) {
119+
this.createLayer(path.vertices[2]);
120+
}
121+
}
122+
this.lastLayer().paths.push(path);
123+
}
124+
125+
/**
126+
* Gets the last layer in the indexes
127+
* @returns The most recent layer
128+
*/
129+
private lastLayer(): Layer {
130+
return this.indexes[this.indexes.length - 1];
131+
}
132+
133+
/**
134+
* Creates a new layer at the specified Z height
135+
* @param z - Z height for the new layer
136+
*/
137+
private createLayer(z: number): void {
138+
const layerNumber = this.indexes.length;
139+
const height = z - (this.lastLayer()?.z || 0);
140+
this.indexes.push(new Layer(this.indexes.length, [], layerNumber, height, z));
141+
}
142+
}
143+
144+
/**
145+
* Indexer that organizes paths by tool number
146+
*/
147+
export class ToolIndexer extends Indexer {
148+
/** 2D array of paths indexed by tool number */
149+
protected declare indexes: Path[][];
150+
151+
/**
152+
* Creates a new ToolIndexer
153+
* @param indexes - 2D array to store paths by tool
154+
*/
155+
constructor(indexes: Path[][]) {
156+
super(indexes);
157+
}
158+
159+
/**
160+
* Sorts a path into the appropriate tool's path array
161+
* @param path - Path to sort
162+
*/
163+
sortIn(path: Path): void {
164+
if (path.travelType === PathType.Extrusion) {
165+
this.indexes;
166+
this.indexes[path.tool] = this.indexes[path.tool] || [];
167+
if (this.indexes[path.tool] === undefined) {
168+
this.indexes[path.tool] = [];
169+
}
170+
this.indexes[path.tool].push(path);
171+
}
172+
}
173+
}

src/interpreter.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export class Interpreter {
6060
state.z = z ?? state.z;
6161

6262
currentPath.addPoint(state.x, state.y, state.z);
63+
if (pathType === PathType.Extrusion) {
64+
job.boundingBox.update(state.x, state.y, state.z);
65+
}
6366
}
6467

6568
g1 = this.g0;
@@ -160,13 +163,19 @@ export class Interpreter {
160163
py = centerY + arcRadius * Math.sin(currentAngle);
161164
pz += zStep;
162165
currentPath.addPoint(px, py, pz);
166+
if (pathType === PathType.Extrusion) {
167+
job.boundingBox.update(px, py, pz);
168+
}
163169
}
164170

165171
state.x = x || state.x;
166172
state.y = y || state.y;
167173
state.z = z || state.z;
168174

169175
currentPath.addPoint(state.x, state.y, state.z);
176+
if (pathType === PathType.Extrusion) {
177+
job.boundingBox.update(state.x, state.y, state.z);
178+
}
170179
}
171180

172181
g3 = this.g2;

0 commit comments

Comments
 (0)