diff --git a/draw.cpp b/draw.cpp index 1ae0eb6..fc349fa 100644 --- a/draw.cpp +++ b/draw.cpp @@ -1026,7 +1026,7 @@ void GraphicsWindow::Paint(int w, int h) { // And the naked edges, if the user did Analyze -> Show Naked Edges. glLineWidth(7); glColor3d(1, 0, 0); - glxDrawEdges(&(SS.nakedEdges)); + glxDrawEdges(&(SS.nakedEdges), true); // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); diff --git a/export.cpp b/export.cpp index f68f111..f29b9cc 100644 --- a/export.cpp +++ b/export.cpp @@ -7,7 +7,7 @@ void SolveSpace::ExportSectionTo(char *filename) { Group *g = SK.GetGroup(SS.GW.activeGroup); g->GenerateDisplayItems(); - if(g->displayMesh.l.n == 0) { + if(g->displayMesh.IsEmpty()) { Error("No solid model present; draw one with extrudes and revolves, " "or use Export 2d View to export bare lines and curves."); return; @@ -100,7 +100,7 @@ void SolveSpace::ExportViewTo(char *filename) { g->GenerateDisplayItems(); sm = &(g->displayMesh); } - if(sm->l.n == 0) { + if(sm->IsEmpty()) { sm = NULL; } @@ -979,7 +979,7 @@ void HpglFileWriter::FinishAndCloseFile(void) { //----------------------------------------------------------------------------- void SolveSpace::ExportMeshTo(char *filename) { SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh); - if(m->l.n == 0) { + if(m->IsEmpty()) { Error("Active group mesh is empty; nothing to export."); return; } diff --git a/glhelper.cpp b/glhelper.cpp index 8f5f93d..82a5c13 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -293,7 +293,7 @@ void glxDebugPolygon(SPolygon *p) } } -void glxDrawEdges(SEdgeList *el) +void glxDrawEdges(SEdgeList *el, bool endpointsToo) { SEdge *se; glBegin(GL_LINES); @@ -302,13 +302,16 @@ void glxDrawEdges(SEdgeList *el) glxVertex3v(se->b); } glEnd(); - glPointSize(12); - glBegin(GL_POINTS); - for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - glxVertex3v(se->a); - glxVertex3v(se->b); + + if(endpointsToo) { + glPointSize(12); + glBegin(GL_POINTS); + for(se = el->l.First(); se; se = el->l.NextAfter(se)) { + glxVertex3v(se->a); + glxVertex3v(se->b); + } + glEnd(); } - glEnd(); } void glxDebugMesh(SMesh *m) diff --git a/groupmesh.cpp b/groupmesh.cpp index e51c39d..4492800 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -51,15 +51,35 @@ void Group::GenerateLoops(void) { } } -void Group::GenerateShellForStepAndRepeat(void) { - Group *src = SK.GetGroup(opA); - SShell *srcs = &(src->thisShell); // the shell to step and repeat +void SShell::RemapFaces(Group *g, int remap) { + SSurface *ss; + for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){ + hEntity face = { ss->face }; + if(face.v == Entity::NO_ENTITY.v) continue; - SShell workA, workB; + face = g->Remap(face, remap); + ss->face = face.v; + } +} + +void SMesh::RemapFaces(Group *g, int remap) { + STriangle *tr; + for(tr = l.First(); tr; tr = l.NextAfter(tr)) { + hEntity face = { tr->meta.face }; + if(face.v == Entity::NO_ENTITY.v) continue; + + face = g->Remap(face, remap); + tr->meta.face = face.v; + } +} + +template +void Group::GenerateForStepAndRepeat(T *prevs, T *steps, T *outs, int how) { + T workA, workB; ZERO(&workA); ZERO(&workB); - SShell *soFar = &workA, *scratch = &workB; - soFar->MakeFromCopyOf(&(src->PreviousGroup()->runningShell)); + T *soFar = &workA, *scratch = &workB; + soFar->MakeFromCopyOf(prevs); int n = (int)valA, a0 = 0; if(subtype == ONE_SIDED && skipFirst) { @@ -70,13 +90,12 @@ void Group::GenerateShellForStepAndRepeat(void) { int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1)); int remap = (a == (n - 1)) ? REMAP_LAST : a; - SShell transd; + T transd; ZERO(&transd); if(type == TRANSLATE) { Vector trans = Vector::From(h.param(0), h.param(1), h.param(2)); trans = trans.ScaledBy(ap); - Quaternion q = Quaternion::From(1, 0, 0, 0); - transd.MakeFromTransformationOf(srcs, trans, q); + transd.MakeFromTransformationOf(steps, trans, Quaternion::IDENTITY); } else { Vector trans = Vector::From(h.param(0), h.param(1), h.param(2)); double theta = ap * SK.GetParam(h.param(3))->val; @@ -84,43 +103,88 @@ void Group::GenerateShellForStepAndRepeat(void) { Vector axis = Vector::From(h.param(4), h.param(5), h.param(6)); Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z); // Rotation is centered at t; so A(x - t) + t = Ax + (t - At) - transd.MakeFromTransformationOf(srcs, + transd.MakeFromTransformationOf(steps, trans.Minus(q.Rotate(trans)), q); } // We need to rewrite any plane face entities to the transformed ones. - SSurface *ss; - for(ss = transd.surface.First(); ss; ss = transd.surface.NextAfter(ss)){ - hEntity face = { ss->face }; - if(face.v == Entity::NO_ENTITY.v) continue; + transd.RemapFaces(this, remap); - face = Remap(face, remap); - ss->face = face.v; - } - - if(src->meshCombine == COMBINE_AS_DIFFERENCE) { + if(how == COMBINE_AS_DIFFERENCE) { scratch->MakeFromDifferenceOf(soFar, &transd); - } else if(src->meshCombine == COMBINE_AS_UNION) { + } else if(how == COMBINE_AS_UNION) { scratch->MakeFromUnionOf(soFar, &transd); } else { scratch->MakeFromAssemblyOf(soFar, &transd); } - SWAP(SShell *, scratch, soFar); + SWAP(T *, scratch, soFar); scratch->Clear(); transd.Clear(); } - runningShell.Clear(); - runningShell = *soFar; + outs->Clear(); + *outs = *soFar; +} + +template +void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs) { + // If this group contributes no new mesh, then our running mesh is the + // same as last time, no combining required. Likewise if we have a mesh + // but it's suppressed. + if(thiss->IsEmpty() || suppress) { + outs->MakeFromCopyOf(prevs); + return; + } + + // So our group's shell appears in thisShell. Combine this with the + // previous group's shell, using the requested operation. + if(meshCombine == COMBINE_AS_UNION) { + outs->MakeFromUnionOf(prevs, thiss); + } else if(meshCombine == COMBINE_AS_DIFFERENCE) { + outs->MakeFromDifferenceOf(prevs, thiss); + } else { + outs->MakeFromAssemblyOf(prevs, thiss); + } } void Group::GenerateShellAndMesh(void) { thisShell.Clear(); + thisMesh.Clear(); + runningShell.Clear(); + runningMesh.Clear(); if(type == TRANSLATE || type == ROTATE) { - GenerateShellForStepAndRepeat(); - goto done; + Group *src = SK.GetGroup(opA); + Group *pg = src->PreviousGroup(); + + if(src->thisMesh.IsEmpty() && pg->runningMesh.IsEmpty() && !forceToMesh) + { + SShell *toStep = &(src->thisShell), + *prev = &(pg->runningShell); + + GenerateForStepAndRepeat + (prev, toStep, &runningShell, src->meshCombine); + } else { + SMesh prevm, stepm; + ZERO(&prevm); + ZERO(&stepm); + + prevm.MakeFromCopyOf(&(pg->runningMesh)); + pg->runningShell.TriangulateInto(&prevm); + + stepm.MakeFromCopyOf(&(src->thisMesh)); + src->thisShell.TriangulateInto(&stepm); + + GenerateForStepAndRepeat + (&prevm, &stepm, &runningMesh, src->meshCombine); + + stepm.Clear(); + prevm.Clear(); + } + + displayDirty = true; + return; } if(type == EXTRUDE) { @@ -205,51 +269,38 @@ void Group::GenerateShellAndMesh(void) { SK.GetParam(h.param(5))->val, SK.GetParam(h.param(6))->val }; - for(int i = 0; i < impMesh.l.n; i++) { - STriangle st = impMesh.l.elem[i]; - - if(st.meta.face != 0) { - hEntity he = { st.meta.face }; - st.meta.face = Remap(he, 0).v; - } - st.a = q.Rotate(st.a).Plus(offset); - st.b = q.Rotate(st.b).Plus(offset); - st.c = q.Rotate(st.c).Plus(offset); - } + thisMesh.MakeFromTransformationOf(&impMesh, offset, q); + thisMesh.RemapFaces(this, 0); thisShell.MakeFromTransformationOf(&impShell, offset, q); - SSurface *srf; - IdList *sl = &(thisShell.surface); - for(srf = sl->First(); srf; srf = sl->NextAfter(srf)) { - if(srf->face != 0) { - hEntity he = { srf->face }; - srf->face = Remap(he, 0).v; - } - } + thisShell.RemapFaces(this, 0); } - runningShell.Clear(); + // So now we've got the mesh or shell for this group. Combine it with + // the previous group's mesh or shell with the requested Boolean, and + // we're done. - // If this group contributes no new mesh, then our running mesh is the - // same as last time, no combining required. Likewise if we have a mesh - // but it's suppressed. - if(suppress) { - runningShell.MakeFromCopyOf(&(PreviousGroup()->runningShell)); - goto done; - } - - // So our group's shell appears in thisShell. Combine this with the - // previous group's shell, using the requested operation. - SShell *a = &(PreviousGroup()->runningShell); - if(meshCombine == COMBINE_AS_UNION) { - runningShell.MakeFromUnionOf(a, &thisShell); - } else if(meshCombine == COMBINE_AS_DIFFERENCE) { - runningShell.MakeFromDifferenceOf(a, &thisShell); + Group *pg = PreviousGroup(); + if(pg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) { + SShell *prevs = &(pg->runningShell); + GenerateForBoolean(prevs, &thisShell, &runningShell); } else { - runningShell.MakeFromAssemblyOf(a, &thisShell); + SMesh prevm, thism; + ZERO(&prevm); + ZERO(&thism); + + prevm.MakeFromCopyOf(&(pg->runningMesh)); + pg->runningShell.TriangulateInto(&prevm); + + thism.MakeFromCopyOf(&thisMesh); + thisShell.TriangulateInto(&thism); + + GenerateForBoolean(&prevm, &thism, &runningMesh); + + thism.Clear(); + prevm.Clear(); } -done: displayDirty = true; } @@ -257,8 +308,19 @@ void Group::GenerateDisplayItems(void) { if(displayDirty) { displayMesh.Clear(); runningShell.TriangulateInto(&displayMesh); + STriangle *tr; + for(tr = runningMesh.l.First(); tr; tr = runningMesh.l.NextAfter(tr)) { + STriangle trn = *tr; + Vector n = trn.Normal(); + trn.an = n; + trn.bn = n; + trn.cn = n; + displayMesh.AddTriangle(&trn); + } + displayEdges.Clear(); runningShell.MakeEdgesInto(&displayEdges); + displayDirty = false; } } @@ -314,7 +376,7 @@ void Group::Draw(void) { glxColor3d(REDf (SS.edgeColor), GREENf(SS.edgeColor), BLUEf (SS.edgeColor)); - glxDrawEdges(&displayEdges); + glxDrawEdges(&displayEdges, false); } if(SS.GW.showMesh) glxDebugMesh(&displayMesh); diff --git a/mesh.cpp b/mesh.cpp index e1380b4..787b972 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -206,7 +206,7 @@ void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) { } } -void SMesh::MakeFromUnion(SMesh *a, SMesh *b) { +void SMesh::MakeFromUnionOf(SMesh *a, SMesh *b) { SBsp3 *bspa = SBsp3::FromMesh(a); SBsp3 *bspb = SBsp3::FromMesh(b); @@ -219,7 +219,7 @@ void SMesh::MakeFromUnion(SMesh *a, SMesh *b) { AddAgainstBsp(a, bspb); } -void SMesh::MakeFromDifference(SMesh *a, SMesh *b) { +void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) { SBsp3 *bspa = SBsp3::FromMesh(a); SBsp3 *bspb = SBsp3::FromMesh(b); @@ -232,40 +232,33 @@ void SMesh::MakeFromDifference(SMesh *a, SMesh *b) { AddAgainstBsp(a, bspb); } -bool SMesh::MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *error) { - SBsp3 *bspa = SBsp3::FromMesh(srca); - SBsp3 *bspb = SBsp3::FromMesh(srcb); - - error->Clear(); - error->flipNormal = true; - error->keepCoplanar = false; - - error->AddAgainstBsp(srcb, bspa); - error->AddAgainstBsp(srca, bspb); - // Now we have a list of all the triangles (or fragments thereof) from - // A that lie inside B, or vice versa. That's the interference, and - // we report it so that it can be flagged. - - // For the actual output, take the union. - flipNormal = false; - keepCoplanar = false; - AddAgainstBsp(srcb, bspa); - - flipNormal = false; - keepCoplanar = true; - AddAgainstBsp(srca, bspb); - - // And we're successful if the intersection was empty. - return (error->l.n == 0); -} - -void SMesh::MakeFromCopy(SMesh *a) { +void SMesh::MakeFromCopyOf(SMesh *a) { int i; for(i = 0; i < a->l.n; i++) { AddTriangle(&(a->l.elem[i])); } } +void SMesh::MakeFromAssemblyOf(SMesh *a, SMesh *b) { + MakeFromCopyOf(a); + MakeFromCopyOf(b); +} + +void SMesh::MakeFromTransformationOf(SMesh *a, Vector trans, Quaternion q) { + STriangle *tr; + for(tr = a->l.First(); tr; tr = a->l.NextAfter(tr)) { + STriangle tt = *tr; + tt.a = (q.Rotate(tt.a)).Plus(trans); + tt.b = (q.Rotate(tt.b)).Plus(trans); + tt.c = (q.Rotate(tt.c)).Plus(trans); + AddTriangle(&tt); + } +} + +bool SMesh::IsEmpty(void) { + return (l.n == 0); +} + DWORD SMesh::FirstIntersectionWith(Point2d mp) { Vector p0 = Vector::From(mp.x, mp.y, 0); Vector gn = Vector::From(0, 0, 1); diff --git a/polygon.h b/polygon.h index 183edff..ff1a113 100644 --- a/polygon.h +++ b/polygon.h @@ -184,10 +184,15 @@ public: void Simplify(int start); void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3); - void MakeFromUnion(SMesh *a, SMesh *b); - void MakeFromDifference(SMesh *a, SMesh *b); - bool MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *errorAt); - void MakeFromCopy(SMesh *a); + void MakeFromUnionOf(SMesh *a, SMesh *b); + void MakeFromDifferenceOf(SMesh *a, SMesh *b); + + void MakeFromCopyOf(SMesh *a); + void MakeFromTransformationOf(SMesh *a, Vector trans, Quaternion q); + void MakeFromAssemblyOf(SMesh *a, SMesh *b); + + bool IsEmpty(void); + void RemapFaces(Group *g, int remap); DWORD FirstIntersectionWith(Point2d mp); }; diff --git a/sketch.h b/sketch.h index 88ff6a7..96fb9df 100644 --- a/sketch.h +++ b/sketch.h @@ -208,9 +208,10 @@ public: void GenerateLoops(void); // And the mesh stuff Group *PreviousGroup(void); - void GenerateShellForStepAndRepeat(void); void GenerateDisplayItems(void); void GenerateShellAndMesh(void); + template void GenerateForStepAndRepeat(T *a, T *b, T *o, int how); + template void GenerateForBoolean(T *a, T *b, T *o); void Draw(void); SPolygon GetPolygon(void); diff --git a/solvespace.h b/solvespace.h index e0a1fbb..8a60a79 100644 --- a/solvespace.h +++ b/solvespace.h @@ -149,7 +149,7 @@ void vl(void); // debug function to validate heaps // End of platform-specific functions //================ - +class Group; class SSurface; #include "dsc.h" #include "polygon.h" @@ -175,7 +175,7 @@ void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p); void glxFillPolygon(SPolygon *p); void glxFillMesh(int color, SMesh *m, DWORD h, DWORD s1, DWORD s2); void glxDebugPolygon(SPolygon *p); -void glxDrawEdges(SEdgeList *l); +void glxDrawEdges(SEdgeList *l, bool endpointsToo); void glxDebugMesh(SMesh *m); void glxMarkPolygonNormal(SPolygon *p); void glxWriteText(char *str); diff --git a/srf/surface.cpp b/srf/surface.cpp index 0ffcc63..3b685c2 100644 --- a/srf/surface.cpp +++ b/srf/surface.cpp @@ -666,6 +666,10 @@ void SShell::TriangulateInto(SMesh *sm) { } } +bool SShell::IsEmpty(void) { + return (surface.n == 0); +} + void SShell::Clear(void) { SSurface *s; for(s = surface.First(); s; s = surface.NextAfter(s)) { diff --git a/srf/surface.h b/srf/surface.h index 33ddf86..25492df 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -319,6 +319,8 @@ public: void MakeEdgesInto(SEdgeList *sel); void MakeSectionEdgesInto(Vector n, double d, SEdgeList *sel, SBezierList *sbl); + bool IsEmpty(void); + void RemapFaces(Group *g, int remap); void Clear(void); }; diff --git a/textscreens.cpp b/textscreens.cpp index 2144b3f..0b4a317 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -463,7 +463,7 @@ void TextWindow::ShowGroupInfo(void) { g->runningShell.surface.n > 0)) { Group *pg = g->PreviousGroup(); - if(pg->runningMesh.l.n == 0 && g->thisMesh.l.n == 0) { + if(pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) { bool fm = g->forceToMesh; Printf(true, "%FtSURFACES%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", @@ -473,7 +473,7 @@ void TextWindow::ShowGroupInfo(void) { (fm ? "" : "as mesh"), (fm ? "as mesh" : "")); } else { Printf(false, - "%FtSURFACES%E %Fas mesh%FE"); + "%FtSURFACES%E %Fsas mesh%E"); } }