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]
solver
Jonathan Westhues 2009-01-25 01:19:59 -08:00
parent bb4b767e99
commit 2e4ec6dd04
14 changed files with 136 additions and 92 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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 {

View File

@ -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);
}

View File

@ -558,6 +558,7 @@ void GraphicsWindow::MenuEdit(int id) {
}
SS.GW.ClearSuper();
HideTextEditControl();
SS.nakedEdges.Clear();
break;
case MNU_DELETE: {

View File

@ -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);
}

View File

@ -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++;
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -471,6 +471,7 @@ public:
SContour path;
hEntity point;
} traced;
SEdgeList nakedEdges;
void MarkGroupDirty(hGroup hg);
void MarkGroupDirtyByEntity(hEntity he);

View File

@ -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<TrimLine> 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();
}

View File

@ -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<SCurve,hSCurve> curve;
IdList<SSurface,hSSurface> 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);

View File

@ -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();