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/35pull/607/head
parent
c95a07a1de
commit
3888909d02
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
17
src/mesh.cpp
17
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++) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -239,7 +239,8 @@ public:
|
|||
enum class CombineAs : uint32_t {
|
||||
UNION = 0,
|
||||
DIFFERENCE = 1,
|
||||
ASSEMBLE = 2
|
||||
ASSEMBLE = 2,
|
||||
INTERSECTION = 3
|
||||
};
|
||||
CombineAs meshCombine;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
Loading…
Reference in New Issue