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]
This commit is contained in:
parent
7cf3a06274
commit
6d7954e167
26
polygon.cpp
26
polygon.cpp
|
@ -187,9 +187,19 @@ bool SEdgeList::AnyEdgeCrosses(Vector a, Vector b) {
|
||||||
&t, &tse);
|
&t, &tse);
|
||||||
if(skew) continue;
|
if(skew) continue;
|
||||||
|
|
||||||
if(t > t_eps && t < (1 - t_eps) &&
|
bool inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
|
||||||
tse > tse_eps && tse < (1 - tse_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;
|
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 SContour::ComputeNormal(void) {
|
||||||
Vector n = Vector::From(0, 0, 0);
|
Vector n = Vector::From(0, 0, 0);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ class SContour {
|
||||||
public:
|
public:
|
||||||
int tag;
|
int tag;
|
||||||
int timesEnclosed;
|
int timesEnclosed;
|
||||||
|
Vector xminPt;
|
||||||
List<SPoint> l;
|
List<SPoint> l;
|
||||||
|
|
||||||
void AddPoint(Vector p);
|
void AddPoint(Vector p);
|
||||||
|
@ -54,6 +55,7 @@ public:
|
||||||
bool AllPointsInPlane(Vector n, double d, Vector *notCoplanarAt);
|
bool AllPointsInPlane(Vector n, double d, Vector *notCoplanarAt);
|
||||||
void OffsetInto(SContour *dest, double r);
|
void OffsetInto(SContour *dest, double r);
|
||||||
void CopyInto(SContour *dest);
|
void CopyInto(SContour *dest);
|
||||||
|
void FindPointWithMinX(void);
|
||||||
|
|
||||||
bool IsEar(int bp);
|
bool IsEar(int bp);
|
||||||
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
||||||
|
|
|
@ -559,7 +559,7 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
|
||||||
STrimBy stb1 = STrimBy::EntireCurve(&ret, hc1);
|
STrimBy stb1 = STrimBy::EntireCurve(&ret, hc1);
|
||||||
|
|
||||||
// The translated curves trim the flat top and bottom surfaces.
|
// 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);
|
(ret.surface.FindById(hs1))->trim.Add(&stb1);
|
||||||
|
|
||||||
// The translated curves also trim the surface of extrusion.
|
// The translated curves also trim the surface of extrusion.
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
void SPolygon::UvTriangulateInto(SMesh *m) {
|
void SPolygon::UvTriangulateInto(SMesh *m) {
|
||||||
if(l.n <= 0) return;
|
if(l.n <= 0) return;
|
||||||
|
|
||||||
|
SDWORD in = GetMilliseconds();
|
||||||
|
|
||||||
normal = Vector::From(0, 0, 1);
|
normal = Vector::From(0, 0, 1);
|
||||||
|
|
||||||
while(l.n > 0) {
|
while(l.n > 0) {
|
||||||
|
@ -45,34 +47,36 @@ void SPolygon::UvTriangulateInto(SMesh *m) {
|
||||||
if(top->ContainsPointProjdToNormal(normal, sc->l.elem[0].p)) {
|
if(top->ContainsPointProjdToNormal(normal, sc->l.elem[0].p)) {
|
||||||
sc->tag = 2;
|
sc->tag = 2;
|
||||||
sc->MakeEdgesInto(&el);
|
sc->MakeEdgesInto(&el);
|
||||||
|
sc->FindPointWithMinX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool holesExist, mergedHole;
|
dbp("finished finding holes: %d ms", GetMilliseconds() - in);
|
||||||
do {
|
for(;;) {
|
||||||
holesExist = false;
|
double xmin = 1e10;
|
||||||
mergedHole = false;
|
SContour *scmin = NULL;
|
||||||
|
|
||||||
for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
|
for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
|
||||||
if(sc->tag != 2) continue;
|
if(sc->tag != 2) continue;
|
||||||
|
|
||||||
holesExist = true;
|
if(sc->xminPt.x < xmin) {
|
||||||
if(merged.BridgeToContour(sc, &el, &vl)) {
|
xmin = sc->xminPt.x;
|
||||||
// Merged it in, so we're done with it.
|
scmin = sc;
|
||||||
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(holesExist && !mergedHole) {
|
if(!scmin) break;
|
||||||
dbp("holes exist, yet couldn't merge one");
|
|
||||||
|
if(!merged.BridgeToContour(scmin, &el, &vl)) {
|
||||||
|
dbp("couldn't merge our hole");
|
||||||
return;
|
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);
|
merged.UvTriangulateInto(m);
|
||||||
|
dbp("finished ear clippping: %d ms", GetMilliseconds() - in);
|
||||||
merged.l.Clear();
|
merged.l.Clear();
|
||||||
el.Clear();
|
el.Clear();
|
||||||
vl.Clear();
|
vl.Clear();
|
||||||
|
@ -84,10 +88,35 @@ void SPolygon::UvTriangulateInto(SMesh *m) {
|
||||||
bool SContour::BridgeToContour(SContour *sc,
|
bool SContour::BridgeToContour(SContour *sc,
|
||||||
SEdgeList *avoidEdges, List<Vector> *avoidPts)
|
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;
|
int thisp, scp;
|
||||||
|
|
||||||
Vector a, b, *f;
|
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;
|
a = l.elem[thisp].p;
|
||||||
|
|
||||||
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
|
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
|
||||||
|
@ -95,7 +124,8 @@ bool SContour::BridgeToContour(SContour *sc,
|
||||||
}
|
}
|
||||||
if(f) continue;
|
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;
|
b = sc->l.elem[scp].p;
|
||||||
|
|
||||||
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
|
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
|
||||||
|
@ -116,7 +146,6 @@ bool SContour::BridgeToContour(SContour *sc,
|
||||||
haveEdge:
|
haveEdge:
|
||||||
SContour merged;
|
SContour merged;
|
||||||
ZERO(&merged);
|
ZERO(&merged);
|
||||||
int i, j;
|
|
||||||
for(i = 0; i < l.n; i++) {
|
for(i = 0; i < l.n; i++) {
|
||||||
merged.AddPoint(l.elem[i].p);
|
merged.AddPoint(l.elem[i].p);
|
||||||
if(i == thisp) {
|
if(i == thisp) {
|
||||||
|
@ -207,26 +236,36 @@ void SContour::ClipEarInto(SMesh *m, int bp) {
|
||||||
|
|
||||||
void SContour::UvTriangulateInto(SMesh *m) {
|
void SContour::UvTriangulateInto(SMesh *m) {
|
||||||
int i;
|
int i;
|
||||||
|
// First, calculate the ear-ness of all the points
|
||||||
for(i = 0; i < l.n; i++) {
|
for(i = 0; i < l.n; i++) {
|
||||||
(l.elem[i]).ear = IsEar(i) ? SPoint::EAR : SPoint::NOT_EAR;
|
(l.elem[i]).ear = IsEar(i) ? SPoint::EAR : SPoint::NOT_EAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool toggle = false;
|
||||||
while(l.n > 3) {
|
while(l.n > 3) {
|
||||||
|
// Some points may have changed ear-ness, so recalculate
|
||||||
for(i = 0; i < l.n; i++) {
|
for(i = 0; i < l.n; i++) {
|
||||||
if(l.elem[i].ear == SPoint::UNKNOWN) {
|
if(l.elem[i].ear == SPoint::UNKNOWN) {
|
||||||
(l.elem[i]).ear = IsEar(i) ? SPoint::EAR : SPoint::NOT_EAR;
|
(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++) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(i >= l.n) {
|
if(ear < 0) {
|
||||||
dbp("couldn't find an ear! fail");
|
dbp("couldn't find an ear! fail");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClipEarInto(m, i);
|
ClipEarInto(m, ear);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClipEarInto(m, 0); // add the last triangle
|
ClipEarInto(m, 0); // add the last triangle
|
||||||
|
|
Loading…
Reference in New Issue
Block a user