Generate intersection curves for surfaces of extrusion along a

parallel axis (which are always lines parallel to that axis).

Remove short pwl segments when possible, to avoid short edges that
get misclassified.

[git-p4: depot-paths = "//depot/solvespace/": change = 1952]
solver
Jonathan Westhues 2009-05-17 23:26:51 -08:00
parent d6d198ee40
commit 40ed1b7ac1
7 changed files with 174 additions and 25 deletions

View File

@ -36,9 +36,9 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
ret = *this;
ZERO(&(ret.pts));
Vector *p = pts.First();
SCurvePt *p = pts.First();
if(!p) oops();
Vector prev = *p;
SCurvePt prev = *p;
ret.pts.Add(p);
p = pts.NextAfter(p);
@ -47,8 +47,10 @@ 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, true,true,true);
if(agnstB) agnstB->AllPointsIntersecting(prev, *p, &il, true,true,true);
if(agnstA)
agnstA->AllPointsIntersecting(prev.p, p->p, &il, true, true, true);
if(agnstB)
agnstB->AllPointsIntersecting(prev.p, p->p, &il, true, true, true);
if(il.n > 0) {
// The intersections were generated by intersecting the pwl
@ -65,8 +67,8 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
// And now sort them in order along the line. Note that we must
// do that after refining, in case the refining would make two
// points switch places.
LineStart = prev;
LineDirection = p->Minus(prev);
LineStart = prev.p;
LineDirection = (p->p).Minus(prev.p);
qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine);
// And now uses the intersections to generate our split pwl edge(s)
@ -76,7 +78,11 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
// On-edge intersection will generate same split point for
// both surfaces, so don't create zero-length edge.
if(!prev.Equals(pi->p)) {
ret.pts.Add(&(pi->p));
SCurvePt scpt;
scpt.tag = 0;
scpt.p = pi->p;
scpt.vertex = true;
ret.pts.Add(&scpt);
}
prev = pi->p;
}
@ -329,8 +335,8 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
int i;
for(i = 1; i < sc->pts.n; i++) {
Vector a = sc->pts.elem[i-1],
b = sc->pts.elem[i];
Vector a = sc->pts.elem[i-1].p,
b = sc->pts.elem[i].p;
Point2d auv, buv;
ss->ClosestPointTo(a, &(auv.x), &(auv.y));
@ -506,6 +512,22 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
// the surfaces in B (which is all of the intersection curves).
a->MakeIntersectionCurvesAgainst(b, this);
SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
SSurface *srfA, *srfB;
if(sc->source == SCurve::FROM_A) {
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);
}
if(b->surface.n == 0 || a->surface.n == 0) {
// Then trim and copy the surfaces
a->CopySurfacesTrimAgainst(b, this, type, true);
@ -518,7 +540,6 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
// 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
// result.
SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
if(sc->source == SCurve::FROM_A) {
sc->surfA = a->surface.FindById(sc->surfA)->newH;

View File

@ -388,9 +388,10 @@ SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q) {
ret.surfA = a->surfA;
ret.surfB = a->surfB;
Vector *p;
SCurvePt *p;
for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) {
Vector pp = (q.Rotate(*p)).Plus(t);
SCurvePt pp = *p;
pp.p = (q.Rotate(p->p)).Plus(t);
ret.pts.Add(&pp);
}
return ret;
@ -400,6 +401,55 @@ void SCurve::Clear(void) {
pts.Clear();
}
//-----------------------------------------------------------------------------
// 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
// without violating the chord tolerance. Those are ugly, and also break
// stuff in the Booleans. So remove them.
//-----------------------------------------------------------------------------
void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) {
if(pts.n < 2) return;
pts.ClearTags();
Vector prev = pts.elem[0].p;
int i, a;
for(i = 1; i < pts.n - 1; i++) {
SCurvePt *sct = &(pts.elem[i]),
*scn = &(pts.elem[i+1]);
if(sct->vertex) {
prev = sct->p;
continue;
}
bool mustKeep = false;
// We must check against both surfaces; the piecewise linear edge
// may have a different chord tolerance in the two surfaces. (For
// example, a circle in the surface of a cylinder is just a straight
// line, so it always has perfect chord tol, but that circle in
// a plane is a circle so it doesn't).
for(a = 0; a < 2; a++) {
SSurface *srf = (a == 0) ? srfA : srfB;
Vector puv, nuv;
srf->ClosestPointTo(prev, &(puv.x), &(puv.y));
srf->ClosestPointTo(scn->p, &(nuv.x), &(nuv.y));
if(srf->ChordToleranceForEdge(nuv, puv) > SS.ChordTolMm()) {
mustKeep = true;
}
}
if(mustKeep) {
prev = sct->p;
} else {
sct->tag = 1;
// and prev is unchanged, since there's no longer any point
// in between
}
}
pts.RemoveTagged();
}
STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) {
STrimBy stb;
ZERO(&stb);
@ -407,12 +457,12 @@ STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) {
SCurve *sc = shell->curve.FindById(hsc);
if(backwards) {
stb.finish = sc->pts.elem[0];
stb.start = sc->pts.elem[sc->pts.n - 1];
stb.finish = sc->pts.elem[0].p;
stb.start = sc->pts.elem[sc->pts.n - 1].p;
stb.backwards = true;
} else {
stb.start = sc->pts.elem[0];
stb.finish = sc->pts.elem[sc->pts.n - 1];
stb.start = sc->pts.elem[0].p;
stb.finish = sc->pts.elem[sc->pts.n - 1].p;
stb.backwards = false;
}

View File

@ -199,6 +199,20 @@ void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) {
}
}
void SBezier::MakePwlInto(List<SCurvePt> *l) {
List<Vector> lv;
ZERO(&lv);
MakePwlInto(&lv);
int i;
for(i = 0; i < lv.n; i++) {
SCurvePt scpt;
scpt.tag = 0;
scpt.p = lv.elem[i];
scpt.vertex = (i == 0) || (i == (lv.n - 1));
l->Add(&scpt);
}
lv.Clear();
}
void SBezier::MakePwlInto(List<Vector> *l) {
l->Add(&(ctrl[0]));
MakePwlWorker(l, 0.0, 1.0);

View File

@ -202,7 +202,7 @@ void SSurface::MakeTrimEdgesInto(SEdgeList *sel, bool asUv,
increment = 1;
}
for(i = first; i != (last + increment); i += increment) {
Vector *pt = &(sc->pts.elem[i]);
Vector *pt = &(sc->pts.elem[i].p);
if(asUv) {
ClosestPointTo(*pt, &u, &v);
ptuv = Vector::From(u, v, 0);

View File

@ -7,6 +7,7 @@ double Bernstein(int k, int deg, double t);
double BernsteinDerivative(int k, int deg, double t);
class SSurface;
class SCurvePt;
// Utility data structure, a two-dimensional BSP to accelerate polygon
// operations.
@ -67,6 +68,7 @@ public:
Vector Start(void);
Vector Finish(void);
bool Equals(SBezier *b);
void MakePwlInto(List<SCurvePt> *l);
void MakePwlInto(List<Vector> *l);
void MakePwlWorker(List<Vector> *l, double ta, double tb);
@ -123,6 +125,13 @@ public:
};
// Stuff for the surface trim curves: piecewise linear
class SCurvePt {
public:
int tag;
Vector p;
bool vertex;
};
class SCurve {
public:
hSCurve h;
@ -139,7 +148,7 @@ public:
bool isExact;
SBezier exact;
List<Vector> pts;
List<SCurvePt> pts;
hSSurface surfA;
hSSurface surfB;
@ -147,6 +156,7 @@ public:
static SCurve FromTransformationOf(SCurve *a, Vector t, Quaternion q);
SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
SSurface *srfA, SSurface *srfB);
void RemoveShortSegments(SSurface *srfA, SSurface *srfB);
void Clear(void);
};

View File

@ -35,7 +35,7 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
}
}
if(existing) {
Vector *v;
SCurvePt *v;
for(v = existing->pts.First(); v; v = existing->pts.NextAfter(v)) {
sc.pts.Add(v);
}
@ -51,11 +51,11 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
if(0 && sb->deg == 1) {
dbp(" ");
Vector *prev = NULL, *v;
SCurvePt *prev = NULL, *v;
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, *v);
SS.nakedEdges.AddEdge(prev->p, v->p);
}
prev = v;
}
@ -79,6 +79,11 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
return;
}
Vector alongt, alongb;
SBezier oft, ofb;
bool isExtdt = this->IsExtrusion(&oft, &alongt),
isExtdb = b->IsExtrusion(&ofb, &alongb);
if(degm == 1 && degn == 1 && b->degm == 1 && b->degn == 1) {
// Line-line intersection; it's a plane or nothing.
Vector na = NormalAt(0, 0).WithMagnitude(1),
@ -139,8 +144,8 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
p.Plus(dl.ScaledBy(tmax)));
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
}
} else if((degm == 1 && degn == 1 && b->IsExtrusion(NULL, NULL)) ||
(b->degm == 1 && b->degn == 1 && this->IsExtrusion(NULL, NULL)))
} else if((degm == 1 && degn == 1 && isExtdb) ||
(b->degm == 1 && b->degn == 1 && isExtdt))
{
// The intersection between a plane and a surface of extrusion
SSurface *splane, *sext;
@ -202,6 +207,55 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
}
} else if(isExtdt && isExtdb &&
sqrt(fabs(alongt.Dot(alongb))) >
sqrt(alongt.Magnitude() * alongb.Magnitude()) - LENGTH_EPS)
{
// Two surfaces of extrusion along the same axis. So they might
// intersect along some number of lines parallel to the axis.
Vector axis = alongt.WithMagnitude(1);
List<SInter> inters;
ZERO(&inters);
List<Vector> lv;
ZERO(&lv);
Vector axisa = axis.ScaledBy((b->ctrl[0][0]).Dot(axis)),
axisb = axis.ScaledBy((b->ctrl[0][1]).Dot(axis)),
axisc = (axisa.Plus(axisb)).ScaledBy(0.5);
oft.MakePwlInto(&lv);
int i;
for(i = 0; i < lv.n - 1; i++) {
Vector pa = lv.elem[i], pb = lv.elem[i+1];
pa = pa.Minus(axis.ScaledBy(pa.Dot(axis)));
pb = pb.Minus(axis.ScaledBy(pb.Dot(axis)));
pa = pa.Plus(axisc);
pb = pb.Plus(axisc);
b->AllPointsIntersecting(pa, pb, &inters, true, false, false);
}
SInter *si;
for(si = inters.First(); si; si = inters.NextAfter(si)) {
Vector p = (si->p).Minus(axis.ScaledBy((si->p).Dot(axis)));
double ub, vb;
b->ClosestPointTo(p, &ub, &vb, true);
SSurface plane;
plane = SSurface::FromPlane(p, axis.Normal(0), axis.Normal(1));
b->PointOnSurfaces(this, &plane, &ub, &vb);
p = b->PointAt(ub, vb);
SBezier bezier;
bezier = SBezier::From(p.Plus(axisa), p.Plus(axisb));
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
}
inters.Clear();
lv.Clear();
}
// need to implement general numerical surface intersection for tough
@ -632,6 +686,7 @@ int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) {
if(d < dmin) {
dmin = d;
if(d < LENGTH_EPS) {
// Edge-on-face (unless edge-on-edge above supercedes)
double dot = (si->surfNormal).Dot(edge_n);

View File

@ -1,8 +1,7 @@
marching algorithm for surface intersection
boundary avoidance when casting ray for point-in-shell
tangent intersections
short pwl edge avoidance
surfaces with coincident control points
assembly
-----