NURBS: Add intersection boolean operation.

The NURBS operation is properly implemented.

ToDo: The mesh operation in SMesh::MakeFromIntersectionOf
is still done as C=A-(A-B).

Implements: https://github.com/solvespace/solvespace/issues/35
pull/607/head
ruevs 2020-05-08 03:48:19 +03:00 committed by phkahler
parent c95a07a1de
commit 3888909d02
8 changed files with 78 additions and 20 deletions

View File

@ -5,6 +5,7 @@ Changelog
--- ---
New sketch features: New sketch features:
* New intersection boolean operation for solid models.
* New groups, revolution and helical extrusion. * New groups, revolution and helical extrusion.
* Extrude, lathe, translate and rotate groups can use the "assembly" * Extrude, lathe, translate and rotate groups can use the "assembly"
boolean operation, to increase performance. boolean operation, to increase performance.

View File

@ -143,7 +143,7 @@ void Group::GenerateForStepAndRepeat(T *steps, T *outs, Group::CombineAs forWhat
// And tack this transformed copy on to the return. // And tack this transformed copy on to the return.
if(soFar->IsEmpty()) { if(soFar->IsEmpty()) {
scratch->MakeFromCopyOf(&transd); scratch->MakeFromCopyOf(&transd);
} else if (forWhat == CombineAs::ASSEMBLE) { } else if(forWhat == CombineAs::ASSEMBLE) {
scratch->MakeFromAssemblyOf(soFar, &transd); scratch->MakeFromAssemblyOf(soFar, &transd);
} else { } else {
scratch->MakeFromUnionOf(soFar, &transd); 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 // So our group's shell appears in thisShell. Combine this with the
// previous group's shell, using the requested operation. // previous group's shell, using the requested operation.
if(how == CombineAs::UNION) { switch(how) {
case CombineAs::UNION:
outs->MakeFromUnionOf(prevs, thiss); outs->MakeFromUnionOf(prevs, thiss);
} else if(how == CombineAs::DIFFERENCE) { break;
case CombineAs::DIFFERENCE:
outs->MakeFromDifferenceOf(prevs, thiss); outs->MakeFromDifferenceOf(prevs, thiss);
} else { break;
case CombineAs::INTERSECTION:
outs->MakeFromIntersectionOf(prevs, thiss);
break;
case CombineAs::ASSEMBLE:
outs->MakeFromAssemblyOf(prevs, thiss); outs->MakeFromAssemblyOf(prevs, thiss);
break;
} }
} }

View File

@ -286,6 +286,23 @@ void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) {
AddAgainstBsp(a, bspb); 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) { void SMesh::MakeFromCopyOf(SMesh *a) {
ssassert(this != a, "Can't make from copy of self"); ssassert(this != a, "Can't make from copy of self");
for(int i = 0; i < a->l.n; i++) { for(int i = 0; i < a->l.n; i++) {

View File

@ -269,6 +269,7 @@ public:
void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3); void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3);
void MakeFromUnionOf(SMesh *a, SMesh *b); void MakeFromUnionOf(SMesh *a, SMesh *b);
void MakeFromDifferenceOf(SMesh *a, SMesh *b); void MakeFromDifferenceOf(SMesh *a, SMesh *b);
void MakeFromIntersectionOf(SMesh *a, SMesh *b);
void MakeFromCopyOf(SMesh *a); void MakeFromCopyOf(SMesh *a);
void MakeFromTransformationOf(SMesh *a, Vector trans, void MakeFromTransformationOf(SMesh *a, Vector trans,

View File

@ -239,7 +239,8 @@ public:
enum class CombineAs : uint32_t { enum class CombineAs : uint32_t {
UNION = 0, UNION = 0,
DIFFERENCE = 1, DIFFERENCE = 1,
ASSEMBLE = 2 ASSEMBLE = 2,
INTERSECTION = 3
}; };
CombineAs meshCombine; CombineAs meshCombine;

View File

@ -1,6 +1,6 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Top-level functions to compute the Boolean union or difference between // Top-level functions to compute the Boolean union, difference or intersection
// two shells of rational polynomial surfaces. // between two shells of rational polynomial surfaces.
// //
// Copyright 2008-2013 Jonathan Westhues. // Copyright 2008-2013 Jonathan Westhues.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -16,6 +16,10 @@ void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) {
MakeFromBoolean(a, b, SSurface::CombineAs::DIFFERENCE); 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 // Take our original pwl curve. Wherever an edge intersects a surface within
// either agnstA or agnstB, split the piecewise linear element. Then refine // 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; return inShell || inSame;
} }
case SSurface::CombineAs::INTERSECTION:
if(opA) {
return inShell;
} else {
return inShell || inSame;
}
default: ssassert(false, "Unexpected combine type"); default: ssassert(false, "Unexpected combine type");
} }
} }
@ -463,7 +474,10 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
// point opposite to the surface normal. // point opposite to the surface normal.
bool bkwds = true; bool bkwds = true;
if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds; 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) { if(bkwds) {
inter.AddEdge(tb, ta, sc->h.v, 1); inter.AddEdge(tb, ta, sc->h.v, 1);
} else { } else {
@ -573,7 +587,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
// we can get duplicate edges if our surface intersects the other shell // we can get duplicate edges if our surface intersects the other shell
// at an edge, so that both surfaces intersect coincident (and both // at an edge, so that both surfaces intersect coincident (and both
// generate an intersection edge). // generate an intersection edge).
final.CullExtraneousEdges(); final.CullExtraneousEdges(/*both=*/true);
// Use our reassembled edges to trim the new surface. // Use our reassembled edges to trim the new surface.
ret.TrimFromEdgeList(&final, /*asUv=*/true); ret.TrimFromEdgeList(&final, /*asUv=*/true);
@ -849,14 +863,14 @@ void SBspUv::InsertEdge(Point2d ea, Point2d eb, SSurface *srf) {
m->more = more; m->more = more;
more = m; more = m;
} else if(fabs(dea) < LENGTH_EPS) { } 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) { if(deb > 0) {
pos = InsertOrCreateEdge(pos, ea, eb, srf); pos = InsertOrCreateEdge(pos, ea, eb, srf);
} else { } else {
neg = InsertOrCreateEdge(neg, ea, eb, srf); neg = InsertOrCreateEdge(neg, ea, eb, srf);
} }
} else if(fabs(deb) < LENGTH_EPS) { } 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) { if(dea > 0) {
pos = InsertOrCreateEdge(pos, ea, eb, srf); pos = InsertOrCreateEdge(pos, ea, eb, srf);
} else { } else {

View File

@ -256,7 +256,7 @@ public:
enum class CombineAs : uint32_t { enum class CombineAs : uint32_t {
UNION = 10, UNION = 10,
DIFFERENCE = 11, DIFFERENCE = 11,
INTERSECT = 12 INTERSECTION = 12
}; };
int tag; int tag;
@ -386,6 +386,7 @@ public:
void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0); void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0);
void MakeFromUnionOf(SShell *a, SShell *b); void MakeFromUnionOf(SShell *a, SShell *b);
void MakeFromDifferenceOf(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 MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type);
void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into); void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into);
void CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into, SSurface::CombineAs type); void CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into, SSurface::CombineAs type);

View File

@ -210,13 +210,21 @@ void TextWindow::ScreenChangeGroupOption(int link, uint32_t v) {
if(g->type == Group::Type::EXTRUDE) { if(g->type == Group::Type::EXTRUDE) {
// When an extrude group is first created, it's positioned for a union // 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 // extrusion. If no constraints were added, flip it when we switch between
// union and difference modes to avoid manual work doing the same. // union/assemble and difference/intersection modes to avoid manual work doing the same.
if(g->meshCombine != (Group::CombineAs)v && g->GetNumConstraints() == 0 && if(g->meshCombine != (Group::CombineAs)v && g->GetNumConstraints() == 0) {
((Group::CombineAs)v == Group::CombineAs::DIFFERENCE || // I apologise for his if statement
g->meshCombine == Group::CombineAs::DIFFERENCE)) { 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->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated());
} }
} }
}
g->meshCombine = (Group::CombineAs)v; g->meshCombine = (Group::CombineAs)v;
break; break;
@ -375,11 +383,13 @@ void TextWindow::ShowGroupInfo() {
g->type == Group::Type::HELIX) { g->type == Group::Type::HELIX) {
bool un = (g->meshCombine == Group::CombineAs::UNION); bool un = (g->meshCombine == Group::CombineAs::UNION);
bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE); bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE);
bool intr = (g->meshCombine == Group::CombineAs::INTERSECTION);
bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE); bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE);
Printf(false, " %Ftsolid model as"); Printf(false, " %Ftsolid model as");
Printf(false, "%Ba %f%D%Lc%Fd%s union%E " Printf(false, "%Ba %f%D%Lc%Fd%s union%E "
"%f%D%Lc%Fd%s difference%E " "%f%D%Lc%Fd%s difference%E "
"%f%D%Lc%Fd%s intersection%E "
"%f%D%Lc%Fd%s assemble%E ", "%f%D%Lc%Fd%s assemble%E ",
&TextWindow::ScreenChangeGroupOption, &TextWindow::ScreenChangeGroupOption,
Group::CombineAs::UNION, Group::CombineAs::UNION,
@ -388,6 +398,9 @@ void TextWindow::ShowGroupInfo() {
Group::CombineAs::DIFFERENCE, Group::CombineAs::DIFFERENCE,
diff ? RADIO_TRUE : RADIO_FALSE, diff ? RADIO_TRUE : RADIO_FALSE,
&TextWindow::ScreenChangeGroupOption, &TextWindow::ScreenChangeGroupOption,
Group::CombineAs::INTERSECTION,
intr ? RADIO_TRUE : RADIO_FALSE,
&TextWindow::ScreenChangeGroupOption,
Group::CombineAs::ASSEMBLE, Group::CombineAs::ASSEMBLE,
(asy ? RADIO_TRUE : RADIO_FALSE)); (asy ? RADIO_TRUE : RADIO_FALSE));