Skip to content

Commit 5afcd55

Browse files
committed
feat: add divide method to Cuboid class
1 parent 5f45e51 commit 5afcd55

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

src/space/cuboid.test.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,103 @@ describe("Cuboid", () => {
116116
expect(result).toBe(false);
117117
expect(spyCallback).toHaveBeenCalled();
118118
});
119+
120+
// Tests for the divide method
121+
describe("divide method", () => {
122+
it("should return a clone of the original cuboid when all divisions are 1", () => {
123+
const divisions = cuboid.divide(1, 1, 1);
124+
expect(divisions.length).toBe(1);
125+
expect(divisions[0].point1).toEqual(cuboid.point1);
126+
expect(divisions[0].point2).toEqual(cuboid.point2);
127+
expect(divisions[0]).not.toBe(cuboid); // Should be a clone, not the same instance
128+
});
129+
130+
it("should divide a cuboid into 8 equal parts (2x2x2)", () => {
131+
const divisions = cuboid.divide(2, 2, 2);
132+
expect(divisions.length).toBe(8);
133+
134+
// Each subdivided cuboid should have 1/8 of the original volume
135+
const expectedVolume = cuboid.getVolume() / 8;
136+
divisions.forEach((div) => {
137+
expect(div.getVolume()).toBeCloseTo(expectedVolume);
138+
});
139+
140+
// Check if we have all the expected subdivisions
141+
const centers = divisions.map((div) => div.getCenter());
142+
143+
// Expected centers for a 2x2x2 division of a cuboid from (0,0,0) to (10,10,10)
144+
const expectedCenters = [
145+
{ x: 2.5, y: 2.5, z: 2.5 },
146+
{ x: 2.5, y: 2.5, z: 7.5 },
147+
{ x: 2.5, y: 7.5, z: 2.5 },
148+
{ x: 2.5, y: 7.5, z: 7.5 },
149+
{ x: 7.5, y: 2.5, z: 2.5 },
150+
{ x: 7.5, y: 2.5, z: 7.5 },
151+
{ x: 7.5, y: 7.5, z: 2.5 },
152+
{ x: 7.5, y: 7.5, z: 7.5 },
153+
];
154+
155+
// Verify all expected centers exist (allowing for small floating point differences)
156+
expectedCenters.forEach((expected) => {
157+
const found = centers.some(
158+
(center) =>
159+
Math.abs(center.x - expected.x) < 0.001 &&
160+
Math.abs(center.y - expected.y) < 0.001 &&
161+
Math.abs(center.z - expected.z) < 0.001,
162+
);
163+
expect(found).toBe(true);
164+
});
165+
});
166+
167+
it("should divide a cuboid along a single axis", () => {
168+
// Divide only along x-axis into 5 parts
169+
const divisions = cuboid.divide(5, 1, 1);
170+
expect(divisions.length).toBe(5);
171+
172+
// Check sizes
173+
divisions.forEach((div) => {
174+
expect(div.getSize().x).toBeCloseTo(2); // 10/5 = 2
175+
expect(div.getSize().y).toBeCloseTo(10);
176+
expect(div.getSize().z).toBeCloseTo(10);
177+
});
178+
179+
// Check positions - each division should be 2 units wide on x-axis
180+
for (let i = 0; i < 5; i++) {
181+
expect(divisions[i].point1.x).toBeCloseTo(i * 2);
182+
expect(divisions[i].point2.x).toBeCloseTo((i + 1) * 2);
183+
}
184+
});
185+
186+
it("should handle uneven divisions correctly", () => {
187+
// Create a non-square cuboid
188+
const unevenCuboid = new Cuboid(
189+
{ x: 0, y: 0, z: 0 },
190+
{ x: 10, y: 5, z: 20 },
191+
);
192+
193+
const divisions = unevenCuboid.divide(2, 1, 4);
194+
expect(divisions.length).toBe(8); // 2x1x4 = 8
195+
196+
// Check dimensions of first division
197+
expect(divisions[0].getSize()).toEqual({
198+
x: 5, // 10/2
199+
y: 5, // 5/1
200+
z: 5, // 20/4
201+
});
202+
});
203+
204+
it("should throw an error if any division parameter is less than 1", () => {
205+
expect(() => cuboid.divide(0, 1, 1)).toThrow();
206+
expect(() => cuboid.divide(1, 0, 1)).toThrow();
207+
expect(() => cuboid.divide(1, 1, 0)).toThrow();
208+
expect(() => cuboid.divide(-2, 3, 4)).toThrow();
209+
});
210+
211+
it("should create the correct number of divisions", () => {
212+
expect(cuboid.divide(3, 4, 5).length).toBe(3 * 4 * 5);
213+
expect(cuboid.divide(10, 1, 1).length).toBe(10);
214+
expect(cuboid.divide(1, 10, 1).length).toBe(10);
215+
expect(cuboid.divide(1, 1, 10).length).toBe(10);
216+
});
217+
});
119218
});

src/space/cuboid.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,4 +383,59 @@ export class Cuboid extends Polygon {
383383
public clone(): Cuboid {
384384
return new Cuboid(this.point1, this.point2);
385385
}
386+
387+
/**
388+
* Divides the cuboid into a specified number of equal-sized smaller cuboids along each axis.
389+
* @param divisionsX Number of divisions along the X axis (must be ≥ 1)
390+
* @param divisionsY Number of divisions along the Y axis (must be ≥ 1)
391+
* @param divisionsZ Number of divisions along the Z axis (must be ≥ 1)
392+
* @returns An array of smaller Cuboids that together make up the original Cuboid
393+
* @throws Error if any division parameter is less than 1
394+
*/
395+
public divide(
396+
divisionsX: number = 1,
397+
divisionsY: number = 1,
398+
divisionsZ: number = 1,
399+
): Cuboid[] {
400+
// Validate input parameters
401+
if (divisionsX < 1 || divisionsY < 1 || divisionsZ < 1) {
402+
throw new Error("Division parameters must be at least 1");
403+
}
404+
405+
// If all divisions are 1, return a copy of this cuboid
406+
if (divisionsX === 1 && divisionsY === 1 && divisionsZ === 1) {
407+
return [this.clone()];
408+
}
409+
410+
const result: Cuboid[] = [];
411+
412+
// Calculate size of each division
413+
const size = this.getSize();
414+
const divSizeX = size.x / divisionsX;
415+
const divSizeY = size.y / divisionsY;
416+
const divSizeZ = size.z / divisionsZ;
417+
418+
// Generate all smaller cuboids
419+
for (let x = 0; x < divisionsX; x++) {
420+
for (let y = 0; y < divisionsY; y++) {
421+
for (let z = 0; z < divisionsZ; z++) {
422+
const p1: Vector3 = {
423+
x: this.point1.x + x * divSizeX,
424+
y: this.point1.y + y * divSizeY,
425+
z: this.point1.z + z * divSizeZ,
426+
};
427+
428+
const p2: Vector3 = {
429+
x: this.point1.x + (x + 1) * divSizeX,
430+
y: this.point1.y + (y + 1) * divSizeY,
431+
z: this.point1.z + (z + 1) * divSizeZ,
432+
};
433+
434+
result.push(new Cuboid(p1, p2));
435+
}
436+
}
437+
}
438+
439+
return result;
440+
}
386441
}

0 commit comments

Comments
 (0)