From bc5b43de85cf28dba572b7b204a0052a34e3bf9b Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 23 Jun 2008 00:37:12 -0800 Subject: [PATCH] Split the BSP stuff off the mesh stuff, and the text screens themselves of the text window utility functions. [git-p4: depot-paths = "//depot/solvespace/": change = 1806] --- Makefile | 2 + bsp.cpp | 646 ++++++++++++++++++++++++++++++++++++++++++++ mesh.cpp | 644 -------------------------------------------- textscreens.cpp | 692 ++++++++++++++++++++++++++++++++++++++++++++++++ textwin.cpp | 667 ---------------------------------------------- 5 files changed, 1340 insertions(+), 1311 deletions(-) create mode 100644 bsp.cpp create mode 100644 textscreens.cpp diff --git a/Makefile b/Makefile index 81c9cc2..9fd81b9 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ W32OBJS = $(OBJDIR)\w32main.obj \ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\textwin.obj \ + $(OBJDIR)\textscreens.obj \ $(OBJDIR)\graphicswin.obj \ $(OBJDIR)\util.obj \ $(OBJDIR)\entity.obj \ @@ -30,6 +31,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\system.obj \ $(OBJDIR)\polygon.obj \ $(OBJDIR)\mesh.obj \ + $(OBJDIR)\bsp.obj \ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib \ diff --git a/bsp.cpp b/bsp.cpp new file mode 100644 index 0000000..177ea29 --- /dev/null +++ b/bsp.cpp @@ -0,0 +1,646 @@ +#include "solvespace.h" + +SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); } +SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } + +SBsp3 *SBsp3::FromMesh(SMesh *m) { + SBsp3 *bsp3 = NULL; + int i; + + SMesh mc; ZERO(&mc); + for(i = 0; i < m->l.n; i++) { + mc.AddTriangle(&(m->l.elem[i])); + } + + srand(0); // Let's be deterministic, at least! + int n = mc.l.n; + while(n > 1) { + int k = rand() % n; + n--; + SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]); + } + + for(i = 0; i < mc.l.n; i++) { + bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL); + } + + mc.Clear(); + return bsp3; +} + +Vector SBsp3::IntersectionWith(Vector a, Vector b) { + double da = a.Dot(n) - d; + double db = b.Dot(n) - d; + if(da*db > 0) oops(); + + double dab = (db - da); + return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab)); +} + +void SBsp3::InsertInPlane(bool pos2, STriangle *tr, SMesh *m) { + Vector tc = ((tr->a).Plus(tr->b).Plus(tr->c)).ScaledBy(1.0/3); + + bool onFace = false; + bool sameNormal; + double maxNormalMag = -1; + + Vector lln, trn = tr->Normal(); + + SBsp3 *ll = this; + while(ll) { + if((ll->tri).ContainsPoint(tc)) { + onFace = true; + // If the mesh contains almost-zero-area triangles, and we're + // just on the edge of one of those, then don't trust its normal. + lln = (ll->tri).Normal(); + if(lln.Magnitude() > maxNormalMag) { + sameNormal = trn.Dot(lln) > 0; + maxNormalMag = lln.Magnitude(); + } + } + ll = ll->more; + } + + if(m->flipNormal && ((!pos2 && !onFace) || + (onFace && !sameNormal && m->keepCoplanar))) + { + m->AddTriangle(tr->meta, tr->c, tr->b, tr->a); + } else if(!(m->flipNormal) && ((pos2 && !onFace) || + (onFace && sameNormal && m->keepCoplanar))) + { + m->AddTriangle(tr->meta, tr->a, tr->b, tr->c); + } else { + m->atLeastOneDiscarded = true; + } +} + +void SBsp3::InsertHow(int how, STriangle *tr, SMesh *instead) { + switch(how) { + case POS: + if(instead && !pos) goto alt; + pos = pos->Insert(tr, instead); + break; + + case NEG: + if(instead && !neg) goto alt; + neg = neg->Insert(tr, instead); + break; + + case COPLANAR: { + if(instead) goto alt; + SBsp3 *m = Alloc(); + m->n = n; + m->d = d; + m->tri = *tr; + m->more = more; + more = m; + break; + } + default: oops(); + } + return; + +alt: + if(how == POS && !(instead->flipNormal)) { + instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c); + } else if(how == NEG && instead->flipNormal) { + instead->AddTriangle(tr->meta, tr->c, tr->b, tr->a); + } else if(how == COPLANAR) { + if(edges) { + edges->InsertTriangle(tr, instead, this); + } else { + // I suppose this actually is allowed to happen, if the coplanar + // face is the leaf, and all of its neighbors are earlier in tree? + InsertInPlane(false, tr, instead); + } + } else { + instead->atLeastOneDiscarded = true; + } +} + +void SBsp3::InsertConvexHow(int how, STriMeta meta, Vector *vertex, int n, + SMesh *instead) +{ + switch(how) { + case POS: + if(pos) { + pos = pos->InsertConvex(meta, vertex, n, instead); + return; + } + break; + + case NEG: + if(neg) { + neg = neg->InsertConvex(meta, vertex, n, instead); + return; + } + break; + + default: oops(); + } + int i; + for(i = 0; i < n - 2; i++) { + STriangle tr = STriangle::From(meta, + vertex[0], vertex[i+1], vertex[i+2]); + InsertHow(how, &tr, instead); + } +} + +SBsp3 *SBsp3::InsertConvex(STriMeta meta, Vector *vertex, int cnt, + SMesh *instead) +{ + Vector e01 = (vertex[1]).Minus(vertex[0]); + Vector e12 = (vertex[2]).Minus(vertex[1]); + Vector out = e01.Cross(e12); + +#define MAX_VERTICES 50 + if(cnt+1 >= MAX_VERTICES) goto triangulate; + + int i; + Vector on[2]; + bool isPos[MAX_VERTICES]; + bool isNeg[MAX_VERTICES]; + bool isOn[MAX_VERTICES]; + int posc = 0, negc = 0, onc = 0; + for(i = 0; i < cnt; i++) { + double dt = n.Dot(vertex[i]); + isPos[i] = isNeg[i] = isOn[i] = false; + if(fabs(dt - d) < LENGTH_EPS) { + isOn[i] = true; + if(onc < 2) { + on[onc] = vertex[i]; + } + onc++; + } else if(dt > d) { + isPos[i] = true; + posc++; + } else { + isNeg[i] = true; + negc++; + } + } + if(onc != 2 && onc != 1 && onc != 0) goto triangulate; + + if(onc == 2) { + if(!instead) { + SEdge se = SEdge::From(on[0], on[1]); + edges = edges->InsertEdge(&se, n, out); + } + } + + if(posc == 0) { + InsertConvexHow(NEG, meta, vertex, cnt, instead); + return this; + } + if(negc == 0) { + InsertConvexHow(POS, meta, vertex, cnt, instead); + return this; + } + + Vector vpos[MAX_VERTICES]; + Vector vneg[MAX_VERTICES]; + int npos = 0, nneg = 0; + + Vector inter[2]; + int inters = 0; + + for(i = 0; i < cnt; i++) { + int ip = WRAP((i + 1), cnt); + + if(isPos[i]) { + vpos[npos++] = vertex[i]; + } + if(isNeg[i]) { + vneg[nneg++] = vertex[i]; + } + if(isOn[i]) { + vneg[nneg++] = vertex[i]; + vpos[npos++] = vertex[i]; + } + if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) { + Vector vi = IntersectionWith(vertex[i], vertex[ip]); + vpos[npos++] = vi; + vneg[nneg++] = vi; + + if(inters >= 2) oops(); + inter[inters++] = vi; + } + } + if(npos > cnt + 1 || nneg > cnt + 1) oops(); + + if(!instead) { + if(inters == 2) { + SEdge se = SEdge::From(inter[0], inter[1]); + edges = edges->InsertEdge(&se, n, out); + } else if(inters == 1 && onc == 1) { + SEdge se = SEdge::From(inter[0], on[0]); + edges = edges->InsertEdge(&se, n, out); + } else if(inters == 0 && onc == 2) { + // We already handled this on-plane existing edge + } else oops(); + } + if(nneg < 3 || npos < 3) oops(); + + InsertConvexHow(NEG, meta, vneg, nneg, instead); + InsertConvexHow(POS, meta, vpos, npos, instead); + return this; + +triangulate: + // We don't handle the special case for this; do it as triangles + SBsp3 *r = this; + for(i = 0; i < cnt - 2; i++) { + STriangle tr = STriangle::From(meta, + vertex[0], vertex[i+1], vertex[i+2]); + r = r->Insert(&tr, instead); + } + return r; +} + +SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead) { + if(!this) { + if(instead) { + if(instead->flipNormal) { + instead->atLeastOneDiscarded = true; + } else { + instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c); + } + return NULL; + } + + // Brand new node; so allocate for it, and fill us in. + SBsp3 *r = Alloc(); + r->n = (tr->Normal()).WithMagnitude(1); + r->d = (tr->a).Dot(r->n); + r->tri = *tr; + return r; + } + + double dt[3] = { (tr->a).Dot(n), (tr->b).Dot(n), (tr->c).Dot(n) }; + + int inc = 0, posc = 0, negc = 0; + bool isPos[3], isNeg[3], isOn[3]; + ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn); + // Count vertices in the plane + for(int i = 0; i < 3; i++) { + if(fabs(dt[i] - d) < LENGTH_EPS) { + inc++; + isOn[i] = true; + } else if(dt[i] > d) { + posc++; + isPos[i] = true; + } else { + negc++; + isNeg[i] = true; + } + } + + // All vertices in-plane + if(inc == 3) { + InsertHow(COPLANAR, tr, instead); + return this; + } + + // No split required + if(posc == 0 || negc == 0) { + if(inc == 2) { + Vector a, b; + if (!isOn[0]) { a = tr->b; b = tr->c; } + else if(!isOn[1]) { a = tr->c; b = tr->a; } + else if(!isOn[2]) { a = tr->a; b = tr->b; } + else oops(); + if(!instead) { + SEdge se = SEdge::From(a, b); + edges = edges->InsertEdge(&se, n, tr->Normal()); + } + } + + if(posc > 0) { + InsertHow(POS, tr, instead); + } else { + InsertHow(NEG, tr, instead); + } + return this; + } + + // The polygon must be split into two pieces, one above, one below. + Vector a, b, c; + + if(posc == 1 && negc == 1 && inc == 1) { + bool bpos; + // Standardize so that a is on the plane + if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1]; + } else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2]; + } else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0]; + } else oops(); + + Vector bPc = IntersectionWith(b, c); + STriangle btri = STriangle::From(tr->meta, a, b, bPc); + STriangle ctri = STriangle::From(tr->meta, c, a, bPc); + + if(bpos) { + InsertHow(POS, &btri, instead); + InsertHow(NEG, &ctri, instead); + } else { + InsertHow(POS, &ctri, instead); + InsertHow(NEG, &btri, instead); + } + + if(!instead) { + SEdge se = SEdge::From(a, bPc); + edges = edges->InsertEdge(&se, n, tr->Normal()); + } + + return this; + } + + if(posc == 2 && negc == 1) { + // Standardize so that a is on one side, and b and c are on the other. + if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c; + } else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a; + } else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b; + } else oops(); + + } else if(posc == 1 && negc == 2) { + if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c; + } else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a; + } else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b; + } else oops(); + } else oops(); + + Vector aPb = IntersectionWith(a, b); + Vector cPa = IntersectionWith(c, a); + + STriangle alone = STriangle::From(tr->meta, a, aPb, cPa); + Vector quad[4] = { aPb, b, c, cPa }; + + if(posc == 2 && negc == 1) { + InsertConvexHow(POS, tr->meta, quad, 4, instead); + InsertHow(NEG, &alone, instead); + } else { + InsertConvexHow(NEG, tr->meta, quad, 4, instead); + InsertHow(POS, &alone, instead); + } + if(!instead) { + SEdge se = SEdge::From(aPb, cPa); + edges = edges->InsertEdge(&se, n, alone.Normal()); + } + + return this; +} + +void SBsp3::DebugDraw(void) { + if(!this) return; + + pos->DebugDraw(); + Vector norm = tri.Normal(); + glNormal3d(norm.x, norm.y, norm.z); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glBegin(GL_TRIANGLES); + glxVertex3v(tri.a); + glxVertex3v(tri.b); + glxVertex3v(tri.c); + glEnd(); + + glDisable(GL_LIGHTING); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glxDepthRangeOffset(2); + glBegin(GL_TRIANGLES); + glxVertex3v(tri.a); + glxVertex3v(tri.b); + glxVertex3v(tri.c); + glEnd(); + + glDisable(GL_LIGHTING); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + glPointSize(10); + glxDepthRangeOffset(2); + glBegin(GL_TRIANGLES); + glxVertex3v(tri.a); + glxVertex3v(tri.b); + glxVertex3v(tri.c); + glEnd(); + + glxDepthRangeOffset(0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + more->DebugDraw(); + neg->DebugDraw(); + + edges->DebugDraw(n, d); +} + +///////////////////////////////// + +Vector SBsp2::IntersectionWith(Vector a, Vector b) { + double da = a.Dot(no) - d; + double db = b.Dot(no) - d; + if(da*db > 0) oops(); + + double dab = (db - da); + return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab)); +} + +SBsp2 *SBsp2::InsertEdge(SEdge *nedge, Vector nnp, Vector out) { + if(!this) { + // Brand new node; so allocate for it, and fill us in. + SBsp2 *r = Alloc(); + r->np = nnp; + r->no = ((r->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1); + if(out.Dot(r->no) < 0) { + r->no = (r->no).ScaledBy(-1); + } + r->d = (nedge->a).Dot(r->no); + r->edge = *nedge; + return r; + } + + double dt[2] = { (nedge->a).Dot(no), (nedge->b).Dot(no) }; + + bool isPos[2], isNeg[2], isOn[2]; + ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn); + for(int i = 0; i < 2; i++) { + if(fabs(dt[i] - d) < LENGTH_EPS) { + isOn[i] = true; + } else if(dt[i] > d) { + isPos[i] = true; + } else { + isNeg[i] = true; + } + } + + if((isPos[0] && isPos[1])||(isPos[0] && isOn[1])||(isOn[0] && isPos[1])) { + pos = pos->InsertEdge(nedge, nnp, out); + return this; + } + if((isNeg[0] && isNeg[1])||(isNeg[0] && isOn[1])||(isOn[0] && isNeg[1])) { + neg = neg->InsertEdge(nedge, nnp, out); + return this; + } + if(isOn[0] && isOn[1]) { + SBsp2 *m = Alloc(); + + m->np = nnp; + m->no = ((m->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1); + if(out.Dot(m->no) < 0) { + m->no = (m->no).ScaledBy(-1); + } + m->d = (nedge->a).Dot(m->no); + m->edge = *nedge; + + m->more = more; + more = m; + return this; + } + if((isPos[0] && isNeg[1]) || (isNeg[0] && isPos[1])) { + Vector aPb = IntersectionWith(nedge->a, nedge->b); + + SEdge ea = SEdge::From(nedge->a, aPb); + SEdge eb = SEdge::From(aPb, nedge->b); + + if(isPos[0]) { + pos = pos->InsertEdge(&ea, nnp, out); + neg = neg->InsertEdge(&eb, nnp, out); + } else { + neg = neg->InsertEdge(&ea, nnp, out); + pos = pos->InsertEdge(&eb, nnp, out); + } + return this; + } + oops(); +} + +void SBsp2::InsertTriangleHow(int how, STriangle *tr, SMesh *m, SBsp3 *bsp3) { + switch(how) { + case POS: + if(pos) { + pos->InsertTriangle(tr, m, bsp3); + } else { + bsp3->InsertInPlane(true, tr, m); + } + break; + + case NEG: + if(neg) { + neg->InsertTriangle(tr, m, bsp3); + } else { + bsp3->InsertInPlane(false, tr, m); + } + break; + + default: oops(); + } +} + +void SBsp2::InsertTriangle(STriangle *tr, SMesh *m, SBsp3 *bsp3) { + double dt[3] = { (tr->a).Dot(no), (tr->b).Dot(no), (tr->c).Dot(no) }; + + bool isPos[3], isNeg[3], isOn[3]; + int inc = 0, posc = 0, negc = 0; + ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn); + for(int i = 0; i < 3; i++) { + if(fabs(dt[i] - d) < LENGTH_EPS) { + isOn[i] = true; + inc++; + } else if(dt[i] > d) { + isPos[i] = true; + posc++; + } else { + isNeg[i] = true; + negc++; + } + } + + if(inc == 3) { + // All vertices on-line; so it's a degenerate triangle, to ignore. + return; + } + + // No split required + if(posc == 0 || negc == 0) { + if(posc > 0) { + InsertTriangleHow(POS, tr, m, bsp3); + } else { + InsertTriangleHow(NEG, tr, m, bsp3); + } + return; + } + + // The polygon must be split into two pieces, one above, one below. + Vector a, b, c; + + if(posc == 1 && negc == 1 && inc == 1) { + bool bpos; + // Standardize so that a is on the plane + if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1]; + } else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2]; + } else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0]; + } else oops(); + + Vector bPc = IntersectionWith(b, c); + STriangle btri = STriangle::From(tr->meta, a, b, bPc); + STriangle ctri = STriangle::From(tr->meta, c, a, bPc); + + if(bpos) { + InsertTriangleHow(POS, &btri, m, bsp3); + InsertTriangleHow(NEG, &ctri, m, bsp3); + } else { + InsertTriangleHow(POS, &ctri, m, bsp3); + InsertTriangleHow(NEG, &btri, m, bsp3); + } + + return; + } + + if(posc == 2 && negc == 1) { + // Standardize so that a is on one side, and b and c are on the other. + if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c; + } else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a; + } else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b; + } else oops(); + + } else if(posc == 1 && negc == 2) { + if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c; + } else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a; + } else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b; + } else oops(); + } else oops(); + + Vector aPb = IntersectionWith(a, b); + Vector cPa = IntersectionWith(c, a); + + STriangle alone = STriangle::From(tr->meta, a, aPb, cPa); + STriangle quad1 = STriangle::From(tr->meta, aPb, b, c ); + STriangle quad2 = STriangle::From(tr->meta, aPb, c, cPa); + + if(posc == 2 && negc == 1) { + InsertTriangleHow(POS, &quad1, m, bsp3); + InsertTriangleHow(POS, &quad2, m, bsp3); + InsertTriangleHow(NEG, &alone, m, bsp3); + } else { + InsertTriangleHow(NEG, &quad1, m, bsp3); + InsertTriangleHow(NEG, &quad2, m, bsp3); + InsertTriangleHow(POS, &alone, m, bsp3); + } + + return; +} + +void SBsp2::DebugDraw(Vector n, double d) { + if(!this) return; + + if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops(); + if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops(); + + glLineWidth(10); + glBegin(GL_LINES); + glxVertex3v(edge.a); + glxVertex3v(edge.b); + glEnd(); + pos->DebugDraw(n, d); + neg->DebugDraw(n, d); + more->DebugDraw(n, d); + glLineWidth(1); +} + diff --git a/mesh.cpp b/mesh.cpp index 08fa570..586fba2 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -293,647 +293,3 @@ DWORD SMesh::FirstIntersectionWith(Point2d mp) { return face; } -SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); } -SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } - -SBsp3 *SBsp3::FromMesh(SMesh *m) { - SBsp3 *bsp3 = NULL; - int i; - - SMesh mc; ZERO(&mc); - for(i = 0; i < m->l.n; i++) { - mc.AddTriangle(&(m->l.elem[i])); - } - - srand(0); // Let's be deterministic, at least! - int n = mc.l.n; - while(n > 1) { - int k = rand() % n; - n--; - SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]); - } - - for(i = 0; i < mc.l.n; i++) { - bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL); - } - - mc.Clear(); - return bsp3; -} - -Vector SBsp3::IntersectionWith(Vector a, Vector b) { - double da = a.Dot(n) - d; - double db = b.Dot(n) - d; - if(da*db > 0) oops(); - - double dab = (db - da); - return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab)); -} - -void SBsp3::InsertInPlane(bool pos2, STriangle *tr, SMesh *m) { - Vector tc = ((tr->a).Plus(tr->b).Plus(tr->c)).ScaledBy(1.0/3); - - bool onFace = false; - bool sameNormal; - double maxNormalMag = -1; - - Vector lln, trn = tr->Normal(); - - SBsp3 *ll = this; - while(ll) { - if((ll->tri).ContainsPoint(tc)) { - onFace = true; - // If the mesh contains almost-zero-area triangles, and we're - // just on the edge of one of those, then don't trust its normal. - lln = (ll->tri).Normal(); - if(lln.Magnitude() > maxNormalMag) { - sameNormal = trn.Dot(lln) > 0; - maxNormalMag = lln.Magnitude(); - } - } - ll = ll->more; - } - - if(m->flipNormal && ((!pos2 && !onFace) || - (onFace && !sameNormal && m->keepCoplanar))) - { - m->AddTriangle(tr->meta, tr->c, tr->b, tr->a); - } else if(!(m->flipNormal) && ((pos2 && !onFace) || - (onFace && sameNormal && m->keepCoplanar))) - { - m->AddTriangle(tr->meta, tr->a, tr->b, tr->c); - } else { - m->atLeastOneDiscarded = true; - } -} - -void SBsp3::InsertHow(int how, STriangle *tr, SMesh *instead) { - switch(how) { - case POS: - if(instead && !pos) goto alt; - pos = pos->Insert(tr, instead); - break; - - case NEG: - if(instead && !neg) goto alt; - neg = neg->Insert(tr, instead); - break; - - case COPLANAR: { - if(instead) goto alt; - SBsp3 *m = Alloc(); - m->n = n; - m->d = d; - m->tri = *tr; - m->more = more; - more = m; - break; - } - default: oops(); - } - return; - -alt: - if(how == POS && !(instead->flipNormal)) { - instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c); - } else if(how == NEG && instead->flipNormal) { - instead->AddTriangle(tr->meta, tr->c, tr->b, tr->a); - } else if(how == COPLANAR) { - if(edges) { - edges->InsertTriangle(tr, instead, this); - } else { - // I suppose this actually is allowed to happen, if the coplanar - // face is the leaf, and all of its neighbors are earlier in tree? - InsertInPlane(false, tr, instead); - } - } else { - instead->atLeastOneDiscarded = true; - } -} - -void SBsp3::InsertConvexHow(int how, STriMeta meta, Vector *vertex, int n, - SMesh *instead) -{ - switch(how) { - case POS: - if(pos) { - pos = pos->InsertConvex(meta, vertex, n, instead); - return; - } - break; - - case NEG: - if(neg) { - neg = neg->InsertConvex(meta, vertex, n, instead); - return; - } - break; - - default: oops(); - } - int i; - for(i = 0; i < n - 2; i++) { - STriangle tr = STriangle::From(meta, - vertex[0], vertex[i+1], vertex[i+2]); - InsertHow(how, &tr, instead); - } -} - -SBsp3 *SBsp3::InsertConvex(STriMeta meta, Vector *vertex, int cnt, - SMesh *instead) -{ - Vector e01 = (vertex[1]).Minus(vertex[0]); - Vector e12 = (vertex[2]).Minus(vertex[1]); - Vector out = e01.Cross(e12); - -#define MAX_VERTICES 50 - if(cnt+1 >= MAX_VERTICES) goto triangulate; - - int i; - Vector on[2]; - bool isPos[MAX_VERTICES]; - bool isNeg[MAX_VERTICES]; - bool isOn[MAX_VERTICES]; - int posc = 0, negc = 0, onc = 0; - for(i = 0; i < cnt; i++) { - double dt = n.Dot(vertex[i]); - isPos[i] = isNeg[i] = isOn[i] = false; - if(fabs(dt - d) < LENGTH_EPS) { - isOn[i] = true; - if(onc < 2) { - on[onc] = vertex[i]; - } - onc++; - } else if(dt > d) { - isPos[i] = true; - posc++; - } else { - isNeg[i] = true; - negc++; - } - } - if(onc != 2 && onc != 1 && onc != 0) goto triangulate; - - if(onc == 2) { - if(!instead) { - SEdge se = SEdge::From(on[0], on[1]); - edges = edges->InsertEdge(&se, n, out); - } - } - - if(posc == 0) { - InsertConvexHow(NEG, meta, vertex, cnt, instead); - return this; - } - if(negc == 0) { - InsertConvexHow(POS, meta, vertex, cnt, instead); - return this; - } - - Vector vpos[MAX_VERTICES]; - Vector vneg[MAX_VERTICES]; - int npos = 0, nneg = 0; - - Vector inter[2]; - int inters = 0; - - for(i = 0; i < cnt; i++) { - int ip = WRAP((i + 1), cnt); - - if(isPos[i]) { - vpos[npos++] = vertex[i]; - } - if(isNeg[i]) { - vneg[nneg++] = vertex[i]; - } - if(isOn[i]) { - vneg[nneg++] = vertex[i]; - vpos[npos++] = vertex[i]; - } - if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) { - Vector vi = IntersectionWith(vertex[i], vertex[ip]); - vpos[npos++] = vi; - vneg[nneg++] = vi; - - if(inters >= 2) oops(); - inter[inters++] = vi; - } - } - if(npos > cnt + 1 || nneg > cnt + 1) oops(); - - if(!instead) { - if(inters == 2) { - SEdge se = SEdge::From(inter[0], inter[1]); - edges = edges->InsertEdge(&se, n, out); - } else if(inters == 1 && onc == 1) { - SEdge se = SEdge::From(inter[0], on[0]); - edges = edges->InsertEdge(&se, n, out); - } else if(inters == 0 && onc == 2) { - // We already handled this on-plane existing edge - } else oops(); - } - if(nneg < 3 || npos < 3) oops(); - - InsertConvexHow(NEG, meta, vneg, nneg, instead); - InsertConvexHow(POS, meta, vpos, npos, instead); - return this; - -triangulate: - // We don't handle the special case for this; do it as triangles - SBsp3 *r = this; - for(i = 0; i < cnt - 2; i++) { - STriangle tr = STriangle::From(meta, - vertex[0], vertex[i+1], vertex[i+2]); - r = r->Insert(&tr, instead); - } - return r; -} - -SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead) { - if(!this) { - if(instead) { - if(instead->flipNormal) { - instead->atLeastOneDiscarded = true; - } else { - instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c); - } - return NULL; - } - - // Brand new node; so allocate for it, and fill us in. - SBsp3 *r = Alloc(); - r->n = (tr->Normal()).WithMagnitude(1); - r->d = (tr->a).Dot(r->n); - r->tri = *tr; - return r; - } - - double dt[3] = { (tr->a).Dot(n), (tr->b).Dot(n), (tr->c).Dot(n) }; - - int inc = 0, posc = 0, negc = 0; - bool isPos[3], isNeg[3], isOn[3]; - ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn); - // Count vertices in the plane - for(int i = 0; i < 3; i++) { - if(fabs(dt[i] - d) < LENGTH_EPS) { - inc++; - isOn[i] = true; - } else if(dt[i] > d) { - posc++; - isPos[i] = true; - } else { - negc++; - isNeg[i] = true; - } - } - - // All vertices in-plane - if(inc == 3) { - InsertHow(COPLANAR, tr, instead); - return this; - } - - // No split required - if(posc == 0 || negc == 0) { - if(inc == 2) { - Vector a, b; - if (!isOn[0]) { a = tr->b; b = tr->c; } - else if(!isOn[1]) { a = tr->c; b = tr->a; } - else if(!isOn[2]) { a = tr->a; b = tr->b; } - else oops(); - if(!instead) { - SEdge se = SEdge::From(a, b); - edges = edges->InsertEdge(&se, n, tr->Normal()); - } - } - - if(posc > 0) { - InsertHow(POS, tr, instead); - } else { - InsertHow(NEG, tr, instead); - } - return this; - } - - // The polygon must be split into two pieces, one above, one below. - Vector a, b, c; - - if(posc == 1 && negc == 1 && inc == 1) { - bool bpos; - // Standardize so that a is on the plane - if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1]; - } else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2]; - } else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0]; - } else oops(); - - Vector bPc = IntersectionWith(b, c); - STriangle btri = STriangle::From(tr->meta, a, b, bPc); - STriangle ctri = STriangle::From(tr->meta, c, a, bPc); - - if(bpos) { - InsertHow(POS, &btri, instead); - InsertHow(NEG, &ctri, instead); - } else { - InsertHow(POS, &ctri, instead); - InsertHow(NEG, &btri, instead); - } - - if(!instead) { - SEdge se = SEdge::From(a, bPc); - edges = edges->InsertEdge(&se, n, tr->Normal()); - } - - return this; - } - - if(posc == 2 && negc == 1) { - // Standardize so that a is on one side, and b and c are on the other. - if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c; - } else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a; - } else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b; - } else oops(); - - } else if(posc == 1 && negc == 2) { - if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c; - } else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a; - } else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b; - } else oops(); - } else oops(); - - Vector aPb = IntersectionWith(a, b); - Vector cPa = IntersectionWith(c, a); - - STriangle alone = STriangle::From(tr->meta, a, aPb, cPa); - Vector quad[4] = { aPb, b, c, cPa }; - - if(posc == 2 && negc == 1) { - InsertConvexHow(POS, tr->meta, quad, 4, instead); - InsertHow(NEG, &alone, instead); - } else { - InsertConvexHow(NEG, tr->meta, quad, 4, instead); - InsertHow(POS, &alone, instead); - } - if(!instead) { - SEdge se = SEdge::From(aPb, cPa); - edges = edges->InsertEdge(&se, n, alone.Normal()); - } - - return this; -} - -void SBsp3::DebugDraw(void) { - if(!this) return; - - pos->DebugDraw(); - Vector norm = tri.Normal(); - glNormal3d(norm.x, norm.y, norm.z); - - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glBegin(GL_TRIANGLES); - glxVertex3v(tri.a); - glxVertex3v(tri.b); - glxVertex3v(tri.c); - glEnd(); - - glDisable(GL_LIGHTING); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glxDepthRangeOffset(2); - glBegin(GL_TRIANGLES); - glxVertex3v(tri.a); - glxVertex3v(tri.b); - glxVertex3v(tri.c); - glEnd(); - - glDisable(GL_LIGHTING); - glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); - glPointSize(10); - glxDepthRangeOffset(2); - glBegin(GL_TRIANGLES); - glxVertex3v(tri.a); - glxVertex3v(tri.b); - glxVertex3v(tri.c); - glEnd(); - - glxDepthRangeOffset(0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - more->DebugDraw(); - neg->DebugDraw(); - - edges->DebugDraw(n, d); -} - -///////////////////////////////// - -Vector SBsp2::IntersectionWith(Vector a, Vector b) { - double da = a.Dot(no) - d; - double db = b.Dot(no) - d; - if(da*db > 0) oops(); - - double dab = (db - da); - return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab)); -} - -SBsp2 *SBsp2::InsertEdge(SEdge *nedge, Vector nnp, Vector out) { - if(!this) { - // Brand new node; so allocate for it, and fill us in. - SBsp2 *r = Alloc(); - r->np = nnp; - r->no = ((r->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1); - if(out.Dot(r->no) < 0) { - r->no = (r->no).ScaledBy(-1); - } - r->d = (nedge->a).Dot(r->no); - r->edge = *nedge; - return r; - } - - double dt[2] = { (nedge->a).Dot(no), (nedge->b).Dot(no) }; - - bool isPos[2], isNeg[2], isOn[2]; - ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn); - for(int i = 0; i < 2; i++) { - if(fabs(dt[i] - d) < LENGTH_EPS) { - isOn[i] = true; - } else if(dt[i] > d) { - isPos[i] = true; - } else { - isNeg[i] = true; - } - } - - if((isPos[0] && isPos[1])||(isPos[0] && isOn[1])||(isOn[0] && isPos[1])) { - pos = pos->InsertEdge(nedge, nnp, out); - return this; - } - if((isNeg[0] && isNeg[1])||(isNeg[0] && isOn[1])||(isOn[0] && isNeg[1])) { - neg = neg->InsertEdge(nedge, nnp, out); - return this; - } - if(isOn[0] && isOn[1]) { - SBsp2 *m = Alloc(); - - m->np = nnp; - m->no = ((m->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1); - if(out.Dot(m->no) < 0) { - m->no = (m->no).ScaledBy(-1); - } - m->d = (nedge->a).Dot(m->no); - m->edge = *nedge; - - m->more = more; - more = m; - return this; - } - if((isPos[0] && isNeg[1]) || (isNeg[0] && isPos[1])) { - Vector aPb = IntersectionWith(nedge->a, nedge->b); - - SEdge ea = SEdge::From(nedge->a, aPb); - SEdge eb = SEdge::From(aPb, nedge->b); - - if(isPos[0]) { - pos = pos->InsertEdge(&ea, nnp, out); - neg = neg->InsertEdge(&eb, nnp, out); - } else { - neg = neg->InsertEdge(&ea, nnp, out); - pos = pos->InsertEdge(&eb, nnp, out); - } - return this; - } - oops(); -} - -void SBsp2::InsertTriangleHow(int how, STriangle *tr, SMesh *m, SBsp3 *bsp3) { - switch(how) { - case POS: - if(pos) { - pos->InsertTriangle(tr, m, bsp3); - } else { - bsp3->InsertInPlane(true, tr, m); - } - break; - - case NEG: - if(neg) { - neg->InsertTriangle(tr, m, bsp3); - } else { - bsp3->InsertInPlane(false, tr, m); - } - break; - - default: oops(); - } -} - -void SBsp2::InsertTriangle(STriangle *tr, SMesh *m, SBsp3 *bsp3) { - double dt[3] = { (tr->a).Dot(no), (tr->b).Dot(no), (tr->c).Dot(no) }; - - bool isPos[3], isNeg[3], isOn[3]; - int inc = 0, posc = 0, negc = 0; - ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn); - for(int i = 0; i < 3; i++) { - if(fabs(dt[i] - d) < LENGTH_EPS) { - isOn[i] = true; - inc++; - } else if(dt[i] > d) { - isPos[i] = true; - posc++; - } else { - isNeg[i] = true; - negc++; - } - } - - if(inc == 3) { - // All vertices on-line; so it's a degenerate triangle, to ignore. - return; - } - - // No split required - if(posc == 0 || negc == 0) { - if(posc > 0) { - InsertTriangleHow(POS, tr, m, bsp3); - } else { - InsertTriangleHow(NEG, tr, m, bsp3); - } - return; - } - - // The polygon must be split into two pieces, one above, one below. - Vector a, b, c; - - if(posc == 1 && negc == 1 && inc == 1) { - bool bpos; - // Standardize so that a is on the plane - if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1]; - } else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2]; - } else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0]; - } else oops(); - - Vector bPc = IntersectionWith(b, c); - STriangle btri = STriangle::From(tr->meta, a, b, bPc); - STriangle ctri = STriangle::From(tr->meta, c, a, bPc); - - if(bpos) { - InsertTriangleHow(POS, &btri, m, bsp3); - InsertTriangleHow(NEG, &ctri, m, bsp3); - } else { - InsertTriangleHow(POS, &ctri, m, bsp3); - InsertTriangleHow(NEG, &btri, m, bsp3); - } - - return; - } - - if(posc == 2 && negc == 1) { - // Standardize so that a is on one side, and b and c are on the other. - if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c; - } else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a; - } else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b; - } else oops(); - - } else if(posc == 1 && negc == 2) { - if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c; - } else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a; - } else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b; - } else oops(); - } else oops(); - - Vector aPb = IntersectionWith(a, b); - Vector cPa = IntersectionWith(c, a); - - STriangle alone = STriangle::From(tr->meta, a, aPb, cPa); - STriangle quad1 = STriangle::From(tr->meta, aPb, b, c ); - STriangle quad2 = STriangle::From(tr->meta, aPb, c, cPa); - - if(posc == 2 && negc == 1) { - InsertTriangleHow(POS, &quad1, m, bsp3); - InsertTriangleHow(POS, &quad2, m, bsp3); - InsertTriangleHow(NEG, &alone, m, bsp3); - } else { - InsertTriangleHow(NEG, &quad1, m, bsp3); - InsertTriangleHow(NEG, &quad2, m, bsp3); - InsertTriangleHow(POS, &alone, m, bsp3); - } - - return; -} - -void SBsp2::DebugDraw(Vector n, double d) { - if(!this) return; - - if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops(); - if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops(); - - glLineWidth(10); - glBegin(GL_LINES); - glxVertex3v(edge.a); - glxVertex3v(edge.b); - glEnd(); - pos->DebugDraw(n, d); - neg->DebugDraw(n, d); - more->DebugDraw(n, d); - glLineWidth(1); -} - diff --git a/textscreens.cpp b/textscreens.cpp new file mode 100644 index 0000000..b296c7b --- /dev/null +++ b/textscreens.cpp @@ -0,0 +1,692 @@ +#include "solvespace.h" + +//----------------------------------------------------------------------------- +// A navigation bar that always appears at the top of the window. +//----------------------------------------------------------------------------- +void TextWindow::ScreenNavigation(int link, DWORD v) { + switch(link) { + default: + case 'h': + SS.TW.OneScreenForwardTo(SCREEN_LIST_OF_GROUPS); + break; + + case 'b': + if(SS.TW.history > 0) { + SS.TW.shownIndex--; + if(SS.TW.shownIndex < 0) SS.TW.shownIndex = (HISTORY_LEN-1); + SS.TW.shown = &(SS.TW.showns[SS.TW.shownIndex]); + SS.TW.history--; + } + break; + + case 'f': + SS.TW.OneScreenForwardTo(-1); + break; + } +} +void TextWindow::ShowHeader(bool withNav) { + ClearScreen(); + + char *cd = SS.GW.LockedInWorkplane() ? + SS.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString() : + "free in 3d"; + + // Navigation buttons + if(withNav) { + Printf(false, " %Lb%f<<%E %Lh%fhome%E %Bt%Ft wrkpl:%Fd %s", + (&TextWindow::ScreenNavigation), + (&TextWindow::ScreenNavigation), + cd); + } else { + Printf(false, " %Bt%Ft wrkpl:%Fd %s", cd); + } + +#define hs(b) ((b) ? 's' : 'h') + Printf(false, "%Bt%Ftshow: " + "%Fp%Ll%D%fwrkpls%E " + "%Fp%Ll%D%fnormals%E " + "%Fp%Ll%D%fpoints%E " + "%Fp%Ll%D%fconstraints%E ", + hs(SS.GW.showWorkplanes), (DWORD)&(SS.GW.showWorkplanes), &(SS.GW.ToggleBool), + hs(SS.GW.showNormals), (DWORD)&(SS.GW.showNormals), &(SS.GW.ToggleBool), + hs(SS.GW.showPoints), (DWORD)&(SS.GW.showPoints), &(SS.GW.ToggleBool), +hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints), &(SS.GW.ToggleBool) + ); + Printf(false, "%Bt%Ft " + "%Fp%Ll%D%fshaded%E " + "%Fp%Ll%D%ffaces%E " + "%Fp%Ll%D%fmesh%E " + "%Fp%Ll%D%fhidden-lines%E", +hs(SS.GW.showShaded), (DWORD)(&SS.GW.showShaded), &(SS.GW.ToggleBool), +hs(SS.GW.showFaces), (DWORD)(&SS.GW.showFaces), &(SS.GW.ToggleBool), +hs(SS.GW.showMesh), (DWORD)(&SS.GW.showMesh), &(SS.GW.ToggleBool), +hs(SS.GW.showHdnLines), (DWORD)(&SS.GW.showHdnLines), &(SS.GW.ToggleBool) + ); +} + +//----------------------------------------------------------------------------- +// The screen that shows a list of every group in the sketch, with options +// to hide or show them, and to view them in detail. This is our home page. +//----------------------------------------------------------------------------- +void TextWindow::ScreenSelectGroup(int link, DWORD v) { + SS.TW.OneScreenForwardTo(SCREEN_GROUP_INFO); + SS.TW.shown->group.v = v; +} +void TextWindow::ScreenToggleGroupShown(int link, DWORD v) { + hGroup hg = { v }; + Group *g = SS.GetGroup(hg); + g->visible = !(g->visible); + // If a group was just shown, then it might not have been generated + // previously, so regenerate. + SS.GenerateAll(); +} +void TextWindow::ScreenShowGroupsSpecial(int link, DWORD v) { + int i; + for(i = 0; i < SS.group.n; i++) { + Group *g = &(SS.group.elem[i]); + + if(link == 's') { + g->visible = true; + } else { + g->visible = false; + } + } +} +void TextWindow::ScreenActivateGroup(int link, DWORD v) { + hGroup hg = { v }; + Group *g = SS.GetGroup(hg); + g->visible = true; + SS.GW.activeGroup.v = v; + SS.GetGroup(SS.GW.activeGroup)->Activate(); + SS.GW.ClearSuper(); +} +void TextWindow::ReportHowGroupSolved(hGroup hg) { + SS.GW.ClearSuper(); + SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO); + SS.TW.shown->group.v = hg.v; + SS.later.showTW = true; +} +void TextWindow::ScreenHowGroupSolved(int link, DWORD v) { + if(SS.GW.activeGroup.v != v) { + ScreenActivateGroup(link, v); + } + SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO); + SS.TW.shown->group.v = v; +} +void TextWindow::ScreenShowConfiguration(int link, DWORD v) { + SS.TW.OneScreenForwardTo(SCREEN_CONFIGURATION); +} +void TextWindow::ShowListOfGroups(void) { + Printf(true, "%Ftactv show ok group-name%E"); + int i; + bool afterActive = false; + for(i = 0; i < SS.group.n; i++) { + Group *g = &(SS.group.elem[i]); + char *s = g->DescriptionString(); + bool active = (g->h.v == SS.GW.activeGroup.v); + bool shown = g->visible; + bool ok = (g->solved.how == Group::SOLVED_OKAY); + bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); + Printf(false, "%Bp%Fd " + "%Fp%D%f%s%Ll%s%E%s " + "%Fp%D%f%Ll%s%E%Fh%s%E " + "%Fp%D%f%s%Ll%s%E " + "%Fl%Ll%D%f%s", + // Alternate between light and dark backgrounds, for readability + (i & 1) ? 'd' : 'a', + // Link that activates the group + active ? 's' : 'h', g->h.v, (&TextWindow::ScreenActivateGroup), + active ? "yes" : (ref ? " " : ""), + active ? "" : (ref ? "" : "no"), + active ? "" : " ", + // Link that hides or shows the group + shown ? 's' : 'h', g->h.v, (&TextWindow::ScreenToggleGroupShown), + afterActive ? "" : (shown ? "yes" : "no"), + afterActive ? " - " : (shown ? "" : " "), + // Link to the errors, if a problem occured while solving + ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved), + ok ? "ok" : "", + ok ? "" : "NO", + // Link to a screen that gives more details on the group + g->h.v, (&TextWindow::ScreenSelectGroup), s); + + if(active) afterActive = true; + } + + Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E", + &(TextWindow::ScreenShowGroupsSpecial), + &(TextWindow::ScreenShowGroupsSpecial)); + Printf(false, " %Fl%Ls%fconfiguration%E", + &(TextWindow::ScreenShowConfiguration)); +} + + +//----------------------------------------------------------------------------- +// The screen that shows information about a specific group, and allows the +// user to edit various things about it. +//----------------------------------------------------------------------------- +void TextWindow::ScreenHoverConstraint(int link, DWORD v) { + if(!SS.GW.showConstraints) return; + + hConstraint hc = { v }; + Constraint *c = SS.GetConstraint(hc); + if(c->group.v != SS.GW.activeGroup.v) { + // Only constraints in the active group are visible + return; + } + SS.GW.hover.Clear(); + SS.GW.hover.constraint = hc; + SS.GW.hover.emphasized = true; +} +void TextWindow::ScreenHoverRequest(int link, DWORD v) { + SS.GW.hover.Clear(); + hRequest hr = { v }; + SS.GW.hover.entity = hr.entity(0); + SS.GW.hover.emphasized = true; +} +void TextWindow::ScreenSelectConstraint(int link, DWORD v) { + SS.GW.ClearSelection(); + SS.GW.selection[0].constraint.v = v; +} +void TextWindow::ScreenSelectRequest(int link, DWORD v) { + hRequest hr = { v }; + SS.GW.ClearSelection(); + SS.GW.selection[0].entity = hr.entity(0); +} +void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) { + SS.UndoRemember(); + + Group *g = SS.GetGroup(SS.TW.shown->group); + if(g->subtype == Group::ONE_SIDED) { + g->subtype = Group::TWO_SIDED; + } else if(g->subtype == Group::TWO_SIDED) { + g->subtype = Group::ONE_SIDED; + } else oops(); + SS.MarkGroupDirty(g->h); + SS.GenerateAll(); + SS.GW.ClearSuper(); +} +void TextWindow::ScreenChangeSkipFirst(int link, DWORD v) { + SS.UndoRemember(); + + Group *g = SS.GetGroup(SS.TW.shown->group); + (g->skipFirst) = !(g->skipFirst); + SS.MarkGroupDirty(g->h); + SS.GenerateAll(); + SS.GW.ClearSuper(); +} +void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) { + SS.UndoRemember(); + + Group *g = SS.GetGroup(SS.TW.shown->group); + g->meshCombine = v; + SS.MarkGroupDirty(g->h); + SS.GenerateAll(); + SS.GW.ClearSuper(); +} +void TextWindow::ScreenChangeRightLeftHanded(int link, DWORD v) { + SS.UndoRemember(); + + Group *g = SS.GetGroup(SS.TW.shown->group); + if(g->subtype == Group::RIGHT_HANDED) { + g->subtype = Group::LEFT_HANDED; + } else { + g->subtype = Group::RIGHT_HANDED; + } + SS.MarkGroupDirty(g->h); + SS.GenerateAll(); + SS.GW.ClearSuper(); +} +void TextWindow::ScreenChangeHelixParameter(int link, DWORD v) { + Group *g = SS.GetGroup(SS.TW.shown->group); + char str[1024]; + int r; + if(link == 't') { + sprintf(str, "%.3f", g->valA); + SS.TW.edit.meaning = EDIT_HELIX_TURNS; + r = 12; + } else if(link == 'p') { + strcpy(str, SS.MmToString(g->valB)); + SS.TW.edit.meaning = EDIT_HELIX_PITCH; + r = 14; + } else if(link == 'r') { + strcpy(str, SS.MmToString(g->valC)); + SS.TW.edit.meaning = EDIT_HELIX_DRADIUS; + r = 16; + } else oops(); + SS.TW.edit.group.v = v; + ShowTextEditControl(r, 9, str); +} +void TextWindow::ScreenColor(int link, DWORD v) { + SS.UndoRemember(); + + Group *g = SS.GetGroup(SS.TW.shown->group); + if(v < 0 || v >= SS.MODEL_COLORS) return; + g->color = SS.modelColor[v]; + SS.MarkGroupDirty(g->h); + SS.GenerateAll(); + SS.GW.ClearSuper(); +} +void TextWindow::ScreenChangeExprA(int link, DWORD v) { + Group *g = SS.GetGroup(SS.TW.shown->group); + + // There's an extra line for the skipFirst parameter in one-sided groups. + int r = (g->subtype == Group::ONE_SIDED) ? 15 : 13; + + char str[1024]; + sprintf(str, "%d", (int)g->valA); + ShowTextEditControl(r, 9, str); + SS.TW.edit.meaning = EDIT_TIMES_REPEATED; + SS.TW.edit.group.v = v; +} +void TextWindow::ScreenChangeGroupName(int link, DWORD v) { + Group *g = SS.GetGroup(SS.TW.shown->group); + ShowTextEditControl(7, 14, g->DescriptionString()+5); + SS.TW.edit.meaning = EDIT_GROUP_NAME; + SS.TW.edit.group.v = v; +} +void TextWindow::ScreenDeleteGroup(int link, DWORD v) { + SS.UndoRemember(); + + hGroup hg = SS.TW.shown->group; + if(hg.v == SS.GW.activeGroup.v) { + Error("This group is currently active; activate a different group " + "before proceeding."); + return; + } + SS.group.RemoveById(SS.TW.shown->group); + // This is a major change, so let's re-solve everything. + SS.TW.ClearSuper(); + SS.GW.ClearSuper(); + SS.GenerateAll(0, INT_MAX); +} +void TextWindow::ShowGroupInfo(void) { + Group *g = SS.group.FindById(shown->group); + char *s, *s2, *s3; + + if(shown->group.v == Group::HGROUP_REFERENCES.v) { + Printf(true, "%FtGROUP %E%s", g->DescriptionString()); + } else { + Printf(true, "%FtGROUP %E%s " + "[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", + g->DescriptionString(), + g->h.v, &TextWindow::ScreenChangeGroupName, + g->h.v, &TextWindow::ScreenDeleteGroup); + } + + if(g->type == Group::EXTRUDE) { + s = "EXTRUDE "; + } else if(g->type == Group::TRANSLATE) { + s = "TRANSLATE"; + s2 ="REPEAT "; + s3 ="START "; + } else if(g->type == Group::ROTATE) { + s = "ROTATE "; + s2 ="REPEAT "; + s3 ="START "; + } + + if(g->type == Group::EXTRUDE || g->type == Group::ROTATE || + g->type == Group::TRANSLATE) + { + bool one = (g->subtype == Group::ONE_SIDED); + Printf(true, "%Ft%s%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", s, + &TextWindow::ScreenChangeOneOrTwoSides, + (one ? "" : "one side"), (one ? "one side" : ""), + &TextWindow::ScreenChangeOneOrTwoSides, + (!one ? "" : "two sides"), (!one ? "two sides" : "")); + } + + if(g->type == Group::LATHE) { + Printf(true, "%FtLATHE"); + } + + if(g->type == Group::SWEEP) { + Printf(true, "%FtSWEEP"); + } + + if(g->type == Group::HELICAL_SWEEP) { + bool rh = (g->subtype == Group::RIGHT_HANDED); + Printf(true, + "%FtHELICAL%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", + &ScreenChangeRightLeftHanded, + (rh ? "" : "right-hand"), (rh ? "right-hand" : ""), + &ScreenChangeRightLeftHanded, + (!rh ? "" : "left-hand"), (!rh ? "left-hand" : "")); + Printf(false, "%FtTHROUGH%E %@ turns %Fl%Lt%D%f[change]%E", + g->valA, g->h.v, &ScreenChangeHelixParameter); + Printf(false, "%FtPITCH%E %s axially per turn %Fl%Lp%D%f[change]%E", + SS.MmToString(g->valB), g->h.v, &ScreenChangeHelixParameter); + Printf(false, "%FtdRADIUS%E %s radially per turn %Fl%Lr%D%f[change]%E", + SS.MmToString(g->valC), g->h.v, &ScreenChangeHelixParameter); + } + + if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) { + bool space; + if(g->subtype == Group::ONE_SIDED) { + bool skip = g->skipFirst; + Printf(true, "%Ft%s%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", + s3, + &ScreenChangeSkipFirst, + (!skip ? "" : "with original"), (!skip ? "with original" : ""), + &ScreenChangeSkipFirst, + (skip ? "":"with copy #1"), (skip ? "with copy #1":"")); + space = false; + } else { + space = true; + } + + int times = (int)(g->valA); + Printf(space, "%Ft%s%E %d time%s %Fl%Ll%D%f[change]%E", + s2, times, times == 1 ? "" : "s", + g->h.v, &TextWindow::ScreenChangeExprA); + } + + if(g->type == Group::IMPORTED) { + Printf(true, "%FtIMPORT%E '%s'", g->impFile); + } + + if(g->type == Group::EXTRUDE || + g->type == Group::LATHE || + g->type == Group::SWEEP || + g->type == Group::HELICAL_SWEEP || + g->type == Group::IMPORTED) + { + bool un = (g->meshCombine == Group::COMBINE_AS_UNION); + bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE); + bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE); + bool asa = (g->type == Group::IMPORTED); + + Printf((g->type == Group::HELICAL_SWEEP), + "%FtMERGE AS%E %Fh%f%D%Ll%s%E%Fs%s%E / %Fh%f%D%Ll%s%E%Fs%s%E %s " + "%Fh%f%D%Ll%s%E%Fs%s%E", + &TextWindow::ScreenChangeMeshCombine, + Group::COMBINE_AS_UNION, + (un ? "" : "union"), (un ? "union" : ""), + &TextWindow::ScreenChangeMeshCombine, + Group::COMBINE_AS_DIFFERENCE, + (diff ? "" : "difference"), (diff ? "difference" : ""), + asa ? "/" : "", + &TextWindow::ScreenChangeMeshCombine, + Group::COMBINE_AS_ASSEMBLE, + (asy || !asa ? "" : "assemble"), (asy && asa ? "assemble" : "")); + } + if(g->type == Group::IMPORTED && g->meshError.yes) { + Printf(false, "%Fx the parts interfere!"); + } + + if(g->type == Group::EXTRUDE || + g->type == Group::LATHE || + g->type == Group::SWEEP || + g->type == Group::HELICAL_SWEEP) + { +#define TWOX(v) v v + Printf(true, "%FtM_COLOR%E " TWOX(TWOX(TWOX("%Bp%D%f%Ln %Bd%E "))), + 0x80000000 | SS.modelColor[0], 0, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[1], 1, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[2], 2, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[3], 3, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[4], 4, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[5], 5, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[6], 6, &TextWindow::ScreenColor, + 0x80000000 | SS.modelColor[7], 7, &TextWindow::ScreenColor); + } + + // Leave more space if the group has configuration stuff above the req/ + // constraint list (as all but the drawing groups do). + if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) { + Printf(true, "%Ftrequests in group"); + } else { + Printf(false, ""); + Printf(false, "%Ftrequests in group"); + } + + int i, a = 0; + for(i = 0; i < SS.request.n; i++) { + Request *r = &(SS.request.elem[i]); + + if(r->group.v == shown->group.v) { + char *s = r->DescriptionString(); + Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", + (a & 1) ? 'd' : 'a', + r->h.v, (&TextWindow::ScreenSelectRequest), + &(TextWindow::ScreenHoverRequest), s); + a++; + } + } + if(a == 0) Printf(false, "%Ba (none)"); + + a = 0; + Printf(true, "%Ftconstraints in group"); + for(i = 0; i < SS.constraint.n; i++) { + Constraint *c = &(SS.constraint.elem[i]); + + if(c->group.v == shown->group.v) { + char *s = c->DescriptionString(); + Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", + (a & 1) ? 'd' : 'a', + c->h.v, (&TextWindow::ScreenSelectConstraint), + (&TextWindow::ScreenHoverConstraint), s); + a++; + } + } + if(a == 0) Printf(false, "%Ba (none)"); +} + +//----------------------------------------------------------------------------- +// The screen that's displayed when the sketch fails to solve. A report of +// what failed, and (if the problem is a singular Jacobian) a list of +// constraints that could be removed to fix it. +//----------------------------------------------------------------------------- +void TextWindow::ShowGroupSolveInfo(void) { + Group *g = SS.group.FindById(shown->group); + if(g->solved.how == Group::SOLVED_OKAY) { + // Go back to the default group info screen + shown->screen = SCREEN_GROUP_INFO; + Show(); + return; + } + + Printf(true, "%FtGROUP %E%s", g->DescriptionString()); + switch(g->solved.how) { + case Group::DIDNT_CONVERGE: + Printf(true, " %FxSOLVE FAILED!%Fd no convergence"); + break; + + case Group::SINGULAR_JACOBIAN: { + Printf(true, "%FxSOLVE FAILED!%Fd inconsistent system"); + Printf(true, "remove any one of these to fix it"); + for(int i = 0; i < g->solved.remove.n; i++) { + hConstraint hc = g->solved.remove.elem[i]; + Constraint *c = SS.constraint.FindByIdNoOops(hc); + if(!c) continue; + + Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", + (i & 1) ? 'd' : 'a', + c->h.v, (&TextWindow::ScreenSelectConstraint), + (&TextWindow::ScreenHoverConstraint), + c->DescriptionString()); + } + break; + } + } +} + +//----------------------------------------------------------------------------- +// For the configuration screen, setup items that are not specific to the +// file being edited right now. +//----------------------------------------------------------------------------- +void TextWindow::ScreenChangeLightDirection(int link, DWORD v) { + char str[1024]; + sprintf(str, "%.2f, %.2f, %.2f", CO(SS.lightDir[v])); + ShowTextEditControl(29+2*v, 8, str); + SS.TW.edit.meaning = EDIT_LIGHT_DIRECTION; + SS.TW.edit.i = v; +} +void TextWindow::ScreenChangeLightIntensity(int link, DWORD v) { + char str[1024]; + sprintf(str, "%.2f", SS.lightIntensity[v]); + ShowTextEditControl(29+2*v, 30, str); + SS.TW.edit.meaning = EDIT_LIGHT_INTENSITY; + SS.TW.edit.i = v; +} +void TextWindow::ScreenChangeColor(int link, DWORD v) { + char str[1024]; + sprintf(str, "%.2f, %.2f, %.2f", + REDf(SS.modelColor[v]), + GREENf(SS.modelColor[v]), + BLUEf(SS.modelColor[v])); + ShowTextEditControl(9+2*v, 12, str); + SS.TW.edit.meaning = EDIT_COLOR; + SS.TW.edit.i = v; +} +void TextWindow::ScreenChangeMeshTolerance(int link, DWORD v) { + char str[1024]; + sprintf(str, "%.2f", SS.meshTol); + ShowTextEditControl(37, 3, str); + SS.TW.edit.meaning = EDIT_MESH_TOLERANCE; +} +void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) { + char str[1024]; + sprintf(str, "%.3f", 1000*SS.cameraTangent); + ShowTextEditControl(43, 3, str); + SS.TW.edit.meaning = EDIT_CAMERA_TANGENT; +} +void TextWindow::ShowConfiguration(void) { + int i; + Printf(true, "%Ft material color-(r, g, b)"); + + for(i = 0; i < SS.MODEL_COLORS; i++) { + Printf(false, "%Bp #%d: %Bp %Bp (%@, %@, %@) %f%D%Ll%Fl[change]%E", + (i & 1) ? 'd' : 'a', + i, 0x80000000 | SS.modelColor[i], + (i & 1) ? 'd' : 'a', + REDf(SS.modelColor[i]), + GREENf(SS.modelColor[i]), + BLUEf(SS.modelColor[i]), + &ScreenChangeColor, i); + } + + Printf(false, ""); + Printf(false, "%Ft light direction intensity"); + for(i = 0; i < 2; i++) { + Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E " + "%2 %Fl%D%f%Ll[c]%E", + (i & 1) ? 'd' : 'a', i, + CO(SS.lightDir[i]), i, &ScreenChangeLightDirection, + SS.lightIntensity[i], i, &ScreenChangeLightIntensity); + } + + Printf(false, ""); + Printf(false, "%Ft mesh tolerance (smaller is finer)%E"); + Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles", + SS.meshTol, + &ScreenChangeMeshTolerance, 0, + SS.group.elem[SS.group.n-1].runningMesh.l.n); + + Printf(false, ""); + Printf(false, "%Ft perspective factor (0 for isometric)%E"); + Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E", + SS.cameraTangent*1000, + &ScreenChangeCameraTangent, 0); +} + +//----------------------------------------------------------------------------- +// The edit control is visible, and the user just pressed enter. +//----------------------------------------------------------------------------- +void TextWindow::EditControlDone(char *s) { + switch(edit.meaning) { + case EDIT_TIMES_REPEATED: { + Expr *e = Expr::From(s); + if(e) { + SS.UndoRemember(); + + Group *g = SS.GetGroup(edit.group); + g->valA = e->Eval(); + + SS.MarkGroupDirty(g->h); + SS.later.generateAll = true; + } else { + Error("Not a valid number or expression: '%s'", s); + } + break; + } + case EDIT_GROUP_NAME: { + char *t; + bool invalid = false; + for(t = s; *t; t++) { + if(!(isalnum(*t) || *t == '-' || *t == '_')) { + invalid = true; + } + } + if(invalid || !*s) { + Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); + } else { + SS.UndoRemember(); + + Group *g = SS.GetGroup(edit.group); + g->name.strcpy(s); + } + SS.unsaved = true; + break; + } + case EDIT_LIGHT_INTENSITY: + SS.lightIntensity[edit.i] = min(1, max(0, atof(s))); + InvalidateGraphics(); + break; + case EDIT_LIGHT_DIRECTION: { + double x, y, z; + if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) { + SS.lightDir[edit.i] = Vector::From(x, y, z); + } else { + Error("Bad format: specify coordinates as x, y, z"); + } + InvalidateGraphics(); + break; + } + case EDIT_COLOR: { + double r, g, b; + if(sscanf(s, "%lf, %lf, %lf", &r, &g, &b)==3) { + SS.modelColor[edit.i] = RGB(r*255, g*255, b*255); + } else { + Error("Bad format: specify color as r, g, b"); + } + break; + } + case EDIT_MESH_TOLERANCE: { + SS.meshTol = min(10, max(0.1, atof(s))); + SS.GenerateAll(0, INT_MAX); + break; + } + case EDIT_CAMERA_TANGENT: { + SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0; + InvalidateGraphics(); + break; + } + case EDIT_HELIX_TURNS: + case EDIT_HELIX_PITCH: + case EDIT_HELIX_DRADIUS: { + SS.UndoRemember(); + Group *g = SS.GetGroup(edit.group); + Expr *e = Expr::From(s); + if(!e) { + Error("Not a valid number or expression: '%s'", s); + break; + } + if(edit.meaning == EDIT_HELIX_TURNS) { + g->valA = min(30, fabs(e->Eval())); + } else if(edit.meaning == EDIT_HELIX_PITCH) { + g->valB = SS.ExprToMm(e); + } else { + g->valC = SS.ExprToMm(e); + } + SS.MarkGroupDirty(g->h); + SS.later.generateAll = true; + break; + } + } + SS.later.showTW = true; + HideTextEditControl(); + edit.meaning = EDIT_NOTHING; +} + diff --git a/textwin.cpp b/textwin.cpp index f9be9c2..acc975f 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -413,670 +413,3 @@ void TextWindow::OneScreenForwardTo(int screen) { if(screen >= 0) shown->screen = screen; } -void TextWindow::ScreenNavigation(int link, DWORD v) { - switch(link) { - default: - case 'h': - SS.TW.OneScreenForwardTo(SCREEN_LIST_OF_GROUPS); - break; - - case 'b': - if(SS.TW.history > 0) { - SS.TW.shownIndex--; - if(SS.TW.shownIndex < 0) SS.TW.shownIndex = (HISTORY_LEN-1); - SS.TW.shown = &(SS.TW.showns[SS.TW.shownIndex]); - SS.TW.history--; - } - break; - - case 'f': - SS.TW.OneScreenForwardTo(-1); - break; - } -} -void TextWindow::ShowHeader(bool withNav) { - ClearScreen(); - - char *cd = SS.GW.LockedInWorkplane() ? - SS.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString() : - "free in 3d"; - - // Navigation buttons - if(withNav) { - Printf(false, " %Lb%f<<%E %Lh%fhome%E %Bt%Ft wrkpl:%Fd %s", - (&TextWindow::ScreenNavigation), - (&TextWindow::ScreenNavigation), - cd); - } else { - Printf(false, " %Bt%Ft wrkpl:%Fd %s", cd); - } - -#define hs(b) ((b) ? 's' : 'h') - Printf(false, "%Bt%Ftshow: " - "%Fp%Ll%D%fwrkpls%E " - "%Fp%Ll%D%fnormals%E " - "%Fp%Ll%D%fpoints%E " - "%Fp%Ll%D%fconstraints%E ", - hs(SS.GW.showWorkplanes), (DWORD)&(SS.GW.showWorkplanes), &(SS.GW.ToggleBool), - hs(SS.GW.showNormals), (DWORD)&(SS.GW.showNormals), &(SS.GW.ToggleBool), - hs(SS.GW.showPoints), (DWORD)&(SS.GW.showPoints), &(SS.GW.ToggleBool), -hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints), &(SS.GW.ToggleBool) - ); - Printf(false, "%Bt%Ft " - "%Fp%Ll%D%fshaded%E " - "%Fp%Ll%D%ffaces%E " - "%Fp%Ll%D%fmesh%E " - "%Fp%Ll%D%fhidden-lines%E", -hs(SS.GW.showShaded), (DWORD)(&SS.GW.showShaded), &(SS.GW.ToggleBool), -hs(SS.GW.showFaces), (DWORD)(&SS.GW.showFaces), &(SS.GW.ToggleBool), -hs(SS.GW.showMesh), (DWORD)(&SS.GW.showMesh), &(SS.GW.ToggleBool), -hs(SS.GW.showHdnLines), (DWORD)(&SS.GW.showHdnLines), &(SS.GW.ToggleBool) - ); -} - -void TextWindow::ScreenSelectGroup(int link, DWORD v) { - SS.TW.OneScreenForwardTo(SCREEN_GROUP_INFO); - SS.TW.shown->group.v = v; -} -void TextWindow::ScreenToggleGroupShown(int link, DWORD v) { - hGroup hg = { v }; - Group *g = SS.GetGroup(hg); - g->visible = !(g->visible); - // If a group was just shown, then it might not have been generated - // previously, so regenerate. - SS.GenerateAll(); -} -void TextWindow::ScreenShowGroupsSpecial(int link, DWORD v) { - int i; - for(i = 0; i < SS.group.n; i++) { - Group *g = &(SS.group.elem[i]); - - if(link == 's') { - g->visible = true; - } else { - g->visible = false; - } - } -} -void TextWindow::ScreenActivateGroup(int link, DWORD v) { - hGroup hg = { v }; - Group *g = SS.GetGroup(hg); - g->visible = true; - SS.GW.activeGroup.v = v; - SS.GetGroup(SS.GW.activeGroup)->Activate(); - SS.GW.ClearSuper(); -} -void TextWindow::ReportHowGroupSolved(hGroup hg) { - SS.GW.ClearSuper(); - SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO); - SS.TW.shown->group.v = hg.v; - SS.later.showTW = true; -} -void TextWindow::ScreenHowGroupSolved(int link, DWORD v) { - if(SS.GW.activeGroup.v != v) { - ScreenActivateGroup(link, v); - } - SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO); - SS.TW.shown->group.v = v; -} -void TextWindow::ScreenShowConfiguration(int link, DWORD v) { - SS.TW.OneScreenForwardTo(SCREEN_CONFIGURATION); -} -void TextWindow::ShowListOfGroups(void) { - Printf(true, "%Ftactv show ok group-name%E"); - int i; - bool afterActive = false; - for(i = 0; i < SS.group.n; i++) { - Group *g = &(SS.group.elem[i]); - char *s = g->DescriptionString(); - bool active = (g->h.v == SS.GW.activeGroup.v); - bool shown = g->visible; - bool ok = (g->solved.how == Group::SOLVED_OKAY); - bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); - Printf(false, "%Bp%Fd " - "%Fp%D%f%s%Ll%s%E%s " - "%Fp%D%f%Ll%s%E%Fh%s%E " - "%Fp%D%f%s%Ll%s%E " - "%Fl%Ll%D%f%s", - // Alternate between light and dark backgrounds, for readability - (i & 1) ? 'd' : 'a', - // Link that activates the group - active ? 's' : 'h', g->h.v, (&TextWindow::ScreenActivateGroup), - active ? "yes" : (ref ? " " : ""), - active ? "" : (ref ? "" : "no"), - active ? "" : " ", - // Link that hides or shows the group - shown ? 's' : 'h', g->h.v, (&TextWindow::ScreenToggleGroupShown), - afterActive ? "" : (shown ? "yes" : "no"), - afterActive ? " - " : (shown ? "" : " "), - // Link to the errors, if a problem occured while solving - ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved), - ok ? "ok" : "", - ok ? "" : "NO", - // Link to a screen that gives more details on the group - g->h.v, (&TextWindow::ScreenSelectGroup), s); - - if(active) afterActive = true; - } - - Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E", - &(TextWindow::ScreenShowGroupsSpecial), - &(TextWindow::ScreenShowGroupsSpecial)); - Printf(false, " %Fl%Ls%fconfiguration%E", - &(TextWindow::ScreenShowConfiguration)); -} - - -void TextWindow::ScreenHoverConstraint(int link, DWORD v) { - if(!SS.GW.showConstraints) return; - - hConstraint hc = { v }; - Constraint *c = SS.GetConstraint(hc); - if(c->group.v != SS.GW.activeGroup.v) { - // Only constraints in the active group are visible - return; - } - SS.GW.hover.Clear(); - SS.GW.hover.constraint = hc; - SS.GW.hover.emphasized = true; -} -void TextWindow::ScreenHoverRequest(int link, DWORD v) { - SS.GW.hover.Clear(); - hRequest hr = { v }; - SS.GW.hover.entity = hr.entity(0); - SS.GW.hover.emphasized = true; -} -void TextWindow::ScreenSelectConstraint(int link, DWORD v) { - SS.GW.ClearSelection(); - SS.GW.selection[0].constraint.v = v; -} -void TextWindow::ScreenSelectRequest(int link, DWORD v) { - hRequest hr = { v }; - SS.GW.ClearSelection(); - SS.GW.selection[0].entity = hr.entity(0); -} -void TextWindow::ScreenChangeOneOrTwoSides(int link, DWORD v) { - SS.UndoRemember(); - - Group *g = SS.GetGroup(SS.TW.shown->group); - if(g->subtype == Group::ONE_SIDED) { - g->subtype = Group::TWO_SIDED; - } else if(g->subtype == Group::TWO_SIDED) { - g->subtype = Group::ONE_SIDED; - } else oops(); - SS.MarkGroupDirty(g->h); - SS.GenerateAll(); - SS.GW.ClearSuper(); -} -void TextWindow::ScreenChangeSkipFirst(int link, DWORD v) { - SS.UndoRemember(); - - Group *g = SS.GetGroup(SS.TW.shown->group); - (g->skipFirst) = !(g->skipFirst); - SS.MarkGroupDirty(g->h); - SS.GenerateAll(); - SS.GW.ClearSuper(); -} -void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) { - SS.UndoRemember(); - - Group *g = SS.GetGroup(SS.TW.shown->group); - g->meshCombine = v; - SS.MarkGroupDirty(g->h); - SS.GenerateAll(); - SS.GW.ClearSuper(); -} -void TextWindow::ScreenChangeRightLeftHanded(int link, DWORD v) { - SS.UndoRemember(); - - Group *g = SS.GetGroup(SS.TW.shown->group); - if(g->subtype == Group::RIGHT_HANDED) { - g->subtype = Group::LEFT_HANDED; - } else { - g->subtype = Group::RIGHT_HANDED; - } - SS.MarkGroupDirty(g->h); - SS.GenerateAll(); - SS.GW.ClearSuper(); -} -void TextWindow::ScreenChangeHelixParameter(int link, DWORD v) { - Group *g = SS.GetGroup(SS.TW.shown->group); - char str[1024]; - int r; - if(link == 't') { - sprintf(str, "%.3f", g->valA); - SS.TW.edit.meaning = EDIT_HELIX_TURNS; - r = 12; - } else if(link == 'p') { - strcpy(str, SS.MmToString(g->valB)); - SS.TW.edit.meaning = EDIT_HELIX_PITCH; - r = 14; - } else if(link == 'r') { - strcpy(str, SS.MmToString(g->valC)); - SS.TW.edit.meaning = EDIT_HELIX_DRADIUS; - r = 16; - } else oops(); - SS.TW.edit.group.v = v; - ShowTextEditControl(r, 9, str); -} -void TextWindow::ScreenColor(int link, DWORD v) { - SS.UndoRemember(); - - Group *g = SS.GetGroup(SS.TW.shown->group); - if(v < 0 || v >= SS.MODEL_COLORS) return; - g->color = SS.modelColor[v]; - SS.MarkGroupDirty(g->h); - SS.GenerateAll(); - SS.GW.ClearSuper(); -} -void TextWindow::ScreenChangeExprA(int link, DWORD v) { - Group *g = SS.GetGroup(SS.TW.shown->group); - - // There's an extra line for the skipFirst parameter in one-sided groups. - int r = (g->subtype == Group::ONE_SIDED) ? 15 : 13; - - char str[1024]; - sprintf(str, "%d", (int)g->valA); - ShowTextEditControl(r, 9, str); - SS.TW.edit.meaning = EDIT_TIMES_REPEATED; - SS.TW.edit.group.v = v; -} -void TextWindow::ScreenChangeGroupName(int link, DWORD v) { - Group *g = SS.GetGroup(SS.TW.shown->group); - ShowTextEditControl(7, 14, g->DescriptionString()+5); - SS.TW.edit.meaning = EDIT_GROUP_NAME; - SS.TW.edit.group.v = v; -} -void TextWindow::ScreenDeleteGroup(int link, DWORD v) { - SS.UndoRemember(); - - hGroup hg = SS.TW.shown->group; - if(hg.v == SS.GW.activeGroup.v) { - Error("This group is currently active; activate a different group " - "before proceeding."); - return; - } - SS.group.RemoveById(SS.TW.shown->group); - // This is a major change, so let's re-solve everything. - SS.TW.ClearSuper(); - SS.GW.ClearSuper(); - SS.GenerateAll(0, INT_MAX); -} -void TextWindow::ShowGroupInfo(void) { - Group *g = SS.group.FindById(shown->group); - char *s, *s2, *s3; - - if(shown->group.v == Group::HGROUP_REFERENCES.v) { - Printf(true, "%FtGROUP %E%s", g->DescriptionString()); - } else { - Printf(true, "%FtGROUP %E%s " - "[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", - g->DescriptionString(), - g->h.v, &TextWindow::ScreenChangeGroupName, - g->h.v, &TextWindow::ScreenDeleteGroup); - } - - if(g->type == Group::EXTRUDE) { - s = "EXTRUDE "; - } else if(g->type == Group::TRANSLATE) { - s = "TRANSLATE"; - s2 ="REPEAT "; - s3 ="START "; - } else if(g->type == Group::ROTATE) { - s = "ROTATE "; - s2 ="REPEAT "; - s3 ="START "; - } - - if(g->type == Group::EXTRUDE || g->type == Group::ROTATE || - g->type == Group::TRANSLATE) - { - bool one = (g->subtype == Group::ONE_SIDED); - Printf(true, "%Ft%s%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", s, - &TextWindow::ScreenChangeOneOrTwoSides, - (one ? "" : "one side"), (one ? "one side" : ""), - &TextWindow::ScreenChangeOneOrTwoSides, - (!one ? "" : "two sides"), (!one ? "two sides" : "")); - } - - if(g->type == Group::LATHE) { - Printf(true, "%FtLATHE"); - } - - if(g->type == Group::SWEEP) { - Printf(true, "%FtSWEEP"); - } - - if(g->type == Group::HELICAL_SWEEP) { - bool rh = (g->subtype == Group::RIGHT_HANDED); - Printf(true, - "%FtHELICAL%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", - &ScreenChangeRightLeftHanded, - (rh ? "" : "right-hand"), (rh ? "right-hand" : ""), - &ScreenChangeRightLeftHanded, - (!rh ? "" : "left-hand"), (!rh ? "left-hand" : "")); - Printf(false, "%FtTHROUGH%E %@ turns %Fl%Lt%D%f[change]%E", - g->valA, g->h.v, &ScreenChangeHelixParameter); - Printf(false, "%FtPITCH%E %s axially per turn %Fl%Lp%D%f[change]%E", - SS.MmToString(g->valB), g->h.v, &ScreenChangeHelixParameter); - Printf(false, "%FtdRADIUS%E %s radially per turn %Fl%Lr%D%f[change]%E", - SS.MmToString(g->valC), g->h.v, &ScreenChangeHelixParameter); - } - - if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) { - bool space; - if(g->subtype == Group::ONE_SIDED) { - bool skip = g->skipFirst; - Printf(true, "%Ft%s%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", - s3, - &ScreenChangeSkipFirst, - (!skip ? "" : "with original"), (!skip ? "with original" : ""), - &ScreenChangeSkipFirst, - (skip ? "":"with copy #1"), (skip ? "with copy #1":"")); - space = false; - } else { - space = true; - } - - int times = (int)(g->valA); - Printf(space, "%Ft%s%E %d time%s %Fl%Ll%D%f[change]%E", - s2, times, times == 1 ? "" : "s", - g->h.v, &TextWindow::ScreenChangeExprA); - } - - if(g->type == Group::IMPORTED) { - Printf(true, "%FtIMPORT%E '%s'", g->impFile); - } - - if(g->type == Group::EXTRUDE || - g->type == Group::LATHE || - g->type == Group::SWEEP || - g->type == Group::HELICAL_SWEEP || - g->type == Group::IMPORTED) - { - bool un = (g->meshCombine == Group::COMBINE_AS_UNION); - bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE); - bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE); - bool asa = (g->type == Group::IMPORTED); - - Printf((g->type == Group::HELICAL_SWEEP), - "%FtMERGE AS%E %Fh%f%D%Ll%s%E%Fs%s%E / %Fh%f%D%Ll%s%E%Fs%s%E %s " - "%Fh%f%D%Ll%s%E%Fs%s%E", - &TextWindow::ScreenChangeMeshCombine, - Group::COMBINE_AS_UNION, - (un ? "" : "union"), (un ? "union" : ""), - &TextWindow::ScreenChangeMeshCombine, - Group::COMBINE_AS_DIFFERENCE, - (diff ? "" : "difference"), (diff ? "difference" : ""), - asa ? "/" : "", - &TextWindow::ScreenChangeMeshCombine, - Group::COMBINE_AS_ASSEMBLE, - (asy || !asa ? "" : "assemble"), (asy && asa ? "assemble" : "")); - } - if(g->type == Group::IMPORTED && g->meshError.yes) { - Printf(false, "%Fx the parts interfere!"); - } - - if(g->type == Group::EXTRUDE || - g->type == Group::LATHE || - g->type == Group::SWEEP || - g->type == Group::HELICAL_SWEEP) - { -#define TWOX(v) v v - Printf(true, "%FtM_COLOR%E " TWOX(TWOX(TWOX("%Bp%D%f%Ln %Bd%E "))), - 0x80000000 | SS.modelColor[0], 0, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[1], 1, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[2], 2, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[3], 3, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[4], 4, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[5], 5, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[6], 6, &TextWindow::ScreenColor, - 0x80000000 | SS.modelColor[7], 7, &TextWindow::ScreenColor); - } - - // Leave more space if the group has configuration stuff above the req/ - // constraint list (as all but the drawing groups do). - if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) { - Printf(true, "%Ftrequests in group"); - } else { - Printf(false, ""); - Printf(false, "%Ftrequests in group"); - } - - int i, a = 0; - for(i = 0; i < SS.request.n; i++) { - Request *r = &(SS.request.elem[i]); - - if(r->group.v == shown->group.v) { - char *s = r->DescriptionString(); - Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", - (a & 1) ? 'd' : 'a', - r->h.v, (&TextWindow::ScreenSelectRequest), - &(TextWindow::ScreenHoverRequest), s); - a++; - } - } - if(a == 0) Printf(false, "%Ba (none)"); - - a = 0; - Printf(true, "%Ftconstraints in group"); - for(i = 0; i < SS.constraint.n; i++) { - Constraint *c = &(SS.constraint.elem[i]); - - if(c->group.v == shown->group.v) { - char *s = c->DescriptionString(); - Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", - (a & 1) ? 'd' : 'a', - c->h.v, (&TextWindow::ScreenSelectConstraint), - (&TextWindow::ScreenHoverConstraint), s); - a++; - } - } - if(a == 0) Printf(false, "%Ba (none)"); -} - -void TextWindow::ShowGroupSolveInfo(void) { - Group *g = SS.group.FindById(shown->group); - if(g->solved.how == Group::SOLVED_OKAY) { - // Go back to the default group info screen - shown->screen = SCREEN_GROUP_INFO; - Show(); - return; - } - - Printf(true, "%FtGROUP %E%s", g->DescriptionString()); - switch(g->solved.how) { - case Group::DIDNT_CONVERGE: - Printf(true, " %FxSOLVE FAILED!%Fd no convergence"); - break; - - case Group::SINGULAR_JACOBIAN: { - Printf(true, "%FxSOLVE FAILED!%Fd inconsistent system"); - Printf(true, "remove any one of these to fix it"); - for(int i = 0; i < g->solved.remove.n; i++) { - hConstraint hc = g->solved.remove.elem[i]; - Constraint *c = SS.constraint.FindByIdNoOops(hc); - if(!c) continue; - - Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", - (i & 1) ? 'd' : 'a', - c->h.v, (&TextWindow::ScreenSelectConstraint), - (&TextWindow::ScreenHoverConstraint), - c->DescriptionString()); - } - break; - } - } -} - -void TextWindow::ScreenChangeLightDirection(int link, DWORD v) { - char str[1024]; - sprintf(str, "%.2f, %.2f, %.2f", CO(SS.lightDir[v])); - ShowTextEditControl(29+2*v, 8, str); - SS.TW.edit.meaning = EDIT_LIGHT_DIRECTION; - SS.TW.edit.i = v; -} -void TextWindow::ScreenChangeLightIntensity(int link, DWORD v) { - char str[1024]; - sprintf(str, "%.2f", SS.lightIntensity[v]); - ShowTextEditControl(29+2*v, 30, str); - SS.TW.edit.meaning = EDIT_LIGHT_INTENSITY; - SS.TW.edit.i = v; -} -void TextWindow::ScreenChangeColor(int link, DWORD v) { - char str[1024]; - sprintf(str, "%.2f, %.2f, %.2f", - REDf(SS.modelColor[v]), - GREENf(SS.modelColor[v]), - BLUEf(SS.modelColor[v])); - ShowTextEditControl(9+2*v, 12, str); - SS.TW.edit.meaning = EDIT_COLOR; - SS.TW.edit.i = v; -} -void TextWindow::ScreenChangeMeshTolerance(int link, DWORD v) { - char str[1024]; - sprintf(str, "%.2f", SS.meshTol); - ShowTextEditControl(37, 3, str); - SS.TW.edit.meaning = EDIT_MESH_TOLERANCE; -} -void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) { - char str[1024]; - sprintf(str, "%.3f", 1000*SS.cameraTangent); - ShowTextEditControl(43, 3, str); - SS.TW.edit.meaning = EDIT_CAMERA_TANGENT; -} -void TextWindow::ShowConfiguration(void) { - int i; - Printf(true, "%Ft material color-(r, g, b)"); - - for(i = 0; i < SS.MODEL_COLORS; i++) { - Printf(false, "%Bp #%d: %Bp %Bp (%@, %@, %@) %f%D%Ll%Fl[change]%E", - (i & 1) ? 'd' : 'a', - i, 0x80000000 | SS.modelColor[i], - (i & 1) ? 'd' : 'a', - REDf(SS.modelColor[i]), - GREENf(SS.modelColor[i]), - BLUEf(SS.modelColor[i]), - &ScreenChangeColor, i); - } - - Printf(false, ""); - Printf(false, "%Ft light direction intensity"); - for(i = 0; i < 2; i++) { - Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E " - "%2 %Fl%D%f%Ll[c]%E", - (i & 1) ? 'd' : 'a', i, - CO(SS.lightDir[i]), i, &ScreenChangeLightDirection, - SS.lightIntensity[i], i, &ScreenChangeLightIntensity); - } - - Printf(false, ""); - Printf(false, "%Ft mesh tolerance (smaller is finer)%E"); - Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles", - SS.meshTol, - &ScreenChangeMeshTolerance, 0, - SS.group.elem[SS.group.n-1].runningMesh.l.n); - - Printf(false, ""); - Printf(false, "%Ft perspective factor (0 for isometric)%E"); - Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E", - SS.cameraTangent*1000, - &ScreenChangeCameraTangent, 0); -} - -void TextWindow::EditControlDone(char *s) { - switch(edit.meaning) { - case EDIT_TIMES_REPEATED: { - Expr *e = Expr::From(s); - if(e) { - SS.UndoRemember(); - - Group *g = SS.GetGroup(edit.group); - g->valA = e->Eval(); - - SS.MarkGroupDirty(g->h); - SS.later.generateAll = true; - } else { - Error("Not a valid number or expression: '%s'", s); - } - break; - } - case EDIT_GROUP_NAME: { - char *t; - bool invalid = false; - for(t = s; *t; t++) { - if(!(isalnum(*t) || *t == '-' || *t == '_')) { - invalid = true; - } - } - if(invalid || !*s) { - Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); - } else { - SS.UndoRemember(); - - Group *g = SS.GetGroup(edit.group); - g->name.strcpy(s); - } - SS.unsaved = true; - break; - } - case EDIT_LIGHT_INTENSITY: - SS.lightIntensity[edit.i] = min(1, max(0, atof(s))); - InvalidateGraphics(); - break; - case EDIT_LIGHT_DIRECTION: { - double x, y, z; - if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) { - SS.lightDir[edit.i] = Vector::From(x, y, z); - } else { - Error("Bad format: specify coordinates as x, y, z"); - } - InvalidateGraphics(); - break; - } - case EDIT_COLOR: { - double r, g, b; - if(sscanf(s, "%lf, %lf, %lf", &r, &g, &b)==3) { - SS.modelColor[edit.i] = RGB(r*255, g*255, b*255); - } else { - Error("Bad format: specify color as r, g, b"); - } - break; - } - case EDIT_MESH_TOLERANCE: { - SS.meshTol = min(10, max(0.1, atof(s))); - SS.GenerateAll(0, INT_MAX); - break; - } - case EDIT_CAMERA_TANGENT: { - SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0; - InvalidateGraphics(); - break; - } - case EDIT_HELIX_TURNS: - case EDIT_HELIX_PITCH: - case EDIT_HELIX_DRADIUS: { - SS.UndoRemember(); - Group *g = SS.GetGroup(edit.group); - Expr *e = Expr::From(s); - if(!e) { - Error("Not a valid number or expression: '%s'", s); - break; - } - if(edit.meaning == EDIT_HELIX_TURNS) { - g->valA = min(30, fabs(e->Eval())); - } else if(edit.meaning == EDIT_HELIX_PITCH) { - g->valB = SS.ExprToMm(e); - } else { - g->valC = SS.ExprToMm(e); - } - SS.MarkGroupDirty(g->h); - SS.later.generateAll = true; - break; - } - } - SS.later.showTW = true; - HideTextEditControl(); - edit.meaning = EDIT_NOTHING; -} -