Skip to content

Commit ce5ea1b

Browse files
io unit tests
1 parent fee393c commit ce5ea1b

File tree

2 files changed

+191
-54
lines changed

2 files changed

+191
-54
lines changed

packages/dev/occt/lib/services/io.test.ts

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ describe("OCCT io unit tests", () => {
283283
expect(pathsWithLayer.layer).toBe("TestLayer");
284284
expect(pathsWithLayer.color).toBe("#FF0000");
285285

286-
const dxfContent = io.dxfCreate({ pathsParts: [pathsWithLayer] });
286+
const dxfDto = new Inputs.OCCT.DxfPathsPartsListDto([pathsWithLayer]);
287+
const dxfContent = io.dxfCreate(dxfDto);
287288
expect(dxfContent).toContain("TestLayer");
288289
expect(dxfContent).toContain("LWPOLYLINE");
289290
});
@@ -594,4 +595,140 @@ describe("OCCT io unit tests", () => {
594595
arcWire.delete();
595596
arc.delete();
596597
});
598+
599+
it("should save cube shape as STL file", () => {
600+
const cube = solid.createCube({ size: 10, center: [0, 0, 0] });
601+
const dto = new Inputs.OCCT.SaveStlDto(cube, "cube.stl", 0.01, false);
602+
const stl = io.saveShapeStl(dto);
603+
604+
// STL file should contain proper header and facet definitions
605+
expect(stl).toContain("solid");
606+
expect(stl).toContain("facet normal");
607+
expect(stl).toContain("outer loop");
608+
expect(stl).toContain("vertex");
609+
expect(stl).toContain("endloop");
610+
expect(stl).toContain("endfacet");
611+
expect(stl).toContain("endsolid");
612+
613+
cube.delete();
614+
});
615+
616+
it("should save cylinder shape as STL file", () => {
617+
const cylinder = solid.createCylinder({ radius: 5, height: 10, direction: [0, 1, 0], center: [0, 0, 0] });
618+
const dto = new Inputs.OCCT.SaveStlDto(cylinder, "cylinder.stl", 0.1, false);
619+
const stl = io.saveShapeStl(dto);
620+
621+
expect(stl).toContain("solid");
622+
expect(stl).toContain("facet normal");
623+
expect(stl).toContain("endsolid");
624+
625+
// Count the number of facets (triangles)
626+
const facetCount = (stl.match(/facet normal/g) || []).length;
627+
expect(facetCount).toBeGreaterThan(10);
628+
629+
cylinder.delete();
630+
});
631+
632+
it("should save cone shape as STL file with Y to Z adjustment", () => {
633+
const cone = solid.createCone({ radius1: 10, radius2: 5, height: 20, angle: 360, direction: [0, 1, 0], center: [0, 0, 0] });
634+
const dto = new Inputs.OCCT.SaveStlDto(cone, "cone.stl", 0.1, true);
635+
const stl = io.saveShapeStl(dto);
636+
637+
expect(stl).toContain("solid");
638+
expect(stl).toContain("facet normal");
639+
expect(stl).toContain("endsolid");
640+
641+
cone.delete();
642+
});
643+
644+
it("should save sphere shape as STL file with higher precision", () => {
645+
const sphere = solid.createSphere({ radius: 5, center: [0, 0, 0] });
646+
const dtoLowRes = new Inputs.OCCT.SaveStlDto(sphere, "sphere.stl", 1, false);
647+
const stlLowRes = io.saveShapeStl(dtoLowRes);
648+
649+
const dtoHighRes = new Inputs.OCCT.SaveStlDto(sphere, "sphere.stl", 0.01, false);
650+
const stlHighRes = io.saveShapeStl(dtoHighRes);
651+
652+
// Higher precision should result in more facets
653+
const facetCountLow = (stlLowRes.match(/facet normal/g) || []).length;
654+
const facetCountHigh = (stlHighRes.match(/facet normal/g) || []).length;
655+
656+
expect(facetCountHigh).toBeGreaterThan(facetCountLow);
657+
658+
sphere.delete();
659+
});
660+
661+
it("should save box shape as STL file and contain valid vertex coordinates", () => {
662+
const box = solid.createBox({ width: 4, length: 6, height: 8, center: [0, 0, 0] });
663+
const dto = new Inputs.OCCT.SaveStlDto(box, "box.stl", 0.01, false);
664+
const stl = io.saveShapeStl(dto);
665+
666+
// A box should have 12 triangular facets (2 per face, 6 faces)
667+
const facetCount = (stl.match(/facet normal/g) || []).length;
668+
expect(facetCount).toBe(12);
669+
670+
// Check that vertex coordinates are present
671+
const vertexMatches = stl.match(/vertex\s+[-\d.e+]+\s+[-\d.e+]+\s+[-\d.e+]+/g);
672+
expect(vertexMatches).not.toBeNull();
673+
expect(vertexMatches).toHaveLength(36); // 12 facets * 3 vertices each
674+
675+
box.delete();
676+
});
677+
678+
it("should load shape from STEP file with .stp extension", () => {
679+
const cube = solid.createCube({ size: 5, center: [0, 0, 0] });
680+
const stepText = io.saveShapeSTEP({ shape: cube, adjustYtoZ: false, fileName: "cube.stp" });
681+
const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "cube.stp", adjustZtoY: false });
682+
683+
const volumeOriginal = solid.getSolidVolume({ shape: cube });
684+
const volumeLoaded = solid.getSolidVolume({ shape: loaded });
685+
expect(volumeOriginal).toBeCloseTo(volumeLoaded);
686+
687+
cube.delete();
688+
loaded.delete();
689+
});
690+
691+
it("should load shape from STEP file with adjustZtoY enabled", () => {
692+
const cylinder = solid.createCylinder({ radius: 3, height: 10, direction: [0, 1, 0], center: [0, 0, 0] });
693+
const stepText = io.saveShapeSTEP({ shape: cylinder, adjustYtoZ: true, fileName: "cylinder.step" });
694+
const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "cylinder.step", adjustZtoY: true });
695+
696+
const volumeOriginal = solid.getSolidVolume({ shape: cylinder });
697+
const volumeLoaded = solid.getSolidVolume({ shape: loaded });
698+
expect(volumeOriginal).toBeCloseTo(volumeLoaded);
699+
700+
cylinder.delete();
701+
loaded.delete();
702+
});
703+
704+
it("should load sphere shape from STEP file and preserve volume", () => {
705+
const sphere = solid.createSphere({ radius: 7, center: [0, 0, 0] });
706+
const stepText = io.saveShapeSTEP({ shape: sphere, adjustYtoZ: false, fileName: "sphere.step" });
707+
const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "sphere.step", adjustZtoY: false });
708+
709+
const volumeOriginal = solid.getSolidVolume({ shape: sphere });
710+
const volumeLoaded = solid.getSolidVolume({ shape: loaded });
711+
expect(volumeOriginal).toBeCloseTo(volumeLoaded);
712+
713+
sphere.delete();
714+
loaded.delete();
715+
});
716+
717+
it("should load box shape from STEP file and preserve volume", () => {
718+
const box = solid.createBox({ width: 4, length: 6, height: 8, center: [0, 0, 0] });
719+
const stepText = io.saveShapeSTEP({ shape: box, adjustYtoZ: false, fileName: "box.step" });
720+
const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "box.step", adjustZtoY: false });
721+
722+
const volumeOriginal = solid.getSolidVolume({ shape: box });
723+
const volumeLoaded = solid.getSolidVolume({ shape: loaded });
724+
expect(volumeOriginal).toBeCloseTo(volumeLoaded);
725+
726+
box.delete();
727+
loaded.delete();
728+
});
729+
730+
it("should return undefined for unsupported file extension", () => {
731+
const result = io.loadSTEPorIGES({ filetext: "some content", fileName: "file.obj", adjustZtoY: false });
732+
expect(result).toBeUndefined();
733+
});
597734
});

packages/dev/occt/lib/services/io.ts

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -187,59 +187,59 @@ export class OCCTIO {
187187

188188
// TODO WIP! This function parses the contents of a `.STEP` file as an assembly
189189
// and returns the assembly structure. It's not finished yet and needs a lot of work.
190-
private parseStepAssembly(inputs: Inputs.OCCT.LoadStepOrIgesDto) {
191-
const fileName = inputs.fileName;
192-
const fileText = inputs.filetext;
193-
194-
// Write file to virtual filesystem
195-
this.occ.FS.createDataFile("/", "step_file.step", fileText as string, true, true, true);
196-
197-
try {
198-
// Create XCAF application and document
199-
const app = this.occ.XCAFApp_Application.GetApplication();
200-
const format = new this.occ.TCollection_ExtendedString_2("MDTV-XCAF", true);
201-
const docHandle = new this.occ.Handle_TDocStd_Document_1();
202-
app.get().NewDocument_2(format, docHandle);
203-
const doc = docHandle.get();
204-
205-
// Use STEPCAFControl_Reader to read with full XCAF support
206-
const reader = new this.occ.STEPCAFControl_Reader_1();
207-
reader.SetColorMode(true); // Enable color reading
208-
reader.SetNameMode(true); // Enable name reading
209-
reader.SetLayerMode(true); // Enable layer reading
210-
reader.SetPropsMode(true); // Enable properties reading
211-
212-
// Read the file
213-
const readResult = reader.ReadFile("step_file.step");
214-
215-
if (readResult === this.occ.IFSelect_ReturnStatus.IFSelect_RetDone) {
216-
// Transfer to XCAF document
217-
const messageProgress = new this.occ.Message_ProgressRange_1();
218-
const transferResult = reader.Transfer_1(docHandle, messageProgress);
219-
messageProgress.delete();
220-
221-
if (transferResult) {
222-
// Parse using the improved assembly parser
223-
// const assemblyStructure = this.ioAssembly.parseXCAFDocument(doc);
224-
225-
// Clean up
226-
reader.delete();
227-
format.delete();
228-
docHandle.delete();
229-
this.occ.FS.unlink("/step_file.step");
230-
231-
return {};
232-
} else {
233-
throw new Error("Failed to transfer STEP data to XCAF document");
234-
}
235-
} else {
236-
throw new Error("Failed to read STEP file");
237-
}
238-
} catch (error) {
239-
this.occ.FS.unlink("/step_file.step");
240-
throw error;
241-
}
242-
}
190+
// private parseStepAssembly(inputs: Inputs.OCCT.LoadStepOrIgesDto) {
191+
// const fileName = inputs.fileName;
192+
// const fileText = inputs.filetext;
193+
194+
// // Write file to virtual filesystem
195+
// this.occ.FS.createDataFile("/", "step_file.step", fileText as string, true, true, true);
196+
197+
// try {
198+
// // Create XCAF application and document
199+
// const app = this.occ.XCAFApp_Application.GetApplication();
200+
// const format = new this.occ.TCollection_ExtendedString_2("MDTV-XCAF", true);
201+
// const docHandle = new this.occ.Handle_TDocStd_Document_1();
202+
// app.get().NewDocument_2(format, docHandle);
203+
// const doc = docHandle.get();
204+
205+
// // Use STEPCAFControl_Reader to read with full XCAF support
206+
// const reader = new this.occ.STEPCAFControl_Reader_1();
207+
// reader.SetColorMode(true); // Enable color reading
208+
// reader.SetNameMode(true); // Enable name reading
209+
// reader.SetLayerMode(true); // Enable layer reading
210+
// reader.SetPropsMode(true); // Enable properties reading
211+
212+
// // Read the file
213+
// const readResult = reader.ReadFile("step_file.step");
214+
215+
// if (readResult === this.occ.IFSelect_ReturnStatus.IFSelect_RetDone) {
216+
// // Transfer to XCAF document
217+
// const messageProgress = new this.occ.Message_ProgressRange_1();
218+
// const transferResult = reader.Transfer_1(docHandle, messageProgress);
219+
// messageProgress.delete();
220+
221+
// if (transferResult) {
222+
// // Parse using the improved assembly parser
223+
// // const assemblyStructure = this.ioAssembly.parseXCAFDocument(doc);
224+
225+
// // Clean up
226+
// reader.delete();
227+
// format.delete();
228+
// docHandle.delete();
229+
// this.occ.FS.unlink("/step_file.step");
230+
231+
// return {};
232+
// } else {
233+
// throw new Error("Failed to transfer STEP data to XCAF document");
234+
// }
235+
// } else {
236+
// throw new Error("Failed to read STEP file");
237+
// }
238+
// } catch (error) {
239+
// this.occ.FS.unlink("/step_file.step");
240+
// throw error;
241+
// }
242+
// }
243243

244244
shapeToDxfPaths(inputs: Inputs.OCCT.ShapeToDxfPathsDto<TopoDS_Shape>): IO.DxfPathDto[] {
245245
return this.och.dxfService.shapeToDxfPaths(inputs);

0 commit comments

Comments
 (0)