From 40ed1b7ac13a00fb9d6381dffb4016f4ef6f12fe Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sun, 17 May 2009 23:26:51 -0800 Subject: [PATCH] Generate intersection curves for surfaces of extrusion along a parallel axis (which are always lines parallel to that axis). Remove short pwl segments when possible, to avoid short edges that get misclassified. [git-p4: depot-paths = "//depot/solvespace/": change = 1952] --- srf/boolean.cpp | 41 ++++++++++++++++++++++-------- srf/curve.cpp | 62 +++++++++++++++++++++++++++++++++++++++----- srf/ratpoly.cpp | 14 ++++++++++ srf/surface.cpp | 2 +- srf/surface.h | 12 ++++++++- srf/surfinter.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++---- wishlist.txt | 3 +-- 7 files changed, 174 insertions(+), 25 deletions(-) diff --git a/srf/boolean.cpp b/srf/boolean.cpp index 3bee9fd..d98523c 100644 --- a/srf/boolean.cpp +++ b/srf/boolean.cpp @@ -36,9 +36,9 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, ret = *this; ZERO(&(ret.pts)); - Vector *p = pts.First(); + SCurvePt *p = pts.First(); if(!p) oops(); - Vector prev = *p; + SCurvePt prev = *p; ret.pts.Add(p); p = pts.NextAfter(p); @@ -47,8 +47,10 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, ZERO(&il); // Find all the intersections with the two passed shells - if(agnstA) agnstA->AllPointsIntersecting(prev, *p, &il, true,true,true); - if(agnstB) agnstB->AllPointsIntersecting(prev, *p, &il, true,true,true); + if(agnstA) + agnstA->AllPointsIntersecting(prev.p, p->p, &il, true, true, true); + if(agnstB) + agnstB->AllPointsIntersecting(prev.p, p->p, &il, true, true, true); if(il.n > 0) { // The intersections were generated by intersecting the pwl @@ -65,8 +67,8 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, // And now sort them in order along the line. Note that we must // do that after refining, in case the refining would make two // points switch places. - LineStart = prev; - LineDirection = p->Minus(prev); + LineStart = prev.p; + LineDirection = (p->p).Minus(prev.p); qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine); // And now uses the intersections to generate our split pwl edge(s) @@ -76,7 +78,11 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, // On-edge intersection will generate same split point for // both surfaces, so don't create zero-length edge. if(!prev.Equals(pi->p)) { - ret.pts.Add(&(pi->p)); + SCurvePt scpt; + scpt.tag = 0; + scpt.p = pi->p; + scpt.vertex = true; + ret.pts.Add(&scpt); } prev = pi->p; } @@ -329,8 +335,8 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, int i; for(i = 1; i < sc->pts.n; i++) { - Vector a = sc->pts.elem[i-1], - b = sc->pts.elem[i]; + Vector a = sc->pts.elem[i-1].p, + b = sc->pts.elem[i].p; Point2d auv, buv; ss->ClosestPointTo(a, &(auv.x), &(auv.y)); @@ -505,6 +511,22 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) { // Generate the intersection curves for each surface in A against all // the surfaces in B (which is all of the intersection curves). a->MakeIntersectionCurvesAgainst(b, this); + + SCurve *sc; + for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { + SSurface *srfA, *srfB; + if(sc->source == SCurve::FROM_A) { + srfA = a->surface.FindById(sc->surfA); + srfB = a->surface.FindById(sc->surfB); + } else if(sc->source == SCurve::FROM_B) { + srfA = b->surface.FindById(sc->surfA); + srfB = b->surface.FindById(sc->surfB); + } else if(sc->source == SCurve::FROM_INTERSECTION) { + srfA = a->surface.FindById(sc->surfA); + srfB = b->surface.FindById(sc->surfB); + } + sc->RemoveShortSegments(srfA, srfB); + } if(b->surface.n == 0 || a->surface.n == 0) { // Then trim and copy the surfaces @@ -518,7 +540,6 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) { // Now that we've copied the surfaces, we know their new hSurfaces, so // rewrite the curves to refer to the surfaces by their handles in the // result. - SCurve *sc; for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { if(sc->source == SCurve::FROM_A) { sc->surfA = a->surface.FindById(sc->surfA)->newH; diff --git a/srf/curve.cpp b/srf/curve.cpp index 833510a..b1dd282 100644 --- a/srf/curve.cpp +++ b/srf/curve.cpp @@ -388,9 +388,10 @@ SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q) { ret.surfA = a->surfA; ret.surfB = a->surfB; - Vector *p; + SCurvePt *p; for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) { - Vector pp = (q.Rotate(*p)).Plus(t); + SCurvePt pp = *p; + pp.p = (q.Rotate(p->p)).Plus(t); ret.pts.Add(&pp); } return ret; @@ -400,6 +401,55 @@ void SCurve::Clear(void) { pts.Clear(); } +//----------------------------------------------------------------------------- +// When we split line segments wherever they intersect a surface, we introduce +// extra pwl points. This may create very short edges that could be removed +// without violating the chord tolerance. Those are ugly, and also break +// stuff in the Booleans. So remove them. +//----------------------------------------------------------------------------- +void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) { + if(pts.n < 2) return; + pts.ClearTags(); + + Vector prev = pts.elem[0].p; + int i, a; + for(i = 1; i < pts.n - 1; i++) { + SCurvePt *sct = &(pts.elem[i]), + *scn = &(pts.elem[i+1]); + if(sct->vertex) { + prev = sct->p; + continue; + } + bool mustKeep = false; + + // We must check against both surfaces; the piecewise linear edge + // may have a different chord tolerance in the two surfaces. (For + // example, a circle in the surface of a cylinder is just a straight + // line, so it always has perfect chord tol, but that circle in + // a plane is a circle so it doesn't). + for(a = 0; a < 2; a++) { + SSurface *srf = (a == 0) ? srfA : srfB; + Vector puv, nuv; + srf->ClosestPointTo(prev, &(puv.x), &(puv.y)); + srf->ClosestPointTo(scn->p, &(nuv.x), &(nuv.y)); + + if(srf->ChordToleranceForEdge(nuv, puv) > SS.ChordTolMm()) { + mustKeep = true; + } + } + + if(mustKeep) { + prev = sct->p; + } else { + sct->tag = 1; + // and prev is unchanged, since there's no longer any point + // in between + } + } + + pts.RemoveTagged(); +} + STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) { STrimBy stb; ZERO(&stb); @@ -407,12 +457,12 @@ STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) { SCurve *sc = shell->curve.FindById(hsc); if(backwards) { - stb.finish = sc->pts.elem[0]; - stb.start = sc->pts.elem[sc->pts.n - 1]; + stb.finish = sc->pts.elem[0].p; + stb.start = sc->pts.elem[sc->pts.n - 1].p; stb.backwards = true; } else { - stb.start = sc->pts.elem[0]; - stb.finish = sc->pts.elem[sc->pts.n - 1]; + stb.start = sc->pts.elem[0].p; + stb.finish = sc->pts.elem[sc->pts.n - 1].p; stb.backwards = false; } diff --git a/srf/ratpoly.cpp b/srf/ratpoly.cpp index a738776..f2d169f 100644 --- a/srf/ratpoly.cpp +++ b/srf/ratpoly.cpp @@ -199,6 +199,20 @@ void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) { } } +void SBezier::MakePwlInto(List *l) { + List lv; + ZERO(&lv); + MakePwlInto(&lv); + int i; + for(i = 0; i < lv.n; i++) { + SCurvePt scpt; + scpt.tag = 0; + scpt.p = lv.elem[i]; + scpt.vertex = (i == 0) || (i == (lv.n - 1)); + l->Add(&scpt); + } + lv.Clear(); +} void SBezier::MakePwlInto(List *l) { l->Add(&(ctrl[0])); MakePwlWorker(l, 0.0, 1.0); diff --git a/srf/surface.cpp b/srf/surface.cpp index 320a91b..df5fd58 100644 --- a/srf/surface.cpp +++ b/srf/surface.cpp @@ -202,7 +202,7 @@ void SSurface::MakeTrimEdgesInto(SEdgeList *sel, bool asUv, increment = 1; } for(i = first; i != (last + increment); i += increment) { - Vector *pt = &(sc->pts.elem[i]); + Vector *pt = &(sc->pts.elem[i].p); if(asUv) { ClosestPointTo(*pt, &u, &v); ptuv = Vector::From(u, v, 0); diff --git a/srf/surface.h b/srf/surface.h index 0dfdf5d..b878afd 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -7,6 +7,7 @@ double Bernstein(int k, int deg, double t); double BernsteinDerivative(int k, int deg, double t); class SSurface; +class SCurvePt; // Utility data structure, a two-dimensional BSP to accelerate polygon // operations. @@ -67,6 +68,7 @@ public: Vector Start(void); Vector Finish(void); bool Equals(SBezier *b); + void MakePwlInto(List *l); void MakePwlInto(List *l); void MakePwlWorker(List *l, double ta, double tb); @@ -123,6 +125,13 @@ public: }; // Stuff for the surface trim curves: piecewise linear +class SCurvePt { +public: + int tag; + Vector p; + bool vertex; +}; + class SCurve { public: hSCurve h; @@ -139,7 +148,7 @@ public: bool isExact; SBezier exact; - List pts; + List pts; hSSurface surfA; hSSurface surfB; @@ -147,6 +156,7 @@ public: static SCurve FromTransformationOf(SCurve *a, Vector t, Quaternion q); SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, SSurface *srfA, SSurface *srfB); + void RemoveShortSegments(SSurface *srfA, SSurface *srfB); void Clear(void); }; diff --git a/srf/surfinter.cpp b/srf/surfinter.cpp index 6f4dd4a..961a6d7 100644 --- a/srf/surfinter.cpp +++ b/srf/surfinter.cpp @@ -35,7 +35,7 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB, } } if(existing) { - Vector *v; + SCurvePt *v; for(v = existing->pts.First(); v; v = existing->pts.NextAfter(v)) { sc.pts.Add(v); } @@ -51,11 +51,11 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB, if(0 && sb->deg == 1) { dbp(" "); - Vector *prev = NULL, *v; + SCurvePt *prev = NULL, *v; dbp("split.pts.n =%d", split.pts.n); for(v = split.pts.First(); v; v = split.pts.NextAfter(v)) { if(prev) { - SS.nakedEdges.AddEdge(*prev, *v); + SS.nakedEdges.AddEdge(prev->p, v->p); } prev = v; } @@ -79,6 +79,11 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, return; } + Vector alongt, alongb; + SBezier oft, ofb; + bool isExtdt = this->IsExtrusion(&oft, &alongt), + isExtdb = b->IsExtrusion(&ofb, &alongb); + if(degm == 1 && degn == 1 && b->degm == 1 && b->degn == 1) { // Line-line intersection; it's a plane or nothing. Vector na = NormalAt(0, 0).WithMagnitude(1), @@ -139,8 +144,8 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, p.Plus(dl.ScaledBy(tmax))); AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); } - } else if((degm == 1 && degn == 1 && b->IsExtrusion(NULL, NULL)) || - (b->degm == 1 && b->degn == 1 && this->IsExtrusion(NULL, NULL))) + } else if((degm == 1 && degn == 1 && isExtdb) || + (b->degm == 1 && b->degn == 1 && isExtdt)) { // The intersection between a plane and a surface of extrusion SSurface *splane, *sext; @@ -202,6 +207,55 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); } + } else if(isExtdt && isExtdb && + sqrt(fabs(alongt.Dot(alongb))) > + sqrt(alongt.Magnitude() * alongb.Magnitude()) - LENGTH_EPS) + { + // Two surfaces of extrusion along the same axis. So they might + // intersect along some number of lines parallel to the axis. + Vector axis = alongt.WithMagnitude(1); + + List inters; + ZERO(&inters); + List lv; + ZERO(&lv); + + Vector axisa = axis.ScaledBy((b->ctrl[0][0]).Dot(axis)), + axisb = axis.ScaledBy((b->ctrl[0][1]).Dot(axis)), + axisc = (axisa.Plus(axisb)).ScaledBy(0.5); + + oft.MakePwlInto(&lv); + + int i; + for(i = 0; i < lv.n - 1; i++) { + Vector pa = lv.elem[i], pb = lv.elem[i+1]; + pa = pa.Minus(axis.ScaledBy(pa.Dot(axis))); + pb = pb.Minus(axis.ScaledBy(pb.Dot(axis))); + pa = pa.Plus(axisc); + pb = pb.Plus(axisc); + + b->AllPointsIntersecting(pa, pb, &inters, true, false, false); + } + + SInter *si; + for(si = inters.First(); si; si = inters.NextAfter(si)) { + Vector p = (si->p).Minus(axis.ScaledBy((si->p).Dot(axis))); + double ub, vb; + b->ClosestPointTo(p, &ub, &vb, true); + SSurface plane; + plane = SSurface::FromPlane(p, axis.Normal(0), axis.Normal(1)); + + b->PointOnSurfaces(this, &plane, &ub, &vb); + + p = b->PointAt(ub, vb); + + SBezier bezier; + bezier = SBezier::From(p.Plus(axisa), p.Plus(axisb)); + AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); + } + + inters.Clear(); + lv.Clear(); } // need to implement general numerical surface intersection for tough @@ -632,6 +686,7 @@ int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) { if(d < dmin) { dmin = d; + if(d < LENGTH_EPS) { // Edge-on-face (unless edge-on-edge above supercedes) double dot = (si->surfNormal).Dot(edge_n); diff --git a/wishlist.txt b/wishlist.txt index 1913e56..14ec823 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,8 +1,7 @@ marching algorithm for surface intersection -boundary avoidance when casting ray for point-in-shell tangent intersections -short pwl edge avoidance +surfaces with coincident control points assembly -----