From 2d653eada8b772175358200fd087ca66393ba09c Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Thu, 4 Jun 2009 21:38:41 -0800 Subject: [PATCH] Add code to identify planes and cylindrical surfaces from a solid of revolution, and put them in the same form as if they had been draw by an extrusion (so that we can use all the same special case intersection curves). And add code to merge coincident faces into one. That turns out to be more than a cosmetic/efficiency thing, since edge splitting fails at the join between two coincident faces. [git-p4: depot-paths = "//depot/solvespace/": change = 1965] --- Makefile | 3 +- groupmesh.cpp | 4 +++ srf/boolean.cpp | 11 +++--- srf/merge.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++++ srf/surface.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++++ srf/surface.h | 4 ++- srf/surfinter.cpp | 3 +- 7 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 srf/merge.cpp diff --git a/Makefile b/Makefile index fb3c409..a68318d 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ SRFOBJS = $(OBJDIR)\ratpoly.obj \ $(OBJDIR)\triangulate.obj \ $(OBJDIR)\boolean.obj \ $(OBJDIR)\surfinter.obj \ + $(OBJDIR)\merge.obj \ RES = $(OBJDIR)\resource.res @@ -56,7 +57,7 @@ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib g all: $(OBJDIR)/solvespace.exe @cp $(OBJDIR)/solvespace.exe . - solvespace alext.slvs + solvespace t8.slvs clean: rm -f obj/* diff --git a/groupmesh.cpp b/groupmesh.cpp index 1896c43..25306d4 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -168,6 +168,7 @@ void Group::GenerateShellAndMesh(void) { GenerateForStepAndRepeat (prev, toStep, &runningShell, src->meshCombine); + runningShell.MergeCoincidentSurfaces(); } else { SMesh prevm, stepm; ZERO(&prevm); @@ -287,6 +288,8 @@ void Group::GenerateShellAndMesh(void) { thisShell.RemapFaces(this, 0); } + thisShell.MergeCoincidentSurfaces(); + // So now we've got the mesh or shell for this group. Combine it with // the previous group's mesh or shell with the requested Boolean, and // we're done. @@ -295,6 +298,7 @@ void Group::GenerateShellAndMesh(void) { if(pg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) { SShell *prevs = &(pg->runningShell); GenerateForBoolean(prevs, &thisShell, &runningShell); + runningShell.MergeCoincidentSurfaces(); // If the Boolean failed, then we should note that in the text screen // for this group. diff --git a/srf/boolean.cpp b/srf/boolean.cpp index a487aa4..30789dc 100644 --- a/srf/boolean.cpp +++ b/srf/boolean.cpp @@ -109,7 +109,7 @@ void SShell::CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into) { } } -void SSurface::TrimFromEdgeList(SEdgeList *el) { +void SSurface::TrimFromEdgeList(SEdgeList *el, bool asUv) { el->l.ClearTags(); STrimBy stb; @@ -151,9 +151,12 @@ void SSurface::TrimFromEdgeList(SEdgeList *el) { } } while(merged); + if(asUv) { + stb.start = PointAt(stb.start.x, stb.start.y); + stb.finish = PointAt(stb.finish.x, stb.finish.y); + } + // 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); } } @@ -444,7 +447,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, final.CullExtraneousEdges(); // Use our reassembled edges to trim the new surface. - ret.TrimFromEdgeList(&final); + ret.TrimFromEdgeList(&final, true); SPolygon poly; ZERO(&poly); diff --git a/srf/merge.cpp b/srf/merge.cpp new file mode 100644 index 0000000..5255129 --- /dev/null +++ b/srf/merge.cpp @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Routines to merge multiple coincident surfaces (each with their own trim +// curves) into a single surface, with all of the trim curves. +//----------------------------------------------------------------------------- +#include "../solvespace.h" + +void SShell::MergeCoincidentSurfaces(void) { + surface.ClearTags(); + + int i, j; + SSurface *si, *sj; + + for(i = 0; i < surface.n; i++) { + si = &(surface.elem[i]); + if(si->tag) continue; + + SEdgeList sel; + ZERO(&sel); + + bool merged = false; + + for(j = i + 1; j < surface.n; j++) { + sj = &(surface.elem[j]); + if(sj->tag) continue; + if(!sj->CoincidentWith(si, true)) continue; + if(sj->color != si->color) continue; + // But we do merge surfaces with different face entities, since + // otherwise we'd hardly ever merge anything. + + // This surface is coincident, so it gets merged. + sj->tag = 1; + merged = true; + sj->MakeEdgesInto(this, &sel, false); + sj->trim.Clear(); + + // All the references to this surface get replaced with the new srf + SCurve *sc; + for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { + if(sc->surfA.v == sj->h.v) sc->surfA = si->h; + if(sc->surfB.v == sj->h.v) sc->surfB = si->h; + } + } + + if(merged) { + si->MakeEdgesInto(this, &sel, false); + sel.CullExtraneousEdges(); + si->trim.Clear(); + si->TrimFromEdgeList(&sel, false); + + // And we must choose control points such that all the trims lie + // with u and v in [0, 1], so that the bbox tests work. + Vector u, v, n; + si->TangentsAt(0.5, 0.5, &u, &v); + u = u.WithMagnitude(1); + v = v.WithMagnitude(1); + n = si->NormalAt(0.5, 0.5).WithMagnitude(1); + v = (n.Cross(u)).WithMagnitude(1); + + double umax = VERY_NEGATIVE, umin = VERY_POSITIVE, + vmax = VERY_NEGATIVE, vmin = VERY_POSITIVE; + SEdge *se; + for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) { + double ut = (se->a).Dot(u), vt = (se->a).Dot(v); + umax = max(umax, ut); + vmax = max(vmax, vt); + umin = min(umin, ut); + vmin = min(vmin, vt); + } + + // We move in the +v direction as v goes from 0 to 1, and in the + // +u direction as u goes from 0 to 1. So our normal ends up + // pointed the same direction. + double nt = (si->ctrl[0][0]).Dot(n); + si->ctrl[0][0] = + Vector::From(umin, vmin, nt).ScaleOutOfCsys(u, v, n); + si->ctrl[0][1] = + Vector::From(umin, vmax, nt).ScaleOutOfCsys(u, v, n); + si->ctrl[1][1] = + Vector::From(umax, vmax, nt).ScaleOutOfCsys(u, v, n); + si->ctrl[1][0] = + Vector::From(umax, vmin, nt).ScaleOutOfCsys(u, v, n); + } + } + + surface.RemoveTagged(); +} + diff --git a/srf/surface.cpp b/srf/surface.cpp index ed33593..20fa20e 100644 --- a/srf/surface.cpp +++ b/srf/surface.cpp @@ -67,6 +67,8 @@ SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, { SSurface ret; ZERO(&ret); + + ret.degm = sb->deg; ret.degn = 2; @@ -499,6 +501,8 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, ZERO(this); SBezierLoop *sbl; + int i0 = surface.n, i; + // Normalize the axis direction so that the direction of revolution // ends up parallel to the normal of the sketch, on the side of the // axis where the sketch is. @@ -619,6 +623,86 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, hsl.Clear(); } + + for(i = i0; i < surface.n; i++) { + SSurface *srf = &(surface.elem[i]); + + // Revolution of a line; this is potentially a plane, which we can + // rewrite to have degree (1, 1). + if(srf->degm == 1 && srf->degn == 2) { + // close start, far start, far finish + Vector cs, fs, ff; + double d0, d1; + d0 = (srf->ctrl[0][0]).DistanceToLine(pt, axis); + d1 = (srf->ctrl[1][0]).DistanceToLine(pt, axis); + + if(d0 > d1) { + cs = srf->ctrl[1][0]; + fs = srf->ctrl[0][0]; + ff = srf->ctrl[0][2]; + } else { + cs = srf->ctrl[0][0]; + fs = srf->ctrl[1][0]; + ff = srf->ctrl[1][2]; + } + + // origin close, origin far + Vector oc = cs.ClosestPointOnLine(pt, axis), + of = fs.ClosestPointOnLine(pt, axis); + + if(oc.Equals(of)) { + // This is a plane, not a (non-degenerate) cone. + Vector oldn = srf->NormalAt(0.5, 0.5); + + Vector u = fs.Minus(of), v; + + v = (axis.Cross(u)).WithMagnitude(1); + + double vm = (ff.Minus(of)).Dot(v); + v = v.ScaledBy(vm); + + srf->degm = 1; + srf->degn = 1; + srf->ctrl[0][0] = of; + srf->ctrl[0][1] = of.Plus(u); + srf->ctrl[1][0] = of.Plus(v); + srf->ctrl[1][1] = of.Plus(u).Plus(v); + srf->weight[0][0] = 1; + srf->weight[0][1] = 1; + srf->weight[1][0] = 1; + srf->weight[1][1] = 1; + + if(oldn.Dot(srf->NormalAt(0.5, 0.5)) < 0) { + SWAP(Vector, srf->ctrl[0][0], srf->ctrl[1][0]); + SWAP(Vector, srf->ctrl[0][1], srf->ctrl[1][1]); + } + continue; + } + + if(fabs(d0 - d1) < LENGTH_EPS) { + // This is a cylinder; so transpose it so that we'll recognize + // it as a surface of extrusion. + SSurface sn = *srf; + + // Transposing u and v flips the normal, so reverse u to + // flip it again and put it back where we started. + sn.degm = 2; + sn.degn = 1; + int dm, dn; + for(dm = 0; dm <= 1; dm++) { + for(dn = 0; dn <= 2; dn++) { + sn.ctrl [dn][dm] = srf->ctrl [1-dm][dn]; + sn.weight[dn][dm] = srf->weight[1-dm][dn]; + } + } + + *srf = sn; + continue; + } + } + + } + } void SShell::MakeFromCopyOf(SShell *a) { diff --git a/srf/surface.h b/srf/surface.h index e5a2555..dc5b432 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -194,6 +194,7 @@ public: // A rational polynomial surface in Bezier form. class SSurface { public: + int tag; hSSurface h; // Same as newH for the curves; record what a surface gets renamed to @@ -226,7 +227,7 @@ public: DWORD auxA, SShell *shell); SSurface MakeCopyTrimAgainst(SShell *against, SShell *parent, SShell *into, int type, bool opA); - void TrimFromEdgeList(SEdgeList *el); + void TrimFromEdgeList(SEdgeList *el, bool asUv); void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, SShell *into); void AddExactIntersectionCurve(SBezier *sb, SSurface *srfB, @@ -330,6 +331,7 @@ public: void MakeFromCopyOf(SShell *a); void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q); void MakeFromAssemblyOf(SShell *a, SShell *b); + void MergeCoincidentSurfaces(void); void TriangulateInto(SMesh *sm); void MakeEdgesInto(SEdgeList *sel); diff --git a/srf/surfinter.cpp b/srf/surfinter.cpp index b166659..49a4309 100644 --- a/srf/surfinter.cpp +++ b/srf/surfinter.cpp @@ -58,7 +58,8 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB, 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->p, v->p); + Vector e = (prev->p).Minus(v->p).WithMagnitude(-1); + SS.nakedEdges.AddEdge((prev->p).Plus(e), (v->p).Minus(e)); } prev = v; }