Skip to content

Commit 35425c3

Browse files
committed
🐞 fix: fix the bug of exported STL files being empty.
1 parent 64db103 commit 35425c3

File tree

9 files changed

+73
-86
lines changed

9 files changed

+73
-86
lines changed

cpp/src/converter.cpp

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "shared.hpp"
55
#include <BRep_Builder.hxx>
66
#include <BRepTools.hxx>
7+
#include <BRepMesh_IncrementalMesh.hxx>
78
#include <emscripten/bind.h>
89
#include <emscripten/val.h>
910
#include <IGESCAFControl_Reader.hxx>
@@ -24,6 +25,7 @@
2425
#include <StlAPI_Writer.hxx>
2526
#include <BRepBuilderAPI_MakeSolid.hxx>
2627
#include <BRepBuilderAPI_Sewing.hxx>
28+
#include "utils.hpp"
2729

2830
using namespace emscripten;
2931

@@ -295,6 +297,15 @@ class Converter
295297
return sewing.SewedShape();
296298
}
297299

300+
static void writeBufferToFile(const std::string &fileName, const Uint8Array &buffer)
301+
{
302+
std::vector<uint8_t> input = convertJSArrayToNumberVector<uint8_t>(buffer);
303+
std::ofstream dummyFile;
304+
dummyFile.open(fileName, std::ios::binary);
305+
dummyFile.write((char *)input.data(), input.size());
306+
dummyFile.close();
307+
}
308+
298309
public:
299310
static std::string convertToBrep(const TopoDS_Shape &input)
300311
{
@@ -339,12 +350,8 @@ class Converter
339350

340351
static std::optional<ShapeNode> convertFromIges(const Uint8Array &buffer)
341352
{
342-
std::vector<uint8_t> input = convertJSArrayToNumberVector<uint8_t>(buffer);
343353
std::string dummyFileName = "temp.igs";
344-
std::ofstream dummyFile;
345-
dummyFile.open(dummyFileName, std::ios::binary);
346-
dummyFile.write((char *)input.data(), input.size());
347-
dummyFile.close();
354+
writeBufferToFile(dummyFileName, buffer);
348355

349356
IGESCAFControl_Reader igesCafReader;
350357
igesCafReader.SetColorMode(true);
@@ -394,73 +401,44 @@ class Converter
394401

395402
static std::optional<ShapeNode> convertFromStl(const Uint8Array &buffer)
396403
{
397-
std::vector<uint8_t> input = convertJSArrayToNumberVector<uint8_t>(buffer);
398404
std::string dummyFileName = "temp.stl";
399-
std::ofstream dummyFile;
400-
dummyFile.open(dummyFileName, std::ios::binary);
401-
dummyFile.write((char *)input.data(), input.size());
402-
dummyFile.close();
405+
writeBufferToFile(dummyFileName, buffer);
403406

404407
StlAPI_Reader stlReader;
405408
TopoDS_Shape shape;
406409
if (!stlReader.Read(shape, dummyFileName.c_str()))
407410
{
408-
std::remove(dummyFileName.c_str());
409411
return std::nullopt;
410412
}
411-
std::remove(dummyFileName.c_str());
412-
413-
TopoDS_Shape sewedShape = sewShapes({shape});
414413

415414
ShapeNode node = {
416-
.shape = sewedShape,
415+
.shape = shape,
417416
.color = std::nullopt,
418417
.children = {},
419-
.name = "STL Shape"
420-
};
418+
.name = "STL Shape"};
421419

422420
return node;
423421
}
424422

425-
static std::string convertToStl(const ShapeArray &input)
423+
static std::string convertToStl(const TopoDS_Shape &shapeToExport)
426424
{
427-
auto shapes = vecFromJSArray<TopoDS_Shape>(input);
428-
if (shapes.empty())
429-
{
430-
return std::string();
431-
}
432-
433-
TopoDS_Shape shapeToExport = shapes.size() == 1 ? shapes[0] : sewShapes(shapes);
434-
435425
std::string dummyFileName = "temp_export.stl";
426+
auto lineDeflection = boundingBoxRatio(shapeToExport, 0.05);
427+
BRepMesh_IncrementalMesh mesh(shapeToExport, lineDeflection, true, 0.2, true);
436428
StlAPI_Writer stlWriter;
437429
if (!stlWriter.Write(shapeToExport, dummyFileName.c_str()))
438430
{
439-
std::remove(dummyFileName.c_str());
431+
BRepTools::Clean(shapeToExport, true);
440432
return std::string();
441433
}
434+
BRepTools::Clean(shapeToExport, true);
442435

443-
// Read the generated file content
444-
std::ifstream file(dummyFileName, std::ios::binary | std::ios::ate);
445-
if (!file.is_open())
446-
{
447-
std::remove(dummyFileName.c_str());
448-
return std::string();
449-
}
450-
451-
std::streamsize size = file.tellg();
452-
file.seekg(0, std::ios::beg);
453-
454-
std::string content(size, '\0');
455-
if (!file.read(content.data(), size))
456-
{
457-
std::remove(dummyFileName.c_str());
458-
return std::string();
459-
}
460-
461-
file.close();
462-
std::remove(dummyFileName.c_str());
463-
return content;
436+
std::ifstream in(dummyFileName);
437+
std::istreambuf_iterator<char> beg(in), end;
438+
std::string str(beg, end);
439+
in.close();
440+
441+
return str;
464442
}
465443
};
466444

cpp/src/mesher.cpp

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,13 @@
2424
#include <TopoDS_Shape.hxx>
2525
#include <TopoDS.hxx>
2626
#include <UnitsMethods.hxx>
27+
#include "utils.hpp"
2728

2829
using namespace emscripten;
2930
using namespace std;
3031

3132
const double ANGLE_DEFLECTION = 0.2;
3233

33-
double boundingBoxRatio(const TopoDS_Shape &shape, double linearDeflection)
34-
{
35-
Bnd_Box boundingBox;
36-
BRepBndLib::Add(shape, boundingBox, false);
37-
if (boundingBox.IsVoid())
38-
{
39-
return linearDeflection;
40-
}
41-
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
42-
boundingBox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
43-
44-
Standard_Real avgSize = ((xMax - xMin) + (yMax - yMin) + (zMax - zMin)) / 3.0;
45-
double linDeflection = avgSize * linearDeflection;
46-
if (linDeflection < Precision::Confusion())
47-
{
48-
linDeflection = 1.0;
49-
}
50-
return linDeflection;
51-
}
52-
5334
void addPointToPosition(const gp_Pnt &pnt, std::optional<gp_Pnt> &prePnt, std::vector<float> &position)
5435
{
5536
if (prePnt.has_value())

cpp/src/utils.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Part of the Chili3d Project, under the AGPL-3.0 License.
22
// See LICENSE file in the project root for full license information.
33

4+
#include <Bnd_Box.hxx>
5+
#include <BRepBndLib.hxx>
46
#include <GeomAPI_ProjectPointOnCurve.hxx>
57
#include <GeomAPI_ExtremaCurveCurve.hxx>
68
#include "utils.hpp"
@@ -76,4 +78,24 @@ ProjectPointResult projectOrNearestCP(const Geom_Curve* curve, const gp_Pnt& pnt
7678
}
7779

7880
return nearestEnd(curve, pnt);
81+
}
82+
83+
double boundingBoxRatio(const TopoDS_Shape &shape, double linearDeflection)
84+
{
85+
Bnd_Box boundingBox;
86+
BRepBndLib::Add(shape, boundingBox, false);
87+
if (boundingBox.IsVoid())
88+
{
89+
return linearDeflection;
90+
}
91+
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
92+
boundingBox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
93+
94+
Standard_Real avgSize = ((xMax - xMin) + (yMax - yMin) + (zMax - zMin)) / 3.0;
95+
double linDeflection = avgSize * linearDeflection;
96+
if (linDeflection < Precision::Confusion())
97+
{
98+
linDeflection = 1.0;
99+
}
100+
return linDeflection;
79101
}

cpp/src/utils.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
#include <Geom_Curve.hxx>
77
#include <gp_Pnt.hxx>
88
#include "shared.hpp"
9+
#include <TopoDS_Shape.hxx>
910

1011
std::vector<ExtremaCCResult> extremaCCs(const Geom_Curve* curve1, const Geom_Curve* curve2, double maxDistance);
1112

1213
std::optional<ProjectPointResult> projectToCurve(const Geom_Curve* curve, gp_Pnt pnt);
1314

1415
ProjectPointResult nearestEnd(const Geom_Curve* curve, gp_Pnt pnt);
1516

16-
ProjectPointResult projectOrNearestCP(const Geom_Curve* curve, const gp_Pnt& pnt);
17+
ProjectPointResult projectOrNearestCP(const Geom_Curve* curve, const gp_Pnt& pnt);
18+
19+
double boundingBoxRatio(const TopoDS_Shape &shape, double linearDeflection);

packages/chili-builder/src/defaultDataExchange.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,21 @@ async function exportBrep(document: IDocument, shapes: IShape[]) {
2727
if (!comp.isOk) {
2828
return Result.err(comp.error);
2929
}
30-
return document.application.shapeFactory.converter.convertToBrep(comp.value);
30+
31+
const result = document.application.shapeFactory.converter.convertToBrep(comp.value);
32+
comp.value.dispose();
33+
return result;
3134
}
3235

3336
async function exportStl(document: IDocument, shapes: IShape[]) {
34-
return document.application.shapeFactory.converter.convertToSTL(...shapes);
37+
const comp = document.application.shapeFactory.combine(shapes);
38+
if (!comp.isOk) {
39+
return Result.err(comp.error);
40+
}
41+
42+
const result = document.application.shapeFactory.converter.convertToSTL(comp.value);
43+
comp.value.dispose();
44+
return result;
3545
}
3646

3747
export class DefaultDataExchange implements IDataExchange {
@@ -113,7 +123,7 @@ export class DefaultDataExchange implements IDataExchange {
113123
private async convertShapes(type: string, doc: IDocument, shapes: IShape[]) {
114124
if (type === ".step") return this.handleStepExport(doc, shapes);
115125
if (type === ".iges") return this.handleIgesExport(doc, shapes);
116-
if (type === ".stl") return this.handleStlExport(doc, shapes);
126+
if (type === ".stl") return exportStl(doc, shapes);
117127
return exportBrep(doc, shapes);
118128
}
119129

@@ -125,10 +135,6 @@ export class DefaultDataExchange implements IDataExchange {
125135
return doc.application.shapeFactory.converter.convertToIGES(...shapes);
126136
}
127137

128-
private handleStlExport(doc: IDocument, shapes: IShape[]) {
129-
return doc.application.shapeFactory.converter.convertToSTL(...shapes);
130-
}
131-
132138
private handleExportResult(result: Result<string> | undefined) {
133139
if (!result?.isOk) {
134140
PubSub.default.pub("showToast", "error.default:{0}", result?.error);

packages/chili-core/src/shape/shapeConverter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export interface IShapeConverter {
1313
convertFromSTEP(document: IDocument, step: Uint8Array): Result<FolderNode>;
1414
convertToBrep(shape: IShape): Result<string>;
1515
convertFromBrep(brep: string): Result<IShape>;
16-
convertToSTL(...shapes: IShape[]): Result<string>;
16+
convertToSTL(shape: IShape): Result<string>;
1717
convertFromSTL(document: IDocument, stl: Uint8Array): Result<FolderNode>;
1818
}

packages/chili-wasm/lib/chili-wasm.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,12 +577,12 @@ interface EmbindModule {
577577
Converter: {
578578
convertToBrep(_0: TopoDS_Shape): string;
579579
convertFromBrep(_0: EmbindString): TopoDS_Shape;
580+
convertToStl(_0: TopoDS_Shape): string;
580581
convertFromStep(_0: Uint8Array): ShapeNode | undefined;
581582
convertFromIges(_0: Uint8Array): ShapeNode | undefined;
582583
convertFromStl(_0: Uint8Array): ShapeNode | undefined;
583584
convertToStep(_0: Array<TopoDS_Shape>): string;
584585
convertToIges(_0: Array<TopoDS_Shape>): string;
585-
convertToStl(_0: Array<TopoDS_Shape>): string;
586586
};
587587
ShapeResult: {};
588588
ShapeFactory: {
-990 Bytes
Binary file not shown.

packages/chili-wasm/src/converter.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,11 @@ export class OccShapeConverter implements IShapeConverter {
118118
return Result.ok(OcctHelper.wrapShape(shape));
119119
}
120120

121-
convertToSTL(...shapes: IShape[]): Result<string> {
122-
let occShapes = shapes.map((shape) => {
123-
if (shape instanceof OccShape) {
124-
return shape.shape;
125-
}
126-
throw new Error("Shape is not an OccShape");
127-
});
128-
return Result.ok(wasm.Converter.convertToStl(occShapes));
121+
convertToSTL(shape: IShape): Result<string> {
122+
if (shape instanceof OccShape) {
123+
return Result.ok(wasm.Converter.convertToStl(shape.shape));
124+
}
125+
return Result.err("Shape is not an OccShape");
129126
}
130127

131128
convertFromSTL(document: IDocument, stl: Uint8Array): Result<FolderNode> {

0 commit comments

Comments
 (0)