diff --git a/groupmesh.cpp b/groupmesh.cpp index 472d49a..97b0e40 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -177,7 +177,7 @@ done: runningShell.TriangulateInto(&runningMesh); emphEdges.Clear(); if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) { - thisShell.MakeEdgesInto(&emphEdges); + runningShell.MakeEdgesInto(&emphEdges); } } diff --git a/polygon.cpp b/polygon.cpp index f6974bc..94b082e 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -53,7 +53,7 @@ STriangle STriangle::From(STriMeta meta, Vector a, Vector b, Vector c) { } SEdge SEdge::From(Vector a, Vector b) { - SEdge se = { 0, a, b }; + SEdge se = { 0, 0, 0, a, b }; return se; } @@ -61,10 +61,12 @@ void SEdgeList::Clear(void) { l.Clear(); } -void SEdgeList::AddEdge(Vector a, Vector b) { +void SEdgeList::AddEdge(Vector a, Vector b, int auxA, int auxB) { SEdge e; ZERO(&e); e.a = a; e.b = b; + e.auxA = auxA; + e.auxB = auxB; l.Add(&e); } diff --git a/polygon.h b/polygon.h index dee5b5c..d8d0a01 100644 --- a/polygon.h +++ b/polygon.h @@ -10,6 +10,7 @@ class SBsp3; class SEdge { public: int tag; + int auxA, auxB; Vector a, b; static SEdge From(Vector a, Vector b); @@ -20,7 +21,7 @@ public: List l; void Clear(void); - void AddEdge(Vector a, Vector b); + void AddEdge(Vector a, Vector b, int auxA=0, int auxB=0); bool AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir=false); bool AssembleContour(Vector first, Vector last, SContour *dest, SEdge *errorAt, bool keepDir); diff --git a/srf/boolean.cpp b/srf/boolean.cpp index 02995fe..b6480d5 100644 --- a/srf/boolean.cpp +++ b/srf/boolean.cpp @@ -1,10 +1,144 @@ #include "solvespace.h" void SShell::MakeFromUnionOf(SShell *a, SShell *b) { - MakeFromCopyOf(b); + MakeFromBoolean(a, b, AS_UNION); } void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) { - MakeFromCopyOf(b); + MakeFromBoolean(a, b, AS_DIFFERENCE); +} + +SCurve SCurve::MakeCopySplitAgainst(SShell *against) { + SCurve ret; + ZERO(&ret); + + ret.isExact = isExact; + ret.exact = exact; + + Vector *p; + for(p = pts.First(); p; p = pts.NextAfter(p)) { + ret.pts.Add(p); + } + return ret; +} + +void SShell::CopyCurvesSplitAgainst(SShell *against, SShell *into) { + SCurve *sc; + for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { + SCurve scn = sc->MakeCopySplitAgainst(against); + hSCurve hsc = into->curve.AddAndAssignId(&scn); + // And note the new ID so that we can rewrite the trims appropriately + sc->newH = hsc; + } +} + +void SShell::MakeEdgeListUseNewCurveIds(SEdgeList *el) { + SEdge *se; + for(se = el->l.First(); se; se = el->l.NextAfter(se)) { + hSCurve oldh = { se->auxA }; + SCurve *osc = curve.FindById(oldh); + se->auxA = osc->newH.v; + // auxB is the direction, which is unchanged + } +} + +void SSurface::TrimFromEdgeList(SEdgeList *el) { + el->l.ClearTags(); + + STrimBy stb; + ZERO(&stb); + for(;;) { + // Find an edge, any edge; we'll start from there. + SEdge *se; + for(se = el->l.First(); se; se = el->l.NextAfter(se)) { + if(se->tag) continue; + break; + } + if(!se) break; + se->tag = 1; + stb.start = se->a; + stb.finish = se->b; + stb.curve.v = se->auxA; + stb.backwards = se->auxB ? true : false; + + // Find adjoining edges from the same curve; those should be + // merged into a single trim. + bool merged; + do { + merged = false; + for(se = el->l.First(); se; se = el->l.NextAfter(se)) { + if(se->tag) continue; + if(se->auxA != stb.curve.v) continue; + if(( se->auxB && !stb.backwards) || + (!se->auxB && stb.backwards)) continue; + + if((se->a).Equals(stb.finish)) { + stb.finish = se->b; + se->tag = 1; + merged = true; + } else if((se->b).Equals(stb.start)) { + stb.start = se->a; + se->tag = 1; + merged = true; + } + } + } while(merged); + + // And add the merged trim, with xyz (not uv like the polygon) pts + stb.start = PointAt(stb.start.x, stb.start.y); + stb.finish = PointAt(stb.finish.x, stb.finish.y); + trim.Add(&stb); + } +} + +//----------------------------------------------------------------------------- +// Trim this surface against the specified shell, in the way that's appropriate +// for the specified Boolean operation type (and which operand we are). We +// also need a pointer to the shell that contains our own surface, since that +// contains our original trim curves. +//----------------------------------------------------------------------------- +SSurface SSurface::MakeCopyTrimAgainst(SShell *against, SShell *shell, + int type, bool opA) +{ + SSurface ret; + // The returned surface is identical, just the trim curves change + ret = *this; + ZERO(&(ret.trim)); + + SEdgeList el; + ZERO(&el); + MakeEdgesInto(shell, &el, true); + shell->MakeEdgeListUseNewCurveIds(&el); + + ret.TrimFromEdgeList(&el); + + el.Clear(); + return ret; +} + +void SShell::CopySurfacesTrimAgainst(SShell *against, SShell *into, + int type, bool opA) +{ + SSurface *ss; + for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { + SSurface ssn; + ssn = ss->MakeCopyTrimAgainst(against, this, type, opA); + into->surface.AddAndAssignId(&ssn); + } +} + +void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) { + // Copy over all the original curves, splitting them so that a + // piecwise linear segment never crosses a surface from the other + // shell. + a->CopyCurvesSplitAgainst(b, this); + b->CopyCurvesSplitAgainst(a, this); + + // Generate the intersection curves for each surface in A against all + // the surfaces in B + + // Then trim and copy the surfaces + a->CopySurfacesTrimAgainst(b, this, type, true); + b->CopySurfacesTrimAgainst(a, this, type, false); } diff --git a/srf/ratpoly.cpp b/srf/ratpoly.cpp index d134de2..64b1003 100644 --- a/srf/ratpoly.cpp +++ b/srf/ratpoly.cpp @@ -147,6 +147,17 @@ void SBezier::Reverse(void) { } } +void SBezier::GetBoundingProjd(Vector u, Vector orig, + double *umin, double *umax) +{ + int i; + for(i = 0; i <= deg; i++) { + double ut = ((ctrl[i]).Minus(orig)).Dot(u); + if(ut < *umin) *umin = ut; + if(ut > *umax) *umax = ut; + } +} + SBezier SBezier::TransformedBy(Vector t, Quaternion q) { SBezier ret = *this; int i; @@ -228,6 +239,15 @@ void SBezierLoop::Reverse(void) { } } +void SBezierLoop::GetBoundingProjd(Vector u, Vector orig, + double *umin, double *umax) +{ + SBezier *sb; + for(sb = l.First(); sb; sb = l.NextAfter(sb)) { + sb->GetBoundingProjd(u, orig, umin, umax); + } +} + void SBezierLoop::MakePwlInto(SContour *sc) { List lv; ZERO(&lv); @@ -292,6 +312,15 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly, return ret; } +void SBezierLoopSet::GetBoundingProjd(Vector u, Vector orig, + double *umin, double *umax) +{ + SBezierLoop *sbl; + for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) { + sbl->GetBoundingProjd(u, orig, umin, umax); + } +} + void SBezierLoopSet::Clear(void) { int i; for(i = 0; i < l.n; i++) { @@ -358,15 +387,13 @@ SSurface SSurface::FromExtrusionOf(SBezier *sb, Vector t0, Vector t1) { return ret; } -SSurface SSurface::FromPlane(Vector pt, Vector n) { +SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) { SSurface ret; ZERO(&ret); ret.degm = 1; ret.degn = 1; - Vector u = n.Normal(0), v = n.Normal(1); - ret.weight[0][0] = ret.weight[0][1] = 1; ret.weight[1][0] = ret.weight[1][1] = 1; @@ -520,9 +547,9 @@ void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv) { SCurve *sc = shell->curve.FindById(stb->curve); Vector prev, prevuv, ptuv; - bool inCurve = false; + bool inCurve = false, empty = true; double u = 0, v = 0; - + int i, first, last, increment; if(stb->backwards) { first = sc->pts.n - 1; @@ -539,18 +566,23 @@ void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv) { ClosestPointTo(*pt, &u, &v); ptuv = Vector::From(u, v, 0); if(inCurve) { - sel->AddEdge(prevuv, ptuv); + sel->AddEdge(prevuv, ptuv, sc->h.v, stb->backwards); + empty = false; } prevuv = ptuv; } else { if(inCurve) { - sel->AddEdge(prev, *pt); + sel->AddEdge(prev, *pt, sc->h.v, stb->backwards); + empty = false; } prev = *pt; } - if(pt->EqualsExactly(stb->start)) inCurve = true; - if(pt->EqualsExactly(stb->finish)) inCurve = false; + if(pt->Equals(stb->start)) inCurve = true; + if(pt->Equals(stb->finish)) inCurve = false; + } + if(inCurve || empty) { + dbp("trim was empty or unterminated"); } } } @@ -604,12 +636,27 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, SWAP(Vector, t0, t1); } - // First, generate the top and bottom surfaces of the extrusion; just - // planes. + // Define a coordinate system to contain the original sketch, and get + // a bounding box in that csys + Vector n = sbls->normal.ScaledBy(-1); + Vector u = n.Normal(0), v = n.Normal(1); + Vector orig = sbls->point; + double umax = 1e-10, umin = 1e10; + sbls->GetBoundingProjd(u, orig, &umin, &umax); + double vmax = 1e-10, vmin = 1e10; + sbls->GetBoundingProjd(v, orig, &vmin, &vmax); + // and now fix things up so that all u and v lie between 0 and 1 + orig = orig.Plus(u.ScaledBy(umin)); + orig = orig.Plus(v.ScaledBy(vmin)); + u = u.ScaledBy(umax - umin); + v = v.ScaledBy(vmax - vmin); + + // So we can now generate the top and bottom surfaces of the extrusion, + // planes within a translated (and maybe mirrored) version of that csys. SSurface s0, s1; - s0 = SSurface::FromPlane(sbls->point.Plus(t0), sbls->normal.ScaledBy(-1)); + s0 = SSurface::FromPlane(orig.Plus(t0), u, v); s0.color = color; - s1 = SSurface::FromPlane(sbls->point.Plus(t1), sbls->normal.ScaledBy( 1)); + s1 = SSurface::FromPlane(orig.Plus(t1).Plus(u), u.ScaledBy(-1), v); s1.color = color; hSSurface hs0 = surface.AddAndAssignId(&s0), hs1 = surface.AddAndAssignId(&s1); diff --git a/srf/surface.h b/srf/surface.h index c67a591..559d543 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -34,6 +34,7 @@ public: void MakePwlInto(List *l, Vector offset); void MakePwlWorker(List *l, double ta, double tb, Vector offset); + void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax); void Reverse(void); SBezier TransformedBy(Vector t, Quaternion q); @@ -57,6 +58,7 @@ public: inline void Clear(void) { l.Clear(); } void Reverse(void); void MakePwlInto(SContour *sc); + void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax); static SBezierLoop FromCurves(SBezierList *spcl, bool *allClosed, SEdge *errorAt); @@ -71,6 +73,7 @@ public: static SBezierLoopSet From(SBezierList *spcl, SPolygon *poly, bool *allClosed, SEdge *errorAt); + void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax); void Clear(void); }; @@ -79,13 +82,15 @@ class SCurve { public: hSCurve h; + hSCurve newH; // when merging with booleans + bool isExact; SBezier exact; List pts; - static SCurve SCurve::FromTransformationOf(SCurve *a, - Vector t, Quaternion q); + static SCurve FromTransformationOf(SCurve *a, Vector t, Quaternion q); + SCurve MakeCopySplitAgainst(SShell *against); void Clear(void); }; @@ -122,10 +127,14 @@ public: List trim; static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1); - static SSurface FromPlane(Vector pt, Vector n); + static SSurface FromPlane(Vector pt, Vector u, Vector v); static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q, bool includingTrims); + SSurface MakeCopyTrimAgainst(SShell *against, SShell *shell, + int type, bool opA); + void TrimFromEdgeList(SEdgeList *el); + void ClosestPointTo(Vector p, double *u, double *v); Vector PointAt(double u, double v); void TangentsAt(double u, double v, Vector *tu, Vector *tv); @@ -144,8 +153,17 @@ public: void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, int color); + void MakeFromUnionOf(SShell *a, SShell *b); void MakeFromDifferenceOf(SShell *a, SShell *b); + static const int AS_UNION = 10; + static const int AS_DIFFERENCE = 11; + static const int AS_INTERSECT = 12; + void MakeFromBoolean(SShell *a, SShell *b, int type); + void CopyCurvesSplitAgainst(SShell *against, SShell *into); + void CopySurfacesTrimAgainst(SShell *against, SShell *into, int t, bool a); + void MakeEdgeListUseNewCurveIds(SEdgeList *el); + void MakeFromCopyOf(SShell *a); void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q);