Compute surface intersections in a way that is closer to what I

will do for real; now handling the special cases of plane against a
surface of extrusion. Still need to fix up line-surface
intersection to work for curved things, but then some simple curved
cases should work (as well as plane-plane).

[git-p4: depot-paths = "//depot/solvespace/": change = 1919]
This commit is contained in:
Jonathan Westhues 2009-02-23 02:06:02 -08:00
parent 9ade574d36
commit 3da1e1d390
6 changed files with 246 additions and 68 deletions

5
dsc.h
View File

@ -49,6 +49,9 @@ public:
Vector b0, Vector b1,
bool *skew,
double *pa=NULL, double *pb=NULL);
static Vector AtIntersectionOfPlaneAndLine(Vector n, double d,
Vector p0, Vector p1,
bool *parallel);
double Element(int i);
bool Equals(Vector v, double tol=LENGTH_EPS);
@ -93,8 +96,10 @@ public:
double DistanceTo(Point2d p);
double DistanceToLine(Point2d p0, Point2d dp, bool segment);
double Magnitude(void);
double MagSquared(void);
Point2d WithMagnitude(double v);
Point2d Normal(void);
bool Equals(Point2d v, double tol=LENGTH_EPS);
};
// A simple list

View File

@ -38,8 +38,8 @@ 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);
if(agnstB) agnstB->AllPointsIntersecting(prev, *p, &il);
if(agnstA) agnstA->AllPointsIntersecting(prev, *p, &il, true, true);
if(agnstB) agnstB->AllPointsIntersecting(prev, *p, &il, true, true);
// If any intersections exist, sort them in order along the
// line and add them to the curve.
@ -426,7 +426,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
}
final.l.RemoveTagged();
// if(I == 10) DEBUGEDGELIST(&final, &ret);
// if(I == 0) DEBUGEDGELIST(&final, &ret);
// Use our reassembled edges to trim the new surface.
ret.TrimFromEdgeList(&final);
@ -485,13 +485,13 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
a->MakeIntersectionCurvesAgainst(b, this);
I = 100;
if(b->surface.n == 0 || a->surface.n == 0 || a->surface.n == 6) {
if(b->surface.n == 0 || a->surface.n == 0) {
// Then trim and copy the surfaces
a->CopySurfacesTrimAgainst(b, this, type, true);
b->CopySurfacesTrimAgainst(a, this, type, false);
} else {
I = 0;
a->CopySurfacesTrimAgainst(b, this, type, true);
I = -1;
b->CopySurfacesTrimAgainst(a, this, type, false);
}

View File

@ -387,6 +387,33 @@ SSurface SSurface::FromExtrusionOf(SBezier *sb, Vector t0, Vector t1) {
return ret;
}
bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) {
int i;
if(degn != 1) return false;
Vector along = (ctrl[0][1]).Minus(ctrl[0][0]);
for(i = 0; i <= degm; i++) {
if((fabs(weight[i][1] - weight[i][0]) < LENGTH_EPS) &&
((ctrl[i][1]).Minus(ctrl[i][0])).Equals(along))
{
continue;
}
return false;
}
// yes, we are a surface of extrusion; copy the original curve and return
if(of) {
for(i = 0; i <= degm; i++) {
of->weight[i] = weight[i][0];
of->ctrl[i] = ctrl[i][0];
}
of->deg = degm;
*alongp = along;
}
return true;
}
SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) {
SSurface ret;
ZERO(&ret);

View File

@ -181,7 +181,10 @@ public:
void TrimFromEdgeList(SEdgeList *el);
void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SShell *into);
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *l);
void AddExactIntersectionCurve(SBezier *sb, hSSurface hsb,
SShell *agnstA, SShell *agnstB, SShell *into);
void AllPointsIntersecting(Vector a, Vector b,
List<SInter> *l, bool seg, bool trimmed);
void ClosestPointTo(Vector p, double *u, double *v);
Vector PointAt(double u, double v);
@ -190,6 +193,7 @@ public:
void GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin);
bool CoincidentWithPlane(Vector n, double d);
bool CoincidentWith(SSurface *ss, bool sameNormal);
bool IsExtrusion(SBezier *of, Vector *along);
void TriangulateInto(SShell *shell, SMesh *sm);
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv);
@ -217,7 +221,8 @@ public:
void CopySurfacesTrimAgainst(SShell *against, SShell *into, int t, bool a);
void MakeIntersectionCurvesAgainst(SShell *against, SShell *into);
void MakeClassifyingBsps(void);
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il);
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il,
bool seg, bool trimmed);
void MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
SEdgeList *el);
void CleanupAfterBoolean(void);

View File

@ -1,5 +1,28 @@
#include "solvespace.h"
void SSurface::AddExactIntersectionCurve(SBezier *sb, hSSurface hsb,
SShell *agnstA, SShell *agnstB, SShell *into)
{
SCurve sc;
ZERO(&sc);
sc.surfA = h;
sc.surfB = hsb;
sb->MakePwlInto(&(sc.pts));
Vector *prev = NULL, *v;
for(v = sc.pts.First(); v; v = sc.pts.NextAfter(v)) {
if(prev) SS.nakedEdges.AddEdge(*prev, *v);
prev = v;
}
// Now split the line where it intersects our existing surfaces
SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB);
sc.Clear();
split.interCurve = true;
into->curve.AddAndAssignId(&split);
}
void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SShell *into)
{
@ -12,86 +35,174 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
return;
}
if(degm == 1 && degn == 1 && b->degm == 1 && b->degn == 1) {
// Plane-plane intersection, easy; result is a line
Vector pta = ctrl[0][0], ptb = b->ctrl[0][0];
Vector na = NormalAt(0, 0), nb = b->NormalAt(0, 0);
na = na.WithMagnitude(1);
nb = nb.WithMagnitude(1);
Vector d = (na.Cross(nb));
if(d.Magnitude() < LENGTH_EPS) {
// parallel planes, no intersection
return;
if((degm == 1 && degn == 1 && b->IsExtrusion(NULL, NULL)) ||
(b->degm == 1 && b->degn == 1 && this->IsExtrusion(NULL, NULL)))
{
// The intersection between a plane and a surface of extrusion
SSurface *splane, *sext;
if(degm == 1 && degn == 1) {
splane = this;
sext = b;
} else {
splane = b;
sext = this;
}
Vector inter = Vector::AtIntersectionOfPlanes(na, na.Dot(pta),
nb, nb.Dot(ptb));
Vector n = splane->NormalAt(0, 0).WithMagnitude(1), along;
double d = n.Dot(splane->PointAt(0, 0));
SBezier bezier;
(void)sext->IsExtrusion(&bezier, &along);
// The intersection curve can't be longer than the longest curve
// that lies in both planes, which is the diagonal of the shorter;
// so just pick one, and then give some slop, not critical.
double maxl = ((ctrl[0][0]).Minus(ctrl[1][1])).Magnitude();
if(fabs(n.Dot(along)) < LENGTH_EPS) {
// Direction of extrusion is parallel to plane; so intersection
// is zero or more lines. Build a line within the plane, and
// normal to the direction of extrusion, and intersect that line
// against the surface; each intersection point corresponds to
// a line.
Vector pm, alu, p0, dp;
// a point halfway along the extrusion
pm = ((sext->ctrl[0][0]).Plus(sext->ctrl[0][1])).ScaledBy(0.5);
alu = along.WithMagnitude(1);
dp = (n.Cross(along)).WithMagnitude(1);
// n, alu, and dp form an orthogonal csys; set n component to
// place it on the plane, alu component to lie halfway along
// extrusion, and dp component doesn't matter so zero
p0 = n.ScaledBy(d).Plus(alu.ScaledBy(pm.Dot(alu)));
Vector v;
SCurve sc;
ZERO(&sc);
sc.surfA = h;
sc.surfB = b->h;
v = inter.Minus(d.WithMagnitude(5*maxl));
sc.pts.Add(&v);
v = inter.Plus(d.WithMagnitude(5*maxl));
sc.pts.Add(&v);
List<SInter> inters;
ZERO(&inters);
sext->AllPointsIntersecting(p0, p0.Plus(dp), &inters, false, false);
SInter *si;
for(si = inters.First(); si; si = inters.NextAfter(si)) {
Vector al = along.ScaledBy(0.5);
SBezier bezier;
bezier = SBezier::From((si->p).Minus(al), (si->p).Plus(al));
// Now split the line where it intersects our existing surfaces
SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB);
sc.Clear();
AddExactIntersectionCurve(&bezier, b->h, agnstA, agnstB, into);
}
split.interCurve = true;
into->curve.AddAndAssignId(&split);
inters.Clear();
} else {
// Direction of extrusion is not parallel to plane; so
// intersection is projection of extruded curve into our plane
int i;
for(i = 0; i <= bezier.deg; i++) {
Vector p0 = bezier.ctrl[i],
p1 = p0.Plus(along);
bezier.ctrl[i] =
Vector::AtIntersectionOfPlaneAndLine(n, d, p0, p1, NULL);
}
AddExactIntersectionCurve(&bezier, b->h, agnstA, agnstB, into);
}
}
// need to implement general numerical surface intersection for tough
// cases, just giving up for now
}
void SSurface::AllPointsIntersecting(Vector a, Vector b, List<SInter> *l) {
if(degm == 1 && degn == 1) {
// line-plane intersection
Vector p = ctrl[0][0];
Vector n = NormalAt(0, 0).WithMagnitude(1);
double d = n.Dot(p);
if((n.Dot(a) - d < -LENGTH_EPS && n.Dot(b) - d > LENGTH_EPS) ||
(n.Dot(b) - d < -LENGTH_EPS && n.Dot(a) - d > LENGTH_EPS))
{
// It crosses the plane, one point of intersection
// (a + t*(b - a)) dot n = d
// (a dot n) + t*((b - a) dot n) = d
// t = (d - (a dot n))/((b - a) dot n)
double t = (d - a.Dot(n)) / ((b.Minus(a)).Dot(n));
Vector pi = a.Plus((b.Minus(a)).ScaledBy(t));
//-----------------------------------------------------------------------------
// Find all points where a line through a and b intersects our surface, and
// add them to the list. If seg is true then report only intersections that
// lie within the finite line segment (not including the endpoints); otherwise
// we work along the infinite line.
//-----------------------------------------------------------------------------
void SSurface::AllPointsIntersecting(Vector a, Vector b,
List<SInter> *l, bool seg, bool trimmed)
{
Vector ba = b.Minus(a);
double bam = ba.Magnitude();
Point2d puv, dummy = { 0, 0 };
ClosestPointTo(pi, &(puv.x), &(puv.y));
int c = bsp->ClassifyPoint(puv, dummy);
typedef struct {
int tag;
Point2d p;
} Inter;
List<Inter> inters;
ZERO(&inters);
if(c != SBspUv::OUTSIDE) {
SInter si;
si.p = pi;
si.surfNormal = NormalAt(puv.x, puv.y);
si.surface = h;
si.onEdge = (c != SBspUv::INSIDE);
l->Add(&si);
// First, get all the intersections between the infinite ray and the
// untrimmed surface.
int i, j;
for(i = 0; i < degm; i++) {
for(j = 0; j < degn; j++) {
// Intersect the ray with each face in the control polyhedron
Vector p00 = ctrl[i][j],
p01 = ctrl[i][j+1],
p10 = ctrl[i+1][j];
Vector u = p01.Minus(p00), v = p10.Minus(p00);
Vector n = (u.Cross(v)).WithMagnitude(1);
double d = n.Dot(p00);
bool parallel;
Vector pi =
Vector::AtIntersectionOfPlaneAndLine(n, d, a, b, &parallel);
if(parallel) continue;
double ui = (pi.Minus(p00)).Dot(u) / u.MagSquared(),
vi = (pi.Minus(p00)).Dot(v) / v.MagSquared();
double tol = 1e-2;
if(ui < -tol || ui > 1 + tol || vi < -tol || vi > 1 + tol) {
continue;
}
Inter inter;
ClosestPointTo(pi, &inter.p.x, &inter.p.y);
inters.Add(&inter);
}
}
// Remove duplicate intersection points
inters.ClearTags();
for(i = 0; i < inters.n; i++) {
for(j = i + 1; j < inters.n; j++) {
if(inters.elem[i].p.Equals(inters.elem[j].p)) {
inters.elem[j].tag = 1;
}
}
}
inters.RemoveTagged();
for(i = 0; i < inters.n; i++) {
Point2d puv = inters.elem[i].p;
// Make sure the point lies within the finite line segment
Vector pxyz = PointAt(puv.x, puv.y);
double t = (pxyz.Minus(a)).DivPivoting(ba);
if(seg && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) {
continue;
}
// And that it lies inside our trim region
Point2d dummy = { 0, 0 };
int c = bsp->ClassifyPoint(puv, dummy);
if(trimmed && c == SBspUv::OUTSIDE) {
continue;
}
// It does, so generate the intersection
SInter si;
si.p = pxyz;
si.surfNormal = NormalAt(puv.x, puv.y);
si.surface = h;
si.onEdge = (c != SBspUv::INSIDE);
l->Add(&si);
}
inters.Clear();
}
void SShell::AllPointsIntersecting(Vector a, Vector b, List<SInter> *il) {
void SShell::AllPointsIntersecting(Vector a, Vector b,
List<SInter> *il, bool seg, bool trimmed)
{
SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
ss->AllPointsIntersecting(a, b, il);
ss->AllPointsIntersecting(a, b, il, seg, trimmed);
}
}
@ -118,8 +229,7 @@ int SShell::ClassifyPoint(Vector p, Vector pout) {
// the point lies on a surface, but use only one side for in/out
// testing)
Vector ray = Vector::From(Random(1), Random(1), Random(1));
ray = ray.WithMagnitude(1e4);
AllPointsIntersecting(p.Minus(ray), p.Plus(ray), &l);
AllPointsIntersecting(p.Minus(ray), p.Plus(ray), &l, false, true);
double dmin = VERY_POSITIVE;
ret = OUTSIDE; // no intersections means it's outside

View File

@ -620,6 +620,26 @@ Vector Vector::AtIntersectionOfLines(Vector a0, Vector a1,
return pi;
}
Vector Vector::AtIntersectionOfPlaneAndLine(Vector n, double d,
Vector p0, Vector p1,
bool *parallel)
{
Vector dp = p1.Minus(p0);
if(fabs(n.Dot(dp)) < LENGTH_EPS) {
if(parallel) *parallel = true;
return Vector::From(0, 0, 0);
}
if(parallel) *parallel = false;
// n dot (p0 + t*dp) = d
// (n dot p0) + t * (n dot dp) = d
double t = (d - n.Dot(p0)) / (n.Dot(dp));
return p0.Plus(dp.ScaledBy(t));
}
Point2d Point2d::Plus(Point2d b) {
Point2d r;
r.x = x + b.x;
@ -641,6 +661,10 @@ Point2d Point2d::ScaledBy(double s) {
return r;
}
double Point2d::MagSquared(void) {
return x*x + y*y;
}
double Point2d::Magnitude(void) {
return sqrt(x*x + y*y);
}
@ -691,3 +715,10 @@ Point2d Point2d::Normal(void) {
return ret;
}
bool Point2d::Equals(Point2d v, double tol) {
double dx = v.x - x; if(dx < -tol || dx > tol) return false;
double dy = v.y - y; if(dy < -tol || dy > tol) return false;
return (this->Minus(v)).MagSquared() < tol*tol;
}