Add beginnings of marching surface intersection; I can find all the

boundary points, at least. That required some changes to what gets
passed around (for example because to project a point onto this
inexact curve, we need to know which two surfaces it trims so that
we can do a Newton's method on them).

And fix stupidity in the way that I calculated edge normals; I just
did normal in uv space, and there's no particular reason why that
would be normal in xyz. So edges in long skinny surfaces failed,
for example.

[git-p4: depot-paths = "//depot/solvespace/": change = 1990]
solver
Jonathan Westhues 2009-06-18 23:56:33 -08:00
parent 314227ead7
commit 666ea1c047
8 changed files with 180 additions and 51 deletions

View File

@ -285,6 +285,27 @@ void SEdgeList::MergeCollinearSegments(Vector a, Vector b) {
l.RemoveTagged(); l.RemoveTagged();
} }
void SPointList::Clear(void) {
l.Clear();
}
bool SPointList::ContainsPoint(Vector pt) {
SPoint *p;
for(p = l.First(); p; p = l.NextAfter(p)) {
if(pt.Equals(p->p)) {
return true;
}
}
return false;
}
void SPointList::Add(Vector pt) {
SPoint p;
ZERO(&p);
p.p = pt;
l.Add(&p);
}
void SContour::AddPoint(Vector p) { void SContour::AddPoint(Vector p) {
SPoint sp; SPoint sp;
sp.tag = 0; sp.tag = 0;

View File

@ -42,6 +42,15 @@ public:
Vector p; Vector p;
}; };
class SPointList {
public:
List<SPoint> l;
void Clear(void);
bool ContainsPoint(Vector pt);
void Add(Vector pt);
};
class SContour { class SContour {
public: public:
int tag; int tag;

View File

@ -266,14 +266,13 @@ void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv,
Vector *pt, Vector *pt,
Vector *enin, Vector *enout, Vector *enin, Vector *enout,
Vector *surfn, Vector *surfn,
DWORD auxA, SShell *shell) DWORD auxA,
SShell *shell, SShell *sha, SShell *shb)
{ {
// the midpoint of the edge // the midpoint of the edge
Point2d muv = (auv.Plus(buv)).ScaledBy(0.5); Point2d muv = (auv.Plus(buv)).ScaledBy(0.5);
// a vector parallel to the edge // a vector parallel to the edge
Point2d abuv = buv.Minus(auv).WithMagnitude(0.01); Point2d abuv = buv.Minus(auv).WithMagnitude(0.01);
// the edge's inner normal
Point2d enuv = abuv.Normal();
*pt = PointAt(muv); *pt = PointAt(muv);
@ -288,15 +287,35 @@ void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv,
sc->exact.ClosestPointTo(*pt, &t, false); sc->exact.ClosestPointTo(*pt, &t, false);
*pt = sc->exact.PointAt(t); *pt = sc->exact.PointAt(t);
ClosestPointTo(*pt, &muv); ClosestPointTo(*pt, &muv);
} else if(!sc->isExact) {
SSurface *trimmedA = sc->GetSurfaceA(sha, shb),
*trimmedB = sc->GetSurfaceB(sha, shb);
*pt = trimmedA->ClosestPointOnThisAndSurface(trimmedB, *pt);
ClosestPointTo(*pt, &muv);
} }
*surfn = NormalAt(muv.x, muv.y); *surfn = NormalAt(muv.x, muv.y);
// Compute the edge's inner normal in xyz space.
Vector ab = (PointAt(auv)).Minus(PointAt(buv)),
enxyz = (ab.Cross(*surfn)).WithMagnitude(SS.ChordTolMm());
// And based on that, compute the edge's inner normal in uv space. This
// vector is perpendicular to the edge in xyz, but not necessarily in uv.
Vector tu, tv;
TangentsAt(muv.x, muv.y, &tu, &tv);
Point2d enuv;
enuv.x = enxyz.Dot(tu) / tu.MagSquared();
enuv.y = enxyz.Dot(tv) / tv.MagSquared();
// Don't let the magnitude get too tiny at small chord tolerances; we
// will otherwise have numerical problems subtracting nearly-equal
// numbers.
enuv = enuv.WithMagnitude(0.01);
// Compute the inner and outer normals of this edge (within the srf), // Compute the inner and outer normals of this edge (within the srf),
// in xyz space. These are not necessarily antiparallel, if the // in xyz space. These are not necessarily antiparallel, if the
// surface is curved. // surface is curved.
Vector pin = PointAt(muv.Plus(enuv)), Vector pin = PointAt(muv.Minus(enuv)),
pout = PointAt(muv.Minus(enuv)); pout = PointAt(muv.Plus(enuv));
*enin = pin.Minus(*pt), *enin = pin.Minus(*pt),
*enout = pout.Minus(*pt); *enout = pout.Minus(*pt);
} }
@ -307,10 +326,14 @@ void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv,
// also need a pointer to the shell that contains our own surface, since that // also need a pointer to the shell that contains our own surface, since that
// contains our original trim curves. // contains our original trim curves.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
SShell *sha, SShell *shb,
SShell *into, SShell *into,
int type, bool opA) int type)
{ {
bool opA = (parent == sha);
SShell *agnst = opA ? shb : sha;
SSurface ret; SSurface ret;
// The returned surface is identical, just the trim curves change // The returned surface is identical, just the trim curves change
ret = *this; ret = *this;
@ -400,7 +423,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
Vector pt, enin, enout, surfn; Vector pt, enin, enout, surfn;
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn, ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
se->auxA, into); se->auxA, into, sha, shb);
int indir_shell, outdir_shell, indir_orig, outdir_orig; int indir_shell, outdir_shell, indir_orig, outdir_orig;
@ -424,7 +447,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
Vector pt, enin, enout, surfn; Vector pt, enin, enout, surfn;
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn, ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
se->auxA, into); se->auxA, into, sha, shb);
int indir_shell, outdir_shell, indir_orig, outdir_orig; int indir_shell, outdir_shell, indir_orig, outdir_orig;
@ -467,13 +490,13 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
return ret; return ret;
} }
void SShell::CopySurfacesTrimAgainst(SShell *against, SShell *into, void SShell::CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into,
int type, bool opA) int type)
{ {
SSurface *ss; SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
SSurface ssn; SSurface ssn;
ssn = ss->MakeCopyTrimAgainst(against, this, into, type, opA); ssn = ss->MakeCopyTrimAgainst(this, sha, shb, into, type);
ss->newH = into->surface.AddAndAssignId(&ssn); ss->newH = into->surface.AddAndAssignId(&ssn);
I++; I++;
} }
@ -506,20 +529,9 @@ void SShell::CleanupAfterBoolean(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SShell::RewriteSurfaceHandlesForCurves(SShell *a, SShell *b) { void SShell::RewriteSurfaceHandlesForCurves(SShell *a, SShell *b) {
SCurve *sc; SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
if(sc->source == SCurve::FROM_A) { sc->surfA = sc->GetSurfaceA(a, b)->newH,
sc->surfA = a->surface.FindById(sc->surfA)->newH; sc->surfB = sc->GetSurfaceB(a, b)->newH;
sc->surfB = a->surface.FindById(sc->surfB)->newH;
} else if(sc->source == SCurve::FROM_B) {
sc->surfA = b->surface.FindById(sc->surfA)->newH;
sc->surfB = b->surface.FindById(sc->surfB)->newH;
} else if(sc->source == SCurve::FROM_INTERSECTION) {
sc->surfA = a->surface.FindById(sc->surfA)->newH;
sc->surfB = b->surface.FindById(sc->surfB)->newH;
} else {
oops();
}
} }
} }
@ -591,17 +603,9 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
SCurve *sc; SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
SSurface *srfA, *srfB; SSurface *srfA = sc->GetSurfaceA(a, b),
if(sc->source == SCurve::FROM_A) { *srfB = sc->GetSurfaceB(a, b);
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); sc->RemoveShortSegments(srfA, srfB);
} }
@ -613,16 +617,14 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
a->MakeClassifyingBsps(this); a->MakeClassifyingBsps(this);
b->MakeClassifyingBsps(this); b->MakeClassifyingBsps(this);
// Then trim and copy the surfaces
if(b->surface.n == 0 || a->surface.n == 0) { if(b->surface.n == 0 || a->surface.n == 0) {
I = 1023123; I = 1000000;
a->CopySurfacesTrimAgainst(b, this, type, true);
b->CopySurfacesTrimAgainst(a, this, type, false);
} else { } else {
I = 0; I = 0;
a->CopySurfacesTrimAgainst(b, this, type, true);
b->CopySurfacesTrimAgainst(a, this, type, false);
} }
// Then trim and copy the surfaces
a->CopySurfacesTrimAgainst(a, b, this, type);
b->CopySurfacesTrimAgainst(a, b, this, type);
// Now that we've copied the surfaces, we know their new hSurfaces, so // 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 // rewrite the curves to refer to the surfaces by their handles in the

View File

@ -401,6 +401,26 @@ void SCurve::Clear(void) {
pts.Clear(); pts.Clear();
} }
SSurface *SCurve::GetSurfaceA(SShell *a, SShell *b) {
if(source == FROM_A) {
return a->surface.FindById(surfA);
} else if(source == FROM_B) {
return b->surface.FindById(surfA);
} else if(source == FROM_INTERSECTION) {
return a->surface.FindById(surfA);
} else oops();
}
SSurface *SCurve::GetSurfaceB(SShell *a, SShell *b) {
if(source == FROM_A) {
return a->surface.FindById(surfB);
} else if(source == FROM_B) {
return b->surface.FindById(surfB);
} else if(source == FROM_INTERSECTION) {
return b->surface.FindById(surfB);
} else oops();
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// When we split line segments wherever they intersect a surface, we introduce // 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 // extra pwl points. This may create very short edges that could be removed

View File

@ -415,7 +415,7 @@ Vector SSurface::ClosestPointOnThisAndSurface(SSurface *srf2, Vector p) {
(srf[j])->ClosestPointTo(p, &(puv[j]), false); (srf[j])->ClosestPointTo(p, &(puv[j]), false);
} }
for(i = 0; i < 4; i++) { for(i = 0; i < 10; i++) {
Vector tu[2], tv[2], cp[2], n[2]; Vector tu[2], tv[2], cp[2], n[2];
double d[2]; double d[2];
@ -428,6 +428,8 @@ Vector SSurface::ClosestPointOnThisAndSurface(SSurface *srf2, Vector p) {
d[j] = (n[j]).Dot(cp[j]); d[j] = (n[j]).Dot(cp[j]);
} }
if((cp[0]).Equals(cp[1], RATPOLY_EPS)) break;
Vector p0 = Vector::AtIntersectionOfPlanes(n[0], d[0], n[1], d[1]), Vector p0 = Vector::AtIntersectionOfPlanes(n[0], d[0], n[1], d[1]),
dp = (n[0]).Cross(n[1]); dp = (n[0]).Cross(n[1]);

View File

@ -159,6 +159,8 @@ public:
SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
SSurface *srfA, SSurface *srfB); SSurface *srfA, SSurface *srfB);
void RemoveShortSegments(SSurface *srfA, SSurface *srfB); void RemoveShortSegments(SSurface *srfA, SSurface *srfB);
SSurface *GetSurfaceA(SShell *a, SShell *b);
SSurface *GetSurfaceB(SShell *a, SShell *b);
void Clear(void); void Clear(void);
}; };
@ -225,9 +227,10 @@ public:
void EdgeNormalsWithinSurface(Point2d auv, Point2d buv, void EdgeNormalsWithinSurface(Point2d auv, Point2d buv,
Vector *pt, Vector *enin, Vector *enout, Vector *pt, Vector *enin, Vector *enout,
Vector *surfn, Vector *surfn,
DWORD auxA, SShell *shell); DWORD auxA,
SSurface MakeCopyTrimAgainst(SShell *against, SShell *parent, SShell *into, SShell *shell, SShell *sha, SShell *shb);
int type, bool opA); SSurface MakeCopyTrimAgainst(SShell *parent, SShell *a, SShell *b,
SShell *into, int type);
void TrimFromEdgeList(SEdgeList *el, bool asUv); void TrimFromEdgeList(SEdgeList *el, bool asUv);
void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SShell *into); SShell *into);
@ -305,7 +308,8 @@ public:
static const int AS_INTERSECT = 12; static const int AS_INTERSECT = 12;
void MakeFromBoolean(SShell *a, SShell *b, int type); void MakeFromBoolean(SShell *a, SShell *b, int type);
void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into); void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into);
void CopySurfacesTrimAgainst(SShell *against, SShell *into, int t, bool a); void CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into,
int type);
void MakeIntersectionCurvesAgainst(SShell *against, SShell *into); void MakeIntersectionCurvesAgainst(SShell *against, SShell *into);
void MakeClassifyingBsps(SShell *useCurvesFrom); void MakeClassifyingBsps(SShell *useCurvesFrom);
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il, void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il,

View File

@ -276,10 +276,77 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
inters.Clear(); inters.Clear();
lv.Clear(); lv.Clear();
} else {
// Try intersecting the surfaces numerically, by a marching algorithm.
// First, we find all the intersections between a surface and the
// boundary of the other surface.
SPointList spl;
ZERO(&spl);
int a;
for(a = 0; a < 2; a++) {
SShell *shA = (a == 0) ? agnstA : agnstB,
*shB = (a == 0) ? agnstB : agnstA;
SSurface *srfA = (a == 0) ? this : b,
*srfB = (a == 0) ? b : this;
SEdgeList el;
ZERO(&el);
srfA->MakeEdgesInto(shA, &el, false, NULL);
SEdge *se;
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
List<SInter> lsi;
ZERO(&lsi);
srfB->AllPointsIntersecting(se->a, se->b, &lsi,
true, true, false);
if(lsi.n == 0) continue;
// Find the other surface that this curve trims.
hSCurve hsc = { se->auxA };
SCurve *sc = shA->curve.FindById(hsc);
hSSurface hother = (sc->surfA.v == srfA->h.v) ?
sc->surfB : sc->surfA;
SSurface *other = shA->surface.FindById(hother);
SInter *si;
for(si = lsi.First(); si; si = lsi.NextAfter(si)) {
Vector p = si->p;
double u, v;
srfA->ClosestPointTo(p, &u, &v);
srfA->PointOnSurfaces(srfB, other, &u, &v);
p = srfA->PointAt(u, v);
if(!spl.ContainsPoint(p)) spl.Add(p);
}
lsi.Clear();
} }
// need to implement general numerical surface intersection for tough el.Clear();
// cases, just giving up for now }
SPoint *sp;
if(spl.l.n == 2) {
SCurve sc;
ZERO(&sc);
sc.surfA = h;
sc.surfB = b->h;
sc.isExact = false;
sc.source = SCurve::FROM_INTERSECTION;
SCurvePt scpt;
scpt.p = (spl.l.elem[0].p);
sc.pts.Add(&scpt);
scpt.p = (spl.l.elem[1].p);
sc.pts.Add(&scpt);
SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b);
sc.Clear();
into->curve.AddAndAssignId(&split);
}
spl.Clear();
}
} }

View File

@ -1,8 +1,12 @@
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
----- -----
line styles (color, thickness) line styles (color, thickness)
marching algorithm for surface intersection
loop detection loop detection
IGES export IGES export
incremental regen of entities incremental regen of entities