diff --git a/CHANGELOG.md b/CHANGELOG.md index 32dfd905..a3845fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Changelog --- New sketch features: + * New intersection boolean operation for solid models. * New groups, revolution and helical extrusion. * Extrude, lathe, translate and rotate groups can use the "assembly" boolean operation, to increase performance. diff --git a/src/groupmesh.cpp b/src/groupmesh.cpp index b2e390e1..8117834e 100644 --- a/src/groupmesh.cpp +++ b/src/groupmesh.cpp @@ -143,7 +143,7 @@ void Group::GenerateForStepAndRepeat(T *steps, T *outs, Group::CombineAs forWhat // And tack this transformed copy on to the return. if(soFar->IsEmpty()) { scratch->MakeFromCopyOf(&transd); - } else if (forWhat == CombineAs::ASSEMBLE) { + } else if(forWhat == CombineAs::ASSEMBLE) { scratch->MakeFromAssemblyOf(soFar, &transd); } else { scratch->MakeFromUnionOf(soFar, &transd); @@ -170,12 +170,22 @@ void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs, Group::CombineAs how // So our group's shell appears in thisShell. Combine this with the // previous group's shell, using the requested operation. - if(how == CombineAs::UNION) { - outs->MakeFromUnionOf(prevs, thiss); - } else if(how == CombineAs::DIFFERENCE) { - outs->MakeFromDifferenceOf(prevs, thiss); - } else { - outs->MakeFromAssemblyOf(prevs, thiss); + switch(how) { + case CombineAs::UNION: + outs->MakeFromUnionOf(prevs, thiss); + break; + + case CombineAs::DIFFERENCE: + outs->MakeFromDifferenceOf(prevs, thiss); + break; + + case CombineAs::INTERSECTION: + outs->MakeFromIntersectionOf(prevs, thiss); + break; + + case CombineAs::ASSEMBLE: + outs->MakeFromAssemblyOf(prevs, thiss); + break; } } diff --git a/src/mesh.cpp b/src/mesh.cpp index 047d6112..0a8478ab 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -286,6 +286,23 @@ void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) { AddAgainstBsp(a, bspb); } +void SMesh::MakeFromIntersectionOf(SMesh *a, SMesh *b) { + // Emulate triangle mesh intersection with difference + // by doing C=A-(A-B). Figure out how to do it properly later. + SMesh c = {}; + c.MakeFromDifferenceOf(a, b); + MakeFromDifferenceOf(a, &c); + + c.Clear(); + +/* SBsp3 *bspa = SBsp3::FromMesh(a); + SBsp3 *bspb = SBsp3::FromMesh(b); + + AddAgainstBsp(a, bspb); + AddAgainstBsp(b, bspa); +*/ +} + void SMesh::MakeFromCopyOf(SMesh *a) { ssassert(this != a, "Can't make from copy of self"); for(int i = 0; i < a->l.n; i++) { diff --git a/src/polygon.h b/src/polygon.h index 948106f7..a2afafec 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -269,6 +269,7 @@ public: void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3); void MakeFromUnionOf(SMesh *a, SMesh *b); void MakeFromDifferenceOf(SMesh *a, SMesh *b); + void MakeFromIntersectionOf(SMesh *a, SMesh *b); void MakeFromCopyOf(SMesh *a); void MakeFromTransformationOf(SMesh *a, Vector trans, diff --git a/src/sketch.h b/src/sketch.h index a4ef89a4..72cda7eb 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -239,7 +239,8 @@ public: enum class CombineAs : uint32_t { UNION = 0, DIFFERENCE = 1, - ASSEMBLE = 2 + ASSEMBLE = 2, + INTERSECTION = 3 }; CombineAs meshCombine; diff --git a/src/srf/boolean.cpp b/src/srf/boolean.cpp index f01ce625..5ee54ff6 100644 --- a/src/srf/boolean.cpp +++ b/src/srf/boolean.cpp @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- -// Top-level functions to compute the Boolean union or difference between -// two shells of rational polynomial surfaces. +// Top-level functions to compute the Boolean union, difference or intersection +// between two shells of rational polynomial surfaces. // // Copyright 2008-2013 Jonathan Westhues. //----------------------------------------------------------------------------- @@ -16,6 +16,10 @@ void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) { MakeFromBoolean(a, b, SSurface::CombineAs::DIFFERENCE); } +void SShell::MakeFromIntersectionOf(SShell *a, SShell *b) { + MakeFromBoolean(a, b, SSurface::CombineAs::INTERSECTION); +} + //----------------------------------------------------------------------------- // Take our original pwl curve. Wherever an edge intersects a surface within // either agnstA or agnstB, split the piecewise linear element. Then refine @@ -213,6 +217,13 @@ static bool KeepRegion(SSurface::CombineAs type, bool opA, SShell::Class shell, return inShell || inSame; } + case SSurface::CombineAs::INTERSECTION: + if(opA) { + return inShell; + } else { + return inShell || inSame; + } + default: ssassert(false, "Unexpected combine type"); } } @@ -463,7 +474,10 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, // point opposite to the surface normal. bool bkwds = true; if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds; - if(type == SSurface::CombineAs::DIFFERENCE && !opA) bkwds = !bkwds; + if((type == SSurface::CombineAs::DIFFERENCE && !opA) || + (type == SSurface::CombineAs::INTERSECTION)) { // Invert all newly created edges for intersection + bkwds = !bkwds; + } if(bkwds) { inter.AddEdge(tb, ta, sc->h.v, 1); } else { @@ -573,7 +587,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, // we can get duplicate edges if our surface intersects the other shell // at an edge, so that both surfaces intersect coincident (and both // generate an intersection edge). - final.CullExtraneousEdges(); + final.CullExtraneousEdges(/*both=*/true); // Use our reassembled edges to trim the new surface. ret.TrimFromEdgeList(&final, /*asUv=*/true); @@ -849,14 +863,14 @@ void SBspUv::InsertEdge(Point2d ea, Point2d eb, SSurface *srf) { m->more = more; more = m; } else if(fabs(dea) < LENGTH_EPS) { - // Point A lies on this lie, but point B does not + // Point A lies on this line, but point B does not if(deb > 0) { pos = InsertOrCreateEdge(pos, ea, eb, srf); } else { neg = InsertOrCreateEdge(neg, ea, eb, srf); } } else if(fabs(deb) < LENGTH_EPS) { - // Point B lies on this lie, but point A does not + // Point B lies on this line, but point A does not if(dea > 0) { pos = InsertOrCreateEdge(pos, ea, eb, srf); } else { diff --git a/src/srf/surface.h b/src/srf/surface.h index 6fabf66a..a076f082 100644 --- a/src/srf/surface.h +++ b/src/srf/surface.h @@ -256,7 +256,7 @@ public: enum class CombineAs : uint32_t { UNION = 10, DIFFERENCE = 11, - INTERSECT = 12 + INTERSECTION = 12 }; int tag; @@ -386,6 +386,7 @@ public: void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0); void MakeFromUnionOf(SShell *a, SShell *b); void MakeFromDifferenceOf(SShell *a, SShell *b); + void MakeFromIntersectionOf(SShell *a, SShell *b); void MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type); void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into); void CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into, SSurface::CombineAs type); diff --git a/src/textscreens.cpp b/src/textscreens.cpp index 1ea8f133..e35c49f0 100644 --- a/src/textscreens.cpp +++ b/src/textscreens.cpp @@ -210,11 +210,19 @@ void TextWindow::ScreenChangeGroupOption(int link, uint32_t v) { if(g->type == Group::Type::EXTRUDE) { // When an extrude group is first created, it's positioned for a union // extrusion. If no constraints were added, flip it when we switch between - // union and difference modes to avoid manual work doing the same. - if(g->meshCombine != (Group::CombineAs)v && g->GetNumConstraints() == 0 && - ((Group::CombineAs)v == Group::CombineAs::DIFFERENCE || - g->meshCombine == Group::CombineAs::DIFFERENCE)) { - g->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated()); + // union/assemble and difference/intersection modes to avoid manual work doing the same. + if(g->meshCombine != (Group::CombineAs)v && g->GetNumConstraints() == 0) { + // I apologise for his if statement + if(((Group::CombineAs::DIFFERENCE == g->meshCombine || + Group::CombineAs::INTERSECTION == g->meshCombine) && + (Group::CombineAs::DIFFERENCE != (Group::CombineAs)v && + Group::CombineAs::INTERSECTION != (Group::CombineAs)v)) || + ((Group::CombineAs::DIFFERENCE != g->meshCombine && + Group::CombineAs::INTERSECTION != g->meshCombine) && + (Group::CombineAs::DIFFERENCE == (Group::CombineAs)v || + Group::CombineAs::INTERSECTION == (Group::CombineAs)v))) { + g->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated()); + } } } g->meshCombine = (Group::CombineAs)v; @@ -375,11 +383,13 @@ void TextWindow::ShowGroupInfo() { g->type == Group::Type::HELIX) { bool un = (g->meshCombine == Group::CombineAs::UNION); bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE); + bool intr = (g->meshCombine == Group::CombineAs::INTERSECTION); bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE); Printf(false, " %Ftsolid model as"); Printf(false, "%Ba %f%D%Lc%Fd%s union%E " "%f%D%Lc%Fd%s difference%E " + "%f%D%Lc%Fd%s intersection%E " "%f%D%Lc%Fd%s assemble%E ", &TextWindow::ScreenChangeGroupOption, Group::CombineAs::UNION, @@ -388,6 +398,9 @@ void TextWindow::ShowGroupInfo() { Group::CombineAs::DIFFERENCE, diff ? RADIO_TRUE : RADIO_FALSE, &TextWindow::ScreenChangeGroupOption, + Group::CombineAs::INTERSECTION, + intr ? RADIO_TRUE : RADIO_FALSE, + &TextWindow::ScreenChangeGroupOption, Group::CombineAs::ASSEMBLE, (asy ? RADIO_TRUE : RADIO_FALSE));