From 07ddd62a3a6c16441b3eeac7b2a90d4af8e3664f Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sun, 25 Jan 2009 03:52:29 -0800 Subject: [PATCH] Preparatory work for Boolean. Make the u and v coordinates of the trim curves for all surfaces lie between 0 and 1. And add routines to merge the curves and surfaces from two shells into one, and to split the trim curves into their piecewise linear segments and then reassemble them into trim curves. [git-p4: depot-paths = "//depot/solvespace/": change = 1905] --- groupmesh.cpp | 2 +- polygon.cpp | 6 ++- polygon.h | 3 +- srf/boolean.cpp | 138 +++++++++++++++++++++++++++++++++++++++++++++++- srf/ratpoly.cpp | 73 ++++++++++++++++++++----- srf/surface.h | 24 +++++++-- 6 files changed, 224 insertions(+), 22 deletions(-) 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);