From 2e4ec6dd04b4f0603437031dcc60b63bd39c47a9 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sun, 25 Jan 2009 01:19:59 -0800 Subject: [PATCH] Add sin and cos to the expression entry (for dimensions etc.), with the same precedence as sqrt. Add the code to find naked edges, and draw them highlighted on the model. And make the direction of trim curves consistent, always ccw with normal toward viewer; so there's no need to fix the directions before triangulating. [git-p4: depot-paths = "//depot/solvespace/": change = 1903] --- draw.cpp | 11 ++++++++ export.cpp | 2 +- expr.cpp | 12 ++++++++- glhelper.cpp | 9 ++----- graphicswin.cpp | 1 + groupmesh.cpp | 8 ++++-- mesh.cpp | 58 +++++++++------------------------------ polygon.cpp | 14 ++++++---- polygon.h | 9 +++---- solvespace.cpp | 16 +++++++++++ solvespace.h | 1 + srf/ratpoly.cpp | 66 ++++++++++++++++++++++++++++++++------------- srf/surface.h | 13 ++++++--- srf/triangulate.cpp | 8 +++--- 14 files changed, 136 insertions(+), 92 deletions(-) diff --git a/draw.cpp b/draw.cpp index 54f24380..40bfaf56 100644 --- a/draw.cpp +++ b/draw.cpp @@ -1021,6 +1021,17 @@ void GraphicsWindow::Paint(int w, int h) { } glEnd(); + // And the naked edges, if the user did Analyze -> Show Naked Edges. + glLineWidth(7); + glEnable(GL_LINE_STIPPLE); + glLineStipple(4, 0x5555); + glColor3d(1, 0, 0); + glxDrawEdges(&(SS.nakedEdges)); + glLineStipple(4, 0xaaaa); + glColor3d(0, 0, 0); + glxDrawEdges(&(SS.nakedEdges)); + glDisable(GL_LINE_STIPPLE); + // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); glxLockColorTo(1, 1, 0); diff --git a/export.cpp b/export.cpp index 4b12c1e4..39525697 100644 --- a/export.cpp +++ b/export.cpp @@ -88,7 +88,7 @@ void SolveSpace::ExportSectionTo(char *filename) { SKdNode *root = SKdNode::From(&m); SEdgeList el; ZERO(&el); - root->MakeCertainEdgesInto(&el, false); + root->MakeNakedEdgesInto(&el); // Assemble those edges into a polygon, and clear the edge list el.AssemblePolygon(&sp, NULL); el.Clear(); diff --git a/expr.cpp b/expr.cpp index 52544266..cf750586 100644 --- a/expr.cpp +++ b/expr.cpp @@ -598,7 +598,9 @@ int Expr::Precedence(Expr *e) { if(e->op != BINARY_OP && e->op != UNARY_OP) oops(); switch(e->x.c) { + case 'q': case 's': + case 'c': case 'n': return 30; case '*': @@ -629,7 +631,9 @@ c: break; case 'n': n = PopOperand()->Negate(); break; - case 's': n = PopOperand()->Sqrt(); break; + case 'q': n = PopOperand()->Sqrt(); break; + case 's': n = (PopOperand()->Times(Expr::From(PI/180)))->Sin(); break; + case 'c': n = (PopOperand()->Times(Expr::From(PI/180)))->Cos(); break; default: oops(); } @@ -721,6 +725,12 @@ void Expr::Lex(char *in) { Expr *e = AllocExpr(); if(strcmp(name, "sqrt")==0) { + e->op = UNARY_OP; + e->x.c = 'q'; + } else if(strcmp(name, "cos")==0) { + e->op = UNARY_OP; + e->x.c = 'c'; + } else if(strcmp(name, "sin")==0) { e->op = UNARY_OP; e->x.c = 's'; } else { diff --git a/glhelper.cpp b/glhelper.cpp index aa331fbc..2936ffb7 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -295,14 +295,9 @@ void glxDebugPolygon(SPolygon *p) void glxDrawEdges(SEdgeList *el) { - int i; - glLineWidth(1); - glxDepthRangeOffset(2); - glxColor3d(REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor)); - + SEdge *se; glBegin(GL_LINES); - for(i = 0; i < el->l.n; i++) { - SEdge *se = &(el->l.elem[i]); + for(se = el->l.First(); se; se = el->l.NextAfter(se)) { glxVertex3v(se->a); glxVertex3v(se->b); } diff --git a/graphicswin.cpp b/graphicswin.cpp index f3a1391b..a3f9e473 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -558,6 +558,7 @@ void GraphicsWindow::MenuEdit(int id) { } SS.GW.ClearSuper(); HideTextEditControl(); + SS.nakedEdges.Clear(); break; case MNU_DELETE: { diff --git a/groupmesh.cpp b/groupmesh.cpp index 8d7f442a..472d49aa 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -87,7 +87,6 @@ void Group::GenerateShellForStepAndRepeat(void) { void Group::GenerateShellAndMesh(void) { thisShell.Clear(); - STriMeta meta = { 0, color }; if(type == TRANSLATE || type == ROTATE) { GenerateShellForStepAndRepeat(); @@ -105,7 +104,7 @@ void Group::GenerateShellAndMesh(void) { tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1); } - thisShell.MakeFromExtrusionOf(&(src->bezierLoopSet), tbot, ttop); + thisShell.MakeFromExtrusionOf(&(src->bezierLoopSet), tbot, ttop, color); } else if(type == LATHE) { Group *src = SS.GetGroup(opA); @@ -223,6 +222,11 @@ void Group::Draw(void) { glxFillMesh(specColor, &runningMesh, mh, ms1, ms2); glDisable(GL_LIGHTING); + glLineWidth(1); + glxDepthRangeOffset(2); + glxColor3d(REDf (SS.edgeColor), + GREENf(SS.edgeColor), + BLUEf (SS.edgeColor)); glxDrawEdges(&emphEdges); } diff --git a/mesh.cpp b/mesh.cpp index e39bb8bc..cea98473 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -476,21 +476,19 @@ void SKdNode::MakeMeshInto(SMesh *m) { } } -void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int *nOther, - STriMeta m, int cnt) -{ +void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt) { if(gt && lt) { double ac = a.Element(which), bc = b.Element(which); if(ac < c + KDTREE_EPS || bc < c + KDTREE_EPS) { - lt->FindEdgeOn(a, b, n, nOther, m, cnt); + lt->FindEdgeOn(a, b, n, cnt); } if(ac > c - KDTREE_EPS || bc > c - KDTREE_EPS) { - gt->FindEdgeOn(a, b, n, nOther, m, cnt); + gt->FindEdgeOn(a, b, n, cnt); } } else { STriangleLl *ll; @@ -499,43 +497,20 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int *nOther, if(tr->tag == cnt) continue; - if((a.Equals(tr->b, KDTREE_EPS) && b.Equals(tr->a, KDTREE_EPS)) || - (a.Equals(tr->c, KDTREE_EPS) && b.Equals(tr->b, KDTREE_EPS)) || - (a.Equals(tr->a, KDTREE_EPS) && b.Equals(tr->c, KDTREE_EPS))) + if((a.Equals(tr->b) && b.Equals(tr->a)) || + (a.Equals(tr->c) && b.Equals(tr->b)) || + (a.Equals(tr->a) && b.Equals(tr->c))) { (*n)++; - if(tr->meta.face != m.face) { - if(tr->meta.color == m.color && - tr->meta.face != 0 && m.face != 0) - { - hEntity hf0 = { tr->meta.face }, - hf1 = { m.face }; - Entity *f0 = SS.GetEntity(hf0), - *f1 = SS.GetEntity(hf1); - - Vector n0 = f0->FaceGetNormalNum().WithMagnitude(1), - n1 = f1->FaceGetNormalNum().WithMagnitude(1); - - if(n0.Equals(n1) || n0.Equals(n1.ScaledBy(-1))) { - // faces are coincident, skip - // (If the planes are parallel, and the edge - // lies in both planes, then they're also - // coincident.) - } else { - (*nOther)++; - } - } else { - (*nOther)++; - } - } } - + // Ensure that we don't count this triangle twice if it appears + // in two buckets of the kd tree. tr->tag = cnt; } } } -void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, bool emphasized) { +void SKdNode::MakeNakedEdgesInto(SEdgeList *sel) { SMesh m; ZERO(&m); ClearTags(); @@ -551,20 +526,11 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, bool emphasized) { Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a); int n = 0, nOther = 0; - FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++); + FindEdgeOn(a, b, &n, cnt); if(n != 1) { - if(!emphasized) { - if(n == 0 && (a.Minus(b).Magnitude()) > KDTREE_EPS) { - sel->AddEdge(a, b); - } - } else { -// dbp("hanging: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)", -// n, CO(a), CO(b)); - } - } - if(nOther > 0) { - if(emphasized) sel->AddEdge(a, b); + sel->AddEdge(a, b); } + cnt++; } } diff --git a/polygon.cpp b/polygon.cpp index aefafaa0..f6974bc0 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -68,8 +68,8 @@ void SEdgeList::AddEdge(Vector a, Vector b) { l.Add(&e); } -bool SEdgeList::AssembleContour(Vector first, Vector last, - SContour *dest, SEdge *errorAt) +bool SEdgeList::AssembleContour(Vector first, Vector last, SContour *dest, + SEdge *errorAt, bool keepDir) { int i; @@ -87,7 +87,8 @@ bool SEdgeList::AssembleContour(Vector first, Vector last, se->tag = 1; break; } - if(se->b.Equals(last)) { + // Don't allow backwards edges if keepDir is true. + if(!keepDir && se->b.Equals(last)) { dest->AddPoint(se->a); last = se->a; se->tag = 1; @@ -108,7 +109,7 @@ bool SEdgeList::AssembleContour(Vector first, Vector last, return true; } -bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt) { +bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) { dest->Clear(); bool allClosed = true; @@ -130,8 +131,11 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt) { // Create a new empty contour in our polygon, and finish assembling // into that contour. dest->AddEmptyContour(); - if(!AssembleContour(first, last, &(dest->l.elem[dest->l.n-1]), errorAt)) + if(!AssembleContour(first, last, &(dest->l.elem[dest->l.n-1]), + errorAt, keepDir)) + { allClosed = false; + } // But continue assembling, even if some of the contours are open } } diff --git a/polygon.h b/polygon.h index 25e05642..dee5b5cb 100644 --- a/polygon.h +++ b/polygon.h @@ -21,9 +21,9 @@ public: void Clear(void); void AddEdge(Vector a, Vector b); - bool AssemblePolygon(SPolygon *dest, SEdge *errorAt); + bool AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir=false); bool AssembleContour(Vector first, Vector last, SContour *dest, - SEdge *errorAt); + SEdge *errorAt, bool keepDir); int AnyEdgeCrossings(Vector a, Vector b, Vector *pi=NULL); }; @@ -218,9 +218,8 @@ public: void MakeMeshInto(SMesh *m); void ClearTags(void); - void FindEdgeOn(Vector a, Vector b, int *n, int *nOther, - STriMeta m, int cnt); - void MakeCertainEdgesInto(SEdgeList *sel, bool emphasized); + void FindEdgeOn(Vector a, Vector b, int *n, int cnt); + void MakeNakedEdgesInto(SEdgeList *sel); }; #endif diff --git a/solvespace.cpp b/solvespace.cpp index 9c3f5c81..803a9f85 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -183,6 +183,8 @@ void SolveSpace::AfterNewFile(void) { // Clear out the traced point, which is no longer valid traced.point = Entity::NO_ENTITY; traced.path.l.Clear(); + // and the naked edges + nakedEdges.Clear(); ReloadAllImported(); GenerateAll(-1, -1); @@ -400,6 +402,20 @@ void SolveSpace::MenuAnalyze(int id) { break; case GraphicsWindow::MNU_NAKED_EDGES: { + SS.nakedEdges.Clear(); + + SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh); + SKdNode *root = SKdNode::From(m); + root->MakeNakedEdgesInto(&(SS.nakedEdges)); + InvalidateGraphics(); + + if(SS.nakedEdges.l.n == 0) { + Error("Zero naked edges; the model is watertight. " + "An exported STL file will be valid."); + } else { + Error("Found %d naked edges, now highlighted.", + SS.nakedEdges.l.n); + } break; } diff --git a/solvespace.h b/solvespace.h index 4ad632e7..9bd141e3 100644 --- a/solvespace.h +++ b/solvespace.h @@ -471,6 +471,7 @@ public: SContour path; hEntity point; } traced; + SEdgeList nakedEdges; void MarkGroupDirty(hGroup hg); void MarkGroupDirtyByEntity(hEntity he); diff --git a/srf/ratpoly.cpp b/srf/ratpoly.cpp index 37a769ff..d134de2a 100644 --- a/srf/ratpoly.cpp +++ b/srf/ratpoly.cpp @@ -320,13 +320,21 @@ void SCurve::Clear(void) { pts.Clear(); } -STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc) { +STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) { STrimBy stb; ZERO(&stb); stb.curve = hsc; SCurve *sc = shell->curve.FindById(hsc); - stb.start = sc->pts.elem[0]; - stb.finish = sc->pts.elem[sc->pts.n - 1]; + + if(backwards) { + stb.finish = sc->pts.elem[0]; + stb.start = sc->pts.elem[sc->pts.n - 1]; + stb.backwards = true; + } else { + stb.start = sc->pts.elem[0]; + stb.finish = sc->pts.elem[sc->pts.n - 1]; + stb.backwards = false; + } return stb; } @@ -377,6 +385,8 @@ SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q, ZERO(&ret); ret.h = a->h; + ret.color = a->color; + ret.face = a->face; ret.degm = a->degm; ret.degn = a->degn; @@ -511,9 +521,20 @@ void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv) { Vector prev, prevuv, ptuv; bool inCurve = false; - Vector *pt; double u = 0, v = 0; - for(pt = sc->pts.First(); pt; pt = sc->pts.NextAfter(pt)) { + + int i, first, last, increment; + if(stb->backwards) { + first = sc->pts.n - 1; + last = 0; + increment = -1; + } else { + first = 0; + last = sc->pts.n - 1; + increment = 1; + } + for(i = first; i != (last + increment); i += increment) { + Vector *pt = &(sc->pts.elem[i]); if(asUv) { ClosestPointTo(*pt, &u, &v); ptuv = Vector::From(u, v, 0); @@ -542,14 +563,14 @@ void SSurface::TriangulateInto(SShell *shell, SMesh *sm) { SPolygon poly; ZERO(&poly); - if(!el.AssemblePolygon(&poly, NULL)) { + if(!el.AssemblePolygon(&poly, NULL, true)) { dbp("failed to assemble polygon to trim nurbs surface in uv space"); } int i, start = sm->l.n; poly.UvTriangulateInto(sm); - STriMeta meta = { 0, 0x888888 }; + STriMeta meta = { face, color }; for(i = start; i < sm->l.n; i++) { STriangle *st = &(sm->l.elem[i]); st->meta = meta; @@ -559,10 +580,9 @@ void SSurface::TriangulateInto(SShell *shell, SMesh *sm) { st->a = PointAt(st->a.x, st->a.y); st->b = PointAt(st->b.x, st->b.y); st->c = PointAt(st->c.x, st->c.y); - if((st->Normal()).Dot(st->an) < 0) { - // Have to get the vertices in the right order - st->FlipNormal(); - } + // Works out that my chosen contour direction is inconsistent with + // the triangle direction, sigh. + st->FlipNormal(); } el.Clear(); @@ -573,7 +593,9 @@ void SSurface::Clear(void) { trim.Clear(); } -void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { +void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, + int color) +{ ZERO(this); // Make the extrusion direction consistent with respect to the normal @@ -586,7 +608,9 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { // planes. SSurface s0, s1; s0 = SSurface::FromPlane(sbls->point.Plus(t0), sbls->normal.ScaledBy(-1)); + s0.color = color; s1 = SSurface::FromPlane(sbls->point.Plus(t1), sbls->normal.ScaledBy( 1)); + s1.color = color; hSSurface hs0 = surface.AddAndAssignId(&s0), hs1 = surface.AddAndAssignId(&s1); @@ -598,7 +622,7 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { SBezier *sb; typedef struct { - STrimBy trim; + hSCurve hc; hSSurface hs; } TrimLine; List trimLines; @@ -608,6 +632,7 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { // Generate the surface of extrusion of this curve, and add // it to the list SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1); + ss.color = color; hSSurface hsext = surface.AddAndAssignId(&ss); // Translate the curve by t0 and t1 to produce two trim curves @@ -615,19 +640,21 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { ZERO(&sc); sb->MakePwlInto(&(sc.pts), t0); hSCurve hc0 = curve.AddAndAssignId(&sc); - STrimBy stb0 = STrimBy::EntireCurve(this, hc0); + STrimBy stb0 = STrimBy::EntireCurve(this, hc0, false); ZERO(&sc); sb->MakePwlInto(&(sc.pts), t1); hSCurve hc1 = curve.AddAndAssignId(&sc); - STrimBy stb1 = STrimBy::EntireCurve(this, hc1); + STrimBy stb1 = STrimBy::EntireCurve(this, hc1, true); // The translated curves trim the flat top and bottom surfaces. (surface.FindById(hs0))->trim.Add(&stb0); (surface.FindById(hs1))->trim.Add(&stb1); // The translated curves also trim the surface of extrusion. + stb0 = STrimBy::EntireCurve(this, hc0, true); (surface.FindById(hsext))->trim.Add(&stb0); + stb1 = STrimBy::EntireCurve(this, hc1, false); (surface.FindById(hsext))->trim.Add(&stb1); // And form the trim line @@ -639,7 +666,7 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { hSCurve hl = curve.AddAndAssignId(&sc); // save this for later TrimLine tl; - tl.trim = STrimBy::EntireCurve(this, hl); + tl.hc = hl; tl.hs = hsext; trimLines.Add(&tl); } @@ -651,8 +678,11 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]); - ss->trim.Add(&(tl->trim)); - ss->trim.Add(&(tlp->trim)); + STrimBy stb; + stb = STrimBy::EntireCurve(this, tl->hc, true); + ss->trim.Add(&stb); + stb = STrimBy::EntireCurve(this, tlp->hc, false); + ss->trim.Add(&stb); } trimLines.Clear(); } diff --git a/srf/surface.h b/srf/surface.h index 7fd4f8b5..c67a591f 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -97,11 +97,14 @@ public: class STrimBy { public: hSCurve curve; + bool backwards; + // If a trim runs backwards, then start and finish still correspond to + // the actual start and finish, but they appear in reverse order in + // the referenced curve. Vector start; Vector finish; - Vector out; - static STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc); + static STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool bkwds); }; // A rational polynomial surface in Bezier form. @@ -109,6 +112,9 @@ class SSurface { public: hSSurface h; + int color; + DWORD face; + int degm, degn; Vector ctrl[4][4]; double weight[4][4]; @@ -136,7 +142,8 @@ public: IdList curve; IdList surface; - void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1); + void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, + int color); void MakeFromUnionOf(SShell *a, SShell *b); void MakeFromDifferenceOf(SShell *a, SShell *b); void MakeFromCopyOf(SShell *a); diff --git a/srf/triangulate.cpp b/srf/triangulate.cpp index 75e586a2..6152d360 100644 --- a/srf/triangulate.cpp +++ b/srf/triangulate.cpp @@ -51,7 +51,7 @@ void SPolygon::UvTriangulateInto(SMesh *m) { } } - dbp("finished finding holes: %d ms", GetMilliseconds() - in); +// dbp("finished finding holes: %d ms", GetMilliseconds() - in); for(;;) { double xmin = 1e10; SContour *scmin = NULL; @@ -70,13 +70,13 @@ void SPolygon::UvTriangulateInto(SMesh *m) { dbp("couldn't merge our hole"); return; } - dbp(" bridged to contour: %d ms", GetMilliseconds() - in); +// dbp(" bridged to contour: %d ms", GetMilliseconds() - in); scmin->tag = 3; } - dbp("finished merging holes: %d ms", GetMilliseconds() - in); +// dbp("finished merging holes: %d ms", GetMilliseconds() - in); merged.UvTriangulateInto(m); - dbp("finished ear clippping: %d ms", GetMilliseconds() - in); +// dbp("finished ear clippping: %d ms", GetMilliseconds() - in); merged.l.Clear(); el.Clear(); vl.Clear();