When exporting STEP, identify the outer contours, and group them
and their holes into their own advanced faces. So a single surface with multiple outer contours generates multiple advanced faces. Also turn the default chord tol down to 1.5 pixels, seems more likely to make the exact surface Booleans work. [git-p4: depot-paths = "//depot/solvespace/": change = 1975]solver
parent
9455037e49
commit
603f47692e
220
exportstep.cpp
220
exportstep.cpp
|
@ -83,7 +83,50 @@ int StepFileWriter::ExportCurve(SBezier *sb) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StepFileWriter::ExportSurface(SSurface *ss) {
|
int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
|
||||||
|
List<int> listOfTrims;
|
||||||
|
ZERO(&listOfTrims);
|
||||||
|
|
||||||
|
SBezier *sb;
|
||||||
|
for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
|
||||||
|
int curveId = ExportCurve(sb);
|
||||||
|
|
||||||
|
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
||||||
|
id, CO(sb->Start()));
|
||||||
|
fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+1, id);
|
||||||
|
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
||||||
|
id+2, CO(sb->Finish()));
|
||||||
|
fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+3, id+2);
|
||||||
|
fprintf(f, "#%d=EDGE_CURVE('',#%d,#%d,#%d,%s);\n",
|
||||||
|
id+4, id+1, id+3, curveId, ".T.");
|
||||||
|
fprintf(f, "#%d=ORIENTED_EDGE('',*,*,#%d,.T.);\n",
|
||||||
|
id+5, id+4);
|
||||||
|
|
||||||
|
int oe = id+5;
|
||||||
|
listOfTrims.Add(&oe);
|
||||||
|
|
||||||
|
id += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "#%d=EDGE_LOOP('',(", id);
|
||||||
|
int *oe;
|
||||||
|
for(oe = listOfTrims.First(); oe; oe = listOfTrims.NextAfter(oe)) {
|
||||||
|
fprintf(f, "#%d", *oe);
|
||||||
|
if(listOfTrims.NextAfter(oe) != NULL) fprintf(f, ",");
|
||||||
|
}
|
||||||
|
fprintf(f, "));\n");
|
||||||
|
|
||||||
|
int fb = id + 1;
|
||||||
|
fprintf(f, "#%d=%s('',#%d,.T.);\n",
|
||||||
|
fb, inner ? "FACE_BOUND" : "FACE_OUTER_BOUND", id);
|
||||||
|
|
||||||
|
id += 2;
|
||||||
|
listOfTrims.Clear();
|
||||||
|
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StepFileWriter::ExportSurface(SSurface *ss) {
|
||||||
int i, j, srfid = id;
|
int i, j, srfid = id;
|
||||||
|
|
||||||
fprintf(f, "#%d=B_SPLINE_SURFACE_WITH_KNOTS('',%d,%d,(",
|
fprintf(f, "#%d=B_SPLINE_SURFACE_WITH_KNOTS('',%d,%d,(",
|
||||||
|
@ -113,6 +156,9 @@ int StepFileWriter::ExportSurface(SSurface *ss) {
|
||||||
|
|
||||||
id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1);
|
id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1);
|
||||||
|
|
||||||
|
// Get all of the loops of Beziers that trim our surface (with each
|
||||||
|
// Bezier split so that we use the section as t goes from 0 to 1), and
|
||||||
|
// the piecewise linearization of those loops in xyz space.
|
||||||
SBezierList sbl;
|
SBezierList sbl;
|
||||||
SPolygon sp;
|
SPolygon sp;
|
||||||
ZERO(&sbl);
|
ZERO(&sbl);
|
||||||
|
@ -122,66 +168,110 @@ int StepFileWriter::ExportSurface(SSurface *ss) {
|
||||||
ss->MakeSectionEdgesInto(shell, NULL, &sbl);
|
ss->MakeSectionEdgesInto(shell, NULL, &sbl);
|
||||||
SBezierLoopSet sbls = SBezierLoopSet::From(&sbl, &sp, &allClosed, &errorAt);
|
SBezierLoopSet sbls = SBezierLoopSet::From(&sbl, &sp, &allClosed, &errorAt);
|
||||||
|
|
||||||
List<int> listOfLoops;
|
// Convert the xyz piecewise linear to uv piecewise linear.
|
||||||
ZERO(&listOfLoops);
|
SContour *contour;
|
||||||
|
for(contour = sp.l.First(); contour; contour = sp.l.NextAfter(contour)) {
|
||||||
SBezierLoop *loop;
|
SPoint *pt;
|
||||||
for(loop = sbls.l.First(); loop; loop = sbls.l.NextAfter(loop)) {
|
for(pt = contour->l.First(); pt; pt = contour->l.NextAfter(pt)) {
|
||||||
List<int> listOfTrims;
|
double u, v;
|
||||||
ZERO(&listOfTrims);
|
ss->ClosestPointTo(pt->p, &u, &v);
|
||||||
|
pt->p = Vector::From(u, v, 0);
|
||||||
SBezier *sb;
|
|
||||||
for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
|
|
||||||
int curveId = ExportCurve(sb);
|
|
||||||
|
|
||||||
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
|
||||||
id, CO(sb->Start()));
|
|
||||||
fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+1, id);
|
|
||||||
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
|
||||||
id+2, CO(sb->Finish()));
|
|
||||||
fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+3, id+2);
|
|
||||||
fprintf(f, "#%d=EDGE_CURVE('',#%d,#%d,#%d,%s);\n",
|
|
||||||
id+4, id+1, id+3, curveId, ".T.");
|
|
||||||
fprintf(f, "#%d=ORIENTED_EDGE('',*,*,#%d,.T.);\n",
|
|
||||||
id+5, id+4);
|
|
||||||
|
|
||||||
i = id+5;
|
|
||||||
listOfTrims.Add(&i);
|
|
||||||
|
|
||||||
id += 6;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sp.normal = Vector::From(0, 0, 1);
|
||||||
|
|
||||||
fprintf(f, "#%d=EDGE_LOOP('',(", id);
|
static const int OUTER_LOOP = 10;
|
||||||
int *ei;
|
static const int INNER_LOOP = 20;
|
||||||
for(ei = listOfTrims.First(); ei; ei = listOfTrims.NextAfter(ei)) {
|
static const int USED_LOOP = 30;
|
||||||
fprintf(f, "#%d", *ei);
|
// Fix the contour directions; SBezierLoopSet::From() works only for
|
||||||
if(listOfTrims.NextAfter(ei) != NULL) fprintf(f, ",");
|
// planes, since it uses the polygon xyz space.
|
||||||
|
sp.FixContourDirections();
|
||||||
|
for(i = 0; i < sp.l.n; i++) {
|
||||||
|
SContour *contour = &(sp.l.elem[i]);
|
||||||
|
SBezierLoop *bl = &(sbls.l.elem[i]);
|
||||||
|
if(contour->tag) {
|
||||||
|
// This contour got reversed in the polygon to make the directions
|
||||||
|
// consistent, so the same must be necessary for the Bezier loop.
|
||||||
|
bl->Reverse();
|
||||||
|
}
|
||||||
|
if(contour->IsClockwiseProjdToNormal(sp.normal)) {
|
||||||
|
bl->tag = INNER_LOOP;
|
||||||
|
} else {
|
||||||
|
bl->tag = OUTER_LOOP;
|
||||||
}
|
}
|
||||||
fprintf(f, "));\n");
|
|
||||||
|
|
||||||
int fb = id + 1;
|
|
||||||
fprintf(f, "#%d=FACE_OUTER_BOUND('',#%d,.T.);\n", fb, id);
|
|
||||||
listOfLoops.Add(&fb);
|
|
||||||
|
|
||||||
id += 2;
|
|
||||||
listOfTrims.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int advFaceId = id;
|
|
||||||
fprintf(f, "#%d=ADVANCED_FACE('',(", advFaceId);
|
bool loopsRemaining = true;
|
||||||
int *fb;
|
while(loopsRemaining) {
|
||||||
for(fb = listOfLoops.First(); fb; fb = listOfLoops.NextAfter(fb)) {
|
loopsRemaining = false;
|
||||||
fprintf(f, "#%d", *fb);
|
for(i = 0; i < sbls.l.n; i++) {
|
||||||
if(listOfLoops.NextAfter(fb) != NULL) fprintf(f, ",");
|
SBezierLoop *loop = &(sbls.l.elem[i]);
|
||||||
|
if(loop->tag != OUTER_LOOP) continue;
|
||||||
|
|
||||||
|
// Check if this contour contains any outer loops; if it does, then
|
||||||
|
// we should do those "inner outer loops" first; otherwise we
|
||||||
|
// will steal their holes, since their holes also lie inside this
|
||||||
|
// contour.
|
||||||
|
for(j = 0; j < sbls.l.n; j++) {
|
||||||
|
SBezierLoop *outer = &(sbls.l.elem[j]);
|
||||||
|
if(i == j) continue;
|
||||||
|
if(outer->tag != OUTER_LOOP) continue;
|
||||||
|
|
||||||
|
Vector p = sp.l.elem[j].AnyEdgeMidpoint();
|
||||||
|
if(sp.l.elem[i].ContainsPointProjdToNormal(sp.normal, p)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(j < sbls.l.n) {
|
||||||
|
// It does, can't do this one yet.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
loopsRemaining = true;
|
||||||
|
loop->tag = USED_LOOP;
|
||||||
|
|
||||||
|
List<int> listOfLoops;
|
||||||
|
ZERO(&listOfLoops);
|
||||||
|
|
||||||
|
// Create the face outer boundary from the outer loop.
|
||||||
|
int fob = ExportCurveLoop(loop, false);
|
||||||
|
listOfLoops.Add(&fob);
|
||||||
|
|
||||||
|
// And create the face inner boundaries from any inner loops that
|
||||||
|
// lie within this contour.
|
||||||
|
for(j = 0; j < sbls.l.n; j++) {
|
||||||
|
SBezierLoop *inner = &(sbls.l.elem[j]);
|
||||||
|
if(inner->tag != INNER_LOOP) continue;
|
||||||
|
|
||||||
|
Vector p = sp.l.elem[j].AnyEdgeMidpoint();
|
||||||
|
if(sp.l.elem[i].ContainsPointProjdToNormal(sp.normal, p)) {
|
||||||
|
int fib = ExportCurveLoop(inner, true);
|
||||||
|
listOfLoops.Add(&fib);
|
||||||
|
|
||||||
|
inner->tag = USED_LOOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And now create the face that corresponds to this outer loop
|
||||||
|
// and all of its holes.
|
||||||
|
int advFaceId = id;
|
||||||
|
fprintf(f, "#%d=ADVANCED_FACE('',(", advFaceId);
|
||||||
|
int *fb;
|
||||||
|
for(fb = listOfLoops.First(); fb; fb = listOfLoops.NextAfter(fb)) {
|
||||||
|
fprintf(f, "#%d", *fb);
|
||||||
|
if(listOfLoops.NextAfter(fb) != NULL) fprintf(f, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "),#%d,.T.);\n", srfid);
|
||||||
|
fprintf(f, "\n");
|
||||||
|
advancedFaces.Add(&advFaceId);
|
||||||
|
|
||||||
|
id++;
|
||||||
|
|
||||||
|
listOfLoops.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "),#%d,.T.);\n", srfid);
|
|
||||||
fprintf(f, "\n");
|
|
||||||
|
|
||||||
id++;
|
|
||||||
|
|
||||||
listOfLoops.Clear();
|
|
||||||
return advFaceId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StepFileWriter::ExportTo(char *file) {
|
void StepFileWriter::ExportTo(char *file) {
|
||||||
|
@ -190,8 +280,8 @@ void StepFileWriter::ExportTo(char *file) {
|
||||||
if(shell->surface.n == 0) {
|
if(shell->surface.n == 0) {
|
||||||
Error("The model does not contain any surfaces to export.%s",
|
Error("The model does not contain any surfaces to export.%s",
|
||||||
g->runningMesh.l.n > 0 ?
|
g->runningMesh.l.n > 0 ?
|
||||||
"\r\nThe model does contain triangles from a mesh, but a "
|
"\r\n\r\nThe model does contain triangles from a mesh, but "
|
||||||
"triangle mesh cannot be exported as a STEP file. Try "
|
"a triangle mesh cannot be exported as a STEP file. Try "
|
||||||
"File -> Export Mesh... instead." : "");
|
"File -> Export Mesh... instead." : "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -206,22 +296,20 @@ void StepFileWriter::ExportTo(char *file) {
|
||||||
|
|
||||||
id = 200;
|
id = 200;
|
||||||
|
|
||||||
List<int> ls;
|
ZERO(&advancedFaces);
|
||||||
ZERO(&ls);
|
|
||||||
|
|
||||||
SSurface *ss;
|
SSurface *ss;
|
||||||
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
|
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
|
||||||
if(ss->trim.n == 0) continue;
|
if(ss->trim.n == 0) continue;
|
||||||
|
|
||||||
int sid = ExportSurface(ss);
|
ExportSurface(ss);
|
||||||
ls.Add(&sid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "#%d=CLOSED_SHELL('',(", id);
|
fprintf(f, "#%d=CLOSED_SHELL('',(", id);
|
||||||
int *es;
|
int *af;
|
||||||
for(es = ls.First(); es; es = ls.NextAfter(es)) {
|
for(af = advancedFaces.First(); af; af = advancedFaces.NextAfter(af)) {
|
||||||
fprintf(f, "#%d", *es);
|
fprintf(f, "#%d", *af);
|
||||||
if(ls.NextAfter(es) != NULL) fprintf(f, ",");
|
if(advancedFaces.NextAfter(af) != NULL) fprintf(f, ",");
|
||||||
}
|
}
|
||||||
fprintf(f, "));\n");
|
fprintf(f, "));\n");
|
||||||
fprintf(f, "#%d=MANIFOLD_SOLID_BREP('brep_1',#%d);\n", id+1, id);
|
fprintf(f, "#%d=MANIFOLD_SOLID_BREP('brep_1',#%d);\n", id+1, id);
|
||||||
|
@ -238,6 +326,6 @@ void StepFileWriter::ExportTo(char *file) {
|
||||||
);
|
);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
ls.Clear();
|
advancedFaces.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,6 +335,11 @@ Vector SContour::ComputeNormal(void) {
|
||||||
return n.WithMagnitude(1);
|
return n.WithMagnitude(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector SContour::AnyEdgeMidpoint(void) {
|
||||||
|
if(l.n < 2) oops();
|
||||||
|
return ((l.elem[0].p).Plus(l.elem[1].p)).ScaledBy(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
bool SContour::IsClockwiseProjdToNormal(Vector n) {
|
bool SContour::IsClockwiseProjdToNormal(Vector n) {
|
||||||
// Degenerate things might happen as we draw; doesn't really matter
|
// Degenerate things might happen as we draw; doesn't really matter
|
||||||
// what we do then.
|
// what we do then.
|
||||||
|
|
|
@ -59,6 +59,7 @@ public:
|
||||||
void OffsetInto(SContour *dest, double r);
|
void OffsetInto(SContour *dest, double r);
|
||||||
void CopyInto(SContour *dest);
|
void CopyInto(SContour *dest);
|
||||||
void FindPointWithMinX(void);
|
void FindPointWithMinX(void);
|
||||||
|
Vector AnyEdgeMidpoint(void);
|
||||||
|
|
||||||
bool IsEar(int bp);
|
bool IsEar(int bp);
|
||||||
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
||||||
|
|
|
@ -40,7 +40,7 @@ void SolveSpace::Init(char *cmdLine) {
|
||||||
lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" );
|
lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" );
|
||||||
lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" );
|
lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" );
|
||||||
// Chord tolerance
|
// Chord tolerance
|
||||||
chordTol = CnfThawFloat(3.0f, "ChordTolerance");
|
chordTol = CnfThawFloat(1.5f, "ChordTolerance");
|
||||||
// Max pwl segments to generate
|
// Max pwl segments to generate
|
||||||
maxSegments = CnfThawDWORD(10, "MaxSegments");
|
maxSegments = CnfThawDWORD(10, "MaxSegments");
|
||||||
// View units
|
// View units
|
||||||
|
|
|
@ -430,8 +430,10 @@ public:
|
||||||
void ExportTo(char *filename);
|
void ExportTo(char *filename);
|
||||||
void WriteHeader(void);
|
void WriteHeader(void);
|
||||||
int ExportCurve(SBezier *sb);
|
int ExportCurve(SBezier *sb);
|
||||||
int ExportSurface(SSurface *ss);
|
int ExportCurveLoop(SBezierLoop *loop, bool inner);
|
||||||
|
void ExportSurface(SSurface *ss);
|
||||||
|
|
||||||
|
List<int> advancedFaces;
|
||||||
SShell *shell;
|
SShell *shell;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int id;
|
int id;
|
||||||
|
|
|
@ -101,6 +101,7 @@ public:
|
||||||
|
|
||||||
class SBezierLoop {
|
class SBezierLoop {
|
||||||
public:
|
public:
|
||||||
|
int tag;
|
||||||
List<SBezier> l;
|
List<SBezier> l;
|
||||||
|
|
||||||
inline void Clear(void) { l.Clear(); }
|
inline void Clear(void) { l.Clear(); }
|
||||||
|
|
|
@ -38,17 +38,20 @@ void SPolygon::UvTriangulateInto(SMesh *m, SSurface *srf) {
|
||||||
List<Vector> vl;
|
List<Vector> vl;
|
||||||
ZERO(&vl);
|
ZERO(&vl);
|
||||||
|
|
||||||
// And now find all of its holes;
|
// And now find all of its holes. Note that we will also find any
|
||||||
|
// outer contours that lie entirely within this contour, and any
|
||||||
|
// holes for those contours. But that's okay, because we can merge
|
||||||
|
// those too.
|
||||||
SContour *sc;
|
SContour *sc;
|
||||||
for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
|
for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
|
||||||
if(sc->timesEnclosed != 1) continue;
|
if(sc->timesEnclosed != 1) continue;
|
||||||
if(sc->l.n < 2) continue;
|
if(sc->l.n < 2) continue;
|
||||||
|
|
||||||
// Test the midpoint of an edge. Our polygon may not be self-
|
// Test the midpoint of an edge. Our polygon may not be self-
|
||||||
// intersecting, but two countours may share a vertex; so a
|
// intersecting, but two contours may share a vertex; so a
|
||||||
// vertex could be on the edge of another polygon, in which
|
// vertex could be on the edge of another polygon, in which
|
||||||
// case ContainsPointProjdToNormal returns indeterminate.
|
// case ContainsPointProjdToNormal returns indeterminate.
|
||||||
Vector tp = ((sc->l.elem[0].p).Plus(sc->l.elem[1].p)).ScaledBy(0.5);
|
Vector tp = sc->AnyEdgeMidpoint();
|
||||||
if(top->ContainsPointProjdToNormal(normal, tp)) {
|
if(top->ContainsPointProjdToNormal(normal, tp)) {
|
||||||
sc->tag = 2;
|
sc->tag = 2;
|
||||||
sc->MakeEdgesInto(&el);
|
sc->MakeEdgesInto(&el);
|
||||||
|
|
Loading…
Reference in New Issue