Fix an issue with edge intersection testing: if a vertex from edge
A touches edge B, but does not share a vertex with edge B, then that's an intersection. Adjust the ear clipping so that it generates strip-like triangulations, not fan-like. And rearrange deck chairs on the bridge-finding code, which is still pathetically slow. It may not be possible to get reasonable performance without kd tree type acceleration. [git-p4: depot-paths = "//depot/solvespace/": change = 1901]solver
parent
7cf3a06274
commit
6d7954e167
26
polygon.cpp
26
polygon.cpp
|
@ -187,9 +187,19 @@ bool SEdgeList::AnyEdgeCrosses(Vector a, Vector b) {
|
|||
&t, &tse);
|
||||
if(skew) continue;
|
||||
|
||||
if(t > t_eps && t < (1 - t_eps) &&
|
||||
tse > tse_eps && tse < (1 - tse_eps))
|
||||
{
|
||||
bool inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
|
||||
bool inOrEdge1 = (tse > -tse_eps) && (tse < (1 + tse_eps));
|
||||
|
||||
if(inOrEdge0 && inOrEdge1) {
|
||||
if((se->a).Equals(a) || (se->b).Equals(a) ||
|
||||
(se->a).Equals(b) || (se->b).Equals(b))
|
||||
{
|
||||
// Not an intersection if we share an endpoint with an edge
|
||||
continue;
|
||||
}
|
||||
// But it's an intersection if a vertex of one edge lies on the
|
||||
// inside of the other (or if they cross away from either's
|
||||
// vertex).
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -222,6 +232,16 @@ void SContour::CopyInto(SContour *dest) {
|
|||
}
|
||||
}
|
||||
|
||||
void SContour::FindPointWithMinX(void) {
|
||||
SPoint *sp;
|
||||
xminPt = Vector::From(1e10, 1e10, 1e10);
|
||||
for(sp = l.First(); sp; sp = l.NextAfter(sp)) {
|
||||
if(sp->p.x < xminPt.x) {
|
||||
xminPt = sp->p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector SContour::ComputeNormal(void) {
|
||||
Vector n = Vector::From(0, 0, 0);
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ class SContour {
|
|||
public:
|
||||
int tag;
|
||||
int timesEnclosed;
|
||||
Vector xminPt;
|
||||
List<SPoint> l;
|
||||
|
||||
void AddPoint(Vector p);
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
bool AllPointsInPlane(Vector n, double d, Vector *notCoplanarAt);
|
||||
void OffsetInto(SContour *dest, double r);
|
||||
void CopyInto(SContour *dest);
|
||||
void FindPointWithMinX(void);
|
||||
|
||||
bool IsEar(int bp);
|
||||
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
||||
|
|
|
@ -499,7 +499,7 @@ void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
|
|||
st->FlipNormal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
el.Clear();
|
||||
poly.Clear();
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
|
|||
STrimBy stb1 = STrimBy::EntireCurve(&ret, hc1);
|
||||
|
||||
// The translated curves trim the flat top and bottom surfaces.
|
||||
(ret.surface.FindById(hs0))->trim.Add(&stb0);
|
||||
// (ret.surface.FindById(hs0))->trim.Add(&stb0);
|
||||
(ret.surface.FindById(hs1))->trim.Add(&stb1);
|
||||
|
||||
// The translated curves also trim the surface of extrusion.
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
void SPolygon::UvTriangulateInto(SMesh *m) {
|
||||
if(l.n <= 0) return;
|
||||
|
||||
SDWORD in = GetMilliseconds();
|
||||
|
||||
normal = Vector::From(0, 0, 1);
|
||||
|
||||
while(l.n > 0) {
|
||||
|
@ -45,34 +47,36 @@ void SPolygon::UvTriangulateInto(SMesh *m) {
|
|||
if(top->ContainsPointProjdToNormal(normal, sc->l.elem[0].p)) {
|
||||
sc->tag = 2;
|
||||
sc->MakeEdgesInto(&el);
|
||||
sc->FindPointWithMinX();
|
||||
}
|
||||
}
|
||||
|
||||
bool holesExist, mergedHole;
|
||||
do {
|
||||
holesExist = false;
|
||||
mergedHole = false;
|
||||
dbp("finished finding holes: %d ms", GetMilliseconds() - in);
|
||||
for(;;) {
|
||||
double xmin = 1e10;
|
||||
SContour *scmin = NULL;
|
||||
|
||||
for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
|
||||
if(sc->tag != 2) continue;
|
||||
|
||||
holesExist = true;
|
||||
if(merged.BridgeToContour(sc, &el, &vl)) {
|
||||
// Merged it in, so we're done with it.
|
||||
sc->tag = 3;
|
||||
mergedHole = true;
|
||||
} else {
|
||||
// Looks like we can't merge it yet, but that can happen;
|
||||
// we may have to merge the holes in a specific order.
|
||||
if(sc->xminPt.x < xmin) {
|
||||
xmin = sc->xminPt.x;
|
||||
scmin = sc;
|
||||
}
|
||||
}
|
||||
if(holesExist && !mergedHole) {
|
||||
dbp("holes exist, yet couldn't merge one");
|
||||
if(!scmin) break;
|
||||
|
||||
if(!merged.BridgeToContour(scmin, &el, &vl)) {
|
||||
dbp("couldn't merge our hole");
|
||||
return;
|
||||
}
|
||||
} while(holesExist);
|
||||
dbp(" bridged to contour: %d ms", GetMilliseconds() - in);
|
||||
scmin->tag = 3;
|
||||
}
|
||||
dbp("finished merging holes: %d ms", GetMilliseconds() - in);
|
||||
|
||||
merged.UvTriangulateInto(m);
|
||||
dbp("finished ear clippping: %d ms", GetMilliseconds() - in);
|
||||
merged.l.Clear();
|
||||
el.Clear();
|
||||
vl.Clear();
|
||||
|
@ -84,10 +88,35 @@ void SPolygon::UvTriangulateInto(SMesh *m) {
|
|||
bool SContour::BridgeToContour(SContour *sc,
|
||||
SEdgeList *avoidEdges, List<Vector> *avoidPts)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
// Start looking for a bridge on our new hole near its leftmost (min x)
|
||||
// point.
|
||||
int sco = 0;
|
||||
for(i = 0; i < (sc->l.n - 1); i++) {
|
||||
if((sc->l.elem[i].p).EqualsExactly(sc->xminPt)) {
|
||||
sco = i;
|
||||
}
|
||||
}
|
||||
|
||||
// And start looking on our merged contour at whichever point is nearest
|
||||
// to the leftmost point of the new segment.
|
||||
int thiso = 0;
|
||||
double dmin = 1e10;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
Vector p = l.elem[i].p;
|
||||
double d = (p.Minus(sc->xminPt)).MagSquared();
|
||||
if(d < dmin) {
|
||||
dmin = d;
|
||||
thiso = i;
|
||||
}
|
||||
}
|
||||
|
||||
int thisp, scp;
|
||||
|
||||
Vector a, b, *f;
|
||||
for(thisp = 0; thisp < l.n; thisp++) {
|
||||
for(i = 0; i < l.n; i++) {
|
||||
thisp = WRAP(i+thiso, l.n);
|
||||
a = l.elem[thisp].p;
|
||||
|
||||
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
|
||||
|
@ -95,7 +124,8 @@ bool SContour::BridgeToContour(SContour *sc,
|
|||
}
|
||||
if(f) continue;
|
||||
|
||||
for(scp = 0; scp < (sc->l.n - 1); scp++) {
|
||||
for(j = 0; j < (sc->l.n - 1); j++) {
|
||||
scp = WRAP(j+sco, (sc->l.n - 1));
|
||||
b = sc->l.elem[scp].p;
|
||||
|
||||
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
|
||||
|
@ -116,7 +146,6 @@ bool SContour::BridgeToContour(SContour *sc,
|
|||
haveEdge:
|
||||
SContour merged;
|
||||
ZERO(&merged);
|
||||
int i, j;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
merged.AddPoint(l.elem[i].p);
|
||||
if(i == thisp) {
|
||||
|
@ -187,7 +216,7 @@ bool SContour::IsEar(int bp) {
|
|||
void SContour::ClipEarInto(SMesh *m, int bp) {
|
||||
int ap = WRAP(bp-1, l.n),
|
||||
cp = WRAP(bp+1, l.n);
|
||||
|
||||
|
||||
STriangle tr;
|
||||
ZERO(&tr);
|
||||
tr.a = l.elem[ap].p;
|
||||
|
@ -207,26 +236,36 @@ void SContour::ClipEarInto(SMesh *m, int bp) {
|
|||
|
||||
void SContour::UvTriangulateInto(SMesh *m) {
|
||||
int i;
|
||||
// First, calculate the ear-ness of all the points
|
||||
for(i = 0; i < l.n; i++) {
|
||||
(l.elem[i]).ear = IsEar(i) ? SPoint::EAR : SPoint::NOT_EAR;
|
||||
}
|
||||
|
||||
bool toggle = false;
|
||||
while(l.n > 3) {
|
||||
// Some points may have changed ear-ness, so recalculate
|
||||
for(i = 0; i < l.n; i++) {
|
||||
if(l.elem[i].ear == SPoint::UNKNOWN) {
|
||||
(l.elem[i]).ear = IsEar(i) ? SPoint::EAR : SPoint::NOT_EAR;
|
||||
}
|
||||
}
|
||||
|
||||
// And find a candidate ear; alternate the starting position so
|
||||
// we generate strip-like triangulations instead of fan-like
|
||||
int ear = -1;
|
||||
toggle = !toggle;
|
||||
int offset = toggle ? -1 : 0;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
if(l.elem[i].ear == SPoint::EAR) {
|
||||
ear = WRAP(i+offset, l.n);
|
||||
if(l.elem[ear].ear == SPoint::EAR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i >= l.n) {
|
||||
if(ear < 0) {
|
||||
dbp("couldn't find an ear! fail");
|
||||
return;
|
||||
}
|
||||
ClipEarInto(m, i);
|
||||
ClipEarInto(m, ear);
|
||||
}
|
||||
|
||||
ClipEarInto(m, 0); // add the last triangle
|
||||
|
|
Loading…
Reference in New Issue