Skip to content

Commit b868661

Browse files
committed
Add GCodeVector3 for convenience
2 parents 4a6b0b2 + d370d8d commit b868661

5 files changed

Lines changed: 93 additions & 34 deletions

File tree

src/GCodeVector3.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Vector3 } from 'three';
2+
3+
/**
4+
* Represents a 3D vector in GCode coordinates that is easily convertible to Three.js Vector3.
5+
* This is a convenience class. Care should be taken when many allocations are made.
6+
*/
7+
export class GCodeVector3 extends Vector3 {
8+
constructor(x: number, y: number, z: number) {
9+
super(x, y, z);
10+
}
11+
12+
/**
13+
* map from GCode coordinates to Three.js Vector3
14+
* GCode uses (X, Y, Z) while Three.js uses (X, Z, Y)
15+
* also the Y in GCode goes to the back, while in Three.js Z goes to the front
16+
* @returns Vector3
17+
*/
18+
toVector3(): Vector3 {
19+
return new Vector3(this.x, this.z, -this.y);
20+
}
21+
22+
static fromVector3(vector: Vector3): GCodeVector3 {
23+
return new GCodeVector3(vector.x, -vector.z, vector.y);
24+
}
25+
}

src/__tests__/GCodeVector3.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { GCodeVector3 } from '../types';
3+
import { Vector3 } from 'three';
4+
5+
describe('GCodeVector3', () => {
6+
it('should be the identity when converting to Vector3 and back', () => {
7+
const original = new GCodeVector3(1, 2, 3);
8+
const converted = GCodeVector3.fromVector3(original.toVector3());
9+
expect(converted.x).toBe(original.x);
10+
expect(converted.y).toBe(original.y);
11+
expect(converted.z).toBe(original.z);
12+
});
13+
14+
it('should be the identity when converting from Vector3 and back', () => {
15+
const original = new Vector3(4, 5, 6);
16+
const converted = GCodeVector3.fromVector3(original).toVector3();
17+
expect(converted.x).toBe(original.x);
18+
expect(converted.y).toBe(original.y);
19+
expect(converted.z).toBe(original.z);
20+
});
21+
});

src/__tests__/bounding-box.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { BoundingBox } from '../bounding-box';
3+
import { GCodeVector3 } from '../types';
4+
5+
describe('BoundingBox', () => {
6+
it('should return null for center if bounding box is not valid', () => {
7+
const bbox = new BoundingBox();
8+
expect(bbox.center).toBeNull();
9+
});
10+
11+
it('should calculate the correct center coordinates', () => {
12+
const bbox = new BoundingBox();
13+
bbox.update(new GCodeVector3(0, 0, 0));
14+
bbox.update(new GCodeVector3(10, 10, 10));
15+
16+
const center = bbox.center as GCodeVector3;
17+
expect(center).toBeInstanceOf(GCodeVector3);
18+
expect(center.x).toBe(5);
19+
expect(center.y).toBe(5);
20+
expect(center.z).toBe(5);
21+
22+
bbox.update(new GCodeVector3(-5, -5, -5));
23+
const newCenter = bbox.center as GCodeVector3;
24+
expect(newCenter.x).toBe(2.5);
25+
expect(newCenter.y).toBe(2.5);
26+
expect(newCenter.z).toBe(2.5);
27+
});
28+
});

src/bounding-box.ts

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
1+
import { GCodeVector3 } from './GCodeVector3';
12
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-
}
3+
private min: GCodeVector3 = new GCodeVector3(Infinity, Infinity, Infinity);
4+
private max: GCodeVector3 = new GCodeVector3(-Infinity, -Infinity, -Infinity);
125

136
/**
147
* Updates the bounding box with the given coordinates.
@@ -17,12 +10,12 @@ export class BoundingBox {
1710
* @param z - The Z coordinate.
1811
*/
1912
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);
13+
this.min.x = Math.min(this.min.x, x);
14+
this.max.x = Math.max(this.max.x, x);
15+
this.min.y = Math.min(this.min.y, y);
16+
this.max.y = Math.max(this.max.y, y);
17+
this.min.z = Math.min(this.min.z, z);
18+
this.max.z = Math.max(this.max.z, z);
2619
// console.log(`Updated bounding box: minX=${this.minX}, maxX=${this.maxX}, minY=${this.minY}, maxY=${this.maxY}, minZ=${this.minZ}, maxZ=${this.maxZ}`);
2720
}
2821

@@ -31,46 +24,38 @@ export class BoundingBox {
3124
* @returns True if at least one point has been added, false otherwise.
3225
*/
3326
public get isValid(): boolean {
34-
return this.minX !== Infinity;
27+
return this.min.x !== Infinity;
3528
}
3629

3730
/**
3831
* Gets the size of the bounding box.
3932
* @returns An object with x, y, and z dimensions, or null if the bounding box is not valid.
4033
*/
41-
public get size(): { x: number; y: number; z: number } | null {
34+
public get size(): GCodeVector3 | null {
4235
if (!this.isValid) {
4336
return null;
4437
}
45-
return {
46-
x: this.maxX - this.minX,
47-
y: this.maxY - this.minY,
48-
z: this.maxZ - this.minZ
49-
};
38+
return this.max.clone().sub(this.min);
5039
}
5140

5241
/**
5342
* Gets the center coordinates of the bounding box.
5443
* @returns An object with x, y, and z center coordinates, or null if the bounding box is not valid.
5544
*/
56-
public get center(): { x: number; y: number; z: number } | null {
45+
public get center(): GCodeVector3 | null {
5746
if (!this.isValid) {
5847
return null;
5948
}
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-
};
49+
return this.min.clone().add(this.max).multiplyScalar(0.5);
6550
}
6651

67-
public get corners(): { min: { x: number; y: number; z: number }; max: { x: number; y: number; z: number } } {
52+
public get corners(): { min: GCodeVector3; max: GCodeVector3 } | null {
6853
if (!this.isValid) {
6954
return null;
7055
}
7156
return {
72-
min: { x: this.minX, y: this.minY, z: this.minZ },
73-
max: { x: this.maxX, y: this.maxY, z: this.maxZ }
57+
min: this.min.clone(),
58+
max: this.max.clone()
7459
};
7560
}
7661
}

src/webgl-preview.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,8 +842,8 @@ export class WebGLPreview {
842842
// Three.js X position = G-code X center - (Build Volume X / 2)
843843
// Three.js Y position = G-code Z center (since Three.js Y is up, and LineBox handles its own Y-offset)
844844
// Three.js Z position = G-code Y center - (Build Volume Y / 2)
845-
const pos = bb.corners.min;
846-
this.boundingBoxMesh.position.set(pos.x, 0, -pos.y);
845+
const pos = bb.corners.min.toVector3();
846+
this.boundingBoxMesh.position.set(pos.x, pos.y, pos.z);
847847

848848
this.scene.add(this.boundingBoxMesh);
849849
}

0 commit comments

Comments
 (0)