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 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.

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.
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) {
switch(how) {
case CombineAs::UNION:
outs->MakeFromUnionOf(prevs, thiss);
} else if(how == CombineAs::DIFFERENCE) {
break;
case CombineAs::DIFFERENCE:
outs->MakeFromDifferenceOf(prevs, thiss);
} else {
break;
case CombineAs::INTERSECTION:
outs->MakeFromIntersectionOf(prevs, thiss);
break;
case CombineAs::ASSEMBLE:
outs->MakeFromAssemblyOf(prevs, thiss);
break;
}
}

View File

@ -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++) {

View File

@ -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,

View File

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

View File

@ -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 {

View File

@ -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);

View File

@ -210,13 +210,21 @@ 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)) {
// 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;
break;
@ -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));