From f865901bd268c7ba452c6b76c92ee89af5ea723e Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Fri, 26 Jun 2009 21:53:56 -0800 Subject: [PATCH] Group edges into chains (that don't intersect edges from the other contour, except at the ends of the chain), and classify the entire chain. That's much faster than going edge by edge. [git-p4: depot-paths = "//depot/solvespace/": change = 2002] --- Makefile | 2 +- dsc.h | 13 +++++- polygon.cpp | 14 ++++++ polygon.h | 1 + srf/boolean.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++--- srf/surface.h | 1 + wishlist.txt | 2 - 7 files changed, 136 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 0abb5a9..0aff17f 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib g all: $(OBJDIR)/solvespace.exe @cp $(OBJDIR)/solvespace.exe . - solvespace t8.slvs + solvespace block.slvs clean: rm -f obj/* diff --git a/dsc.h b/dsc.h index cb5727c..fa66177 100644 --- a/dsc.h +++ b/dsc.h @@ -140,14 +140,25 @@ public: int n; int elemsAllocated; - void Add(T *t) { + void AllocForOneMore(void) { if(n >= elemsAllocated) { elemsAllocated = (elemsAllocated + 32)*2; elem = (T *)MemRealloc(elem, elemsAllocated*sizeof(elem[0])); } + } + + void Add(T *t) { + AllocForOneMore(); elem[n++] = *t; } + void AddToBeginning(T *t) { + AllocForOneMore(); + memmove(elem+1, elem, n*sizeof(elem[0])); + n++; + elem[0] = *t; + } + T *First(void) { return (n == 0) ? NULL : &(elem[0]); } diff --git a/polygon.cpp b/polygon.cpp index dd6523d..e7c0886 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -299,6 +299,20 @@ bool SPointList::ContainsPoint(Vector pt) { return false; } +void SPointList::IncrementTagFor(Vector pt) { + SPoint *p; + for(p = l.First(); p; p = l.NextAfter(p)) { + if(pt.Equals(p->p)) { + (p->tag)++; + return; + } + } + SPoint pa; + pa.p = pt; + pa.tag = 1; + l.Add(&pa); +} + void SPointList::Add(Vector pt) { SPoint p; ZERO(&p); diff --git a/polygon.h b/polygon.h index f29efb6..2628820 100644 --- a/polygon.h +++ b/polygon.h @@ -49,6 +49,7 @@ public: void Clear(void); bool ContainsPoint(Vector pt); + void IncrementTagFor(Vector pt); void Add(Vector pt); }; diff --git a/srf/boolean.cpp b/srf/boolean.cpp index 858d1c1..185fe02 100644 --- a/srf/boolean.cpp +++ b/srf/boolean.cpp @@ -278,6 +278,59 @@ static char *REGION(int d) { } +//----------------------------------------------------------------------------- +// We are given src, with at least one edge, and avoid, a list of points to +// avoid. We return a chain of edges (that share endpoints), such that no +// point within the avoid list ever occurs in the middle of a chain. And we +// delete the edges in that chain from our source list. +//----------------------------------------------------------------------------- +void SSurface::FindChainAvoiding(SEdgeList *src, SEdgeList *dest, + SPointList *avoid) +{ + if(src->l.n < 1) oops(); + // Start with an arbitrary edge. + dest->l.Add(&(src->l.elem[0])); + src->l.ClearTags(); + src->l.elem[0].tag = 1; + + bool added; + do { + added = false; + // The start and finish of the current edge chain + Vector s = dest->l.elem[0].a, + f = dest->l.elem[dest->l.n - 1].b; + + // We can attach a new edge at the start or finish, as long as that + // start or finish point isn't in the list of points to avoid. + bool startOkay = !avoid->ContainsPoint(s), + finishOkay = !avoid->ContainsPoint(f); + + // Now look for an unused edge that joins at the start or finish of + // our chain (if permitted by the avoid list). + SEdge *se; + for(se = src->l.First(); se; se = src->l.NextAfter(se)) { + if(se->tag) continue; + if(startOkay && s.Equals(se->b)) { + dest->l.AddToBeginning(se); + s = se->a; + se->tag = 1; + startOkay = !avoid->ContainsPoint(s); + } else if(finishOkay && f.Equals(se->a)) { + dest->l.Add(se); + f = se->b; + se->tag = 1; + finishOkay = !avoid->ContainsPoint(f); + } else { + continue; + } + + added = true; + } + } while(added); + + src->l.RemoveTagged(); +} + void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv, Vector *pt, Vector *enin, Vector *enout, @@ -425,11 +478,45 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, } } + // Record all the points where more than two edges join, which I will call + // the choosing points. If two edges join at a non-choosing point, then + // they must either both be kept or both be discarded (since that would + // otherwise create an open contour). + SPointList choosing; + ZERO(&choosing); + SEdge *se; + for(se = orig.l.First(); se; se = orig.l.NextAfter(se)) { + choosing.IncrementTagFor(se->a); + choosing.IncrementTagFor(se->b); + } + for(se = inter.l.First(); se; se = inter.l.NextAfter(se)) { + choosing.IncrementTagFor(se->a); + choosing.IncrementTagFor(se->b); + } + SPoint *sp; + for(sp = choosing.l.First(); sp; sp = choosing.l.NextAfter(sp)) { + if(sp->tag == 2) { + sp->tag = 1; + } else { + sp->tag = 0; + } + } + choosing.l.RemoveTagged(); + + // The list of edges to trim our new surface, a combination of edges from + // our original and intersecting edge lists. SEdgeList final; ZERO(&final); - SEdge *se; - for(se = orig.l.First(); se; se = orig.l.NextAfter(se)) { + while(orig.l.n > 0) { + SEdgeList chain; + ZERO(&chain); + FindChainAvoiding(&orig, &chain, &choosing); + + // Arbitrarily choose an edge within the chain to classify; they + // should all be the same, though. + se = &(chain.l.elem[chain.l.n/2]); + Point2d auv = (se->a).ProjectXy(), buv = (se->b).ProjectXy(); @@ -449,11 +536,21 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, if(KeepEdge(type, opA, indir_shell, outdir_shell, indir_orig, outdir_orig)) { - final.AddEdge(se->a, se->b, se->auxA, se->auxB); + for(se = chain.l.First(); se; se = chain.l.NextAfter(se)) { + final.AddEdge(se->a, se->b, se->auxA, se->auxB); + } } + chain.Clear(); } - for(se = inter.l.First(); se; se = inter.l.NextAfter(se)) { + while(inter.l.n > 0) { + SEdgeList chain; + ZERO(&chain); + FindChainAvoiding(&inter, &chain, &choosing); + + // Any edge in the chain, same as above. + se = &(chain.l.elem[chain.l.n/2]); + Point2d auv = (se->a).ProjectXy(), buv = (se->b).ProjectXy(); @@ -473,8 +570,11 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, if(KeepEdge(type, opA, indir_shell, outdir_shell, indir_orig, outdir_orig)) { - final.AddEdge(se->a, se->b, se->auxA, se->auxB); + for(se = chain.l.First(); se; se = chain.l.NextAfter(se)) { + final.AddEdge(se->a, se->b, se->auxA, se->auxB); + } } + chain.Clear(); } // Cull extraneous edges; duplicates or anti-parallel pairs. In particular, @@ -491,11 +591,12 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, final.l.ClearTags(); if(!final.AssemblePolygon(&poly, NULL, true)) { into->booleanFailed = true; - dbp("failed: I=%d", I); + dbp("failed: I=%d, avoid=%d", I, choosing.l.n); DEBUGEDGELIST(&final, &ret); } poly.Clear(); + choosing.Clear(); final.Clear(); inter.Clear(); orig.Clear(); diff --git a/srf/surface.h b/srf/surface.h index 0de3b08..d10e25b 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -229,6 +229,7 @@ public: Vector *surfn, DWORD auxA, SShell *shell, SShell *sha, SShell *shb); + void FindChainAvoiding(SEdgeList *src, SEdgeList *dest, SPointList *avoid); SSurface MakeCopyTrimAgainst(SShell *parent, SShell *a, SShell *b, SShell *into, int type); void TrimFromEdgeList(SEdgeList *el, bool asUv); diff --git a/wishlist.txt b/wishlist.txt index 655a89f..566f30a 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,7 +1,5 @@ -marching algorithm for surface intersection grid -classification of edges only as necessary (all same for span between intersections) associative entities from solid model, as a special group rounding, as a special group