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