diff --git a/entity.cpp b/entity.cpp index 922f9cc..18c76b6 100644 --- a/entity.cpp +++ b/entity.cpp @@ -234,9 +234,14 @@ void Entity::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) { void Entity::LineDrawOrGetDistance(Vector a, Vector b) { if(dogd.drawing) { + // This fudge guarantees that the line will get drawn in front of + // anything else at the "same" depth in the z-buffer, so that it + // goes in front of the shaded stuff. + Vector n = SS.GW.projRight.Cross(SS.GW.projUp); + n = n.WithMagnitude(1.2/SS.GW.scale); glBegin(GL_LINE_STRIP); - glxVertex3v(a); - glxVertex3v(b); + glxVertex3v(a.Plus(n)); + glxVertex3v(b.Plus(n)); glEnd(); } else { Point2d ap = SS.GW.ProjectPoint(a); diff --git a/glhelper.cpp b/glhelper.cpp index 8c602ae..8cbf29e 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -133,6 +133,9 @@ void glxFillPolygon(SPolygon *p) gluTessCallback(gt, GLU_TESS_COMBINE, (cf *)Combine); gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); + Vector normal = p->Normal(); + glNormal3d(normal.x, normal.y, normal.z); + gluTessBeginPolygon(gt, NULL); for(i = 0; i < p->l.n; i++) { SContour *sc = &(p->l.elem[i]); diff --git a/graphicswin.cpp b/graphicswin.cpp index 5013b47..b79de78 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -108,8 +108,10 @@ void GraphicsWindow::Init(void) { showWorkplanes = true; showAxes = true; showPoints = true; - showAllGroups = true; showConstraints = true; + showSolids = true; + showHdnLines = false; + showSolids = true; solving = SOLVE_ALWAYS; @@ -443,6 +445,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, if(pendingOperation == DRAGGING_NEW_POINT || pendingOperation == DRAGGING_NEW_LINE_POINT) { + SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS); UpdateDraggedEntity(pendingPoint, x, y); HitTestMakeSelection(mp); } else if(pendingOperation == DRAGGING_NEW_CUBIC_POINT) { @@ -690,6 +693,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { break; } } + SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS); SS.TW.Show(); InvalidateGraphics(); @@ -777,6 +781,15 @@ void GraphicsWindow::ToggleAnyDatumShown(int link, DWORD v) { SS.TW.Show(); } +Vector GraphicsWindow::VectorFromProjs(double right, double up, double fwd) { + Vector n = projRight.Cross(projUp); + Vector r = offset.ScaledBy(-1); + r = r.Plus(projRight.ScaledBy(right)); + r = r.Plus(projUp.ScaledBy(up)); + r = r.Plus(n.ScaledBy(fwd)); + return r; +} + void GraphicsWindow::Paint(int w, int h) { havePainted = true; width = w; height = h; @@ -786,57 +799,73 @@ void GraphicsWindow::Paint(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glScaled(scale*2.0/w, scale*2.0/h, 0); + glScaled(scale*2.0/w, scale*2.0/h, scale*2.0/w); double tx = projRight.Dot(offset); double ty = projUp.Dot(offset); + Vector n = projUp.Cross(projRight); + double tz = n.Dot(offset); double mat[16]; MakeMatrix(mat, projRight.x, projRight.y, projRight.z, tx, projUp.x, projUp.y, projUp.z, ty, - 0, 0, 0, 0, + n.x, n.y, n.z, tz, 0, 0, 0, 1); glMultMatrixd(mat); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glShadeModel(GL_SMOOTH); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glEnable(GL_DEPTH_TEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glEnable(GL_NORMALIZE); - glClearIndex((GLfloat)0); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + Vector light = VectorFromProjs(-0.49*w/scale, 0.49*h/scale, 0); + GLfloat lightPos[4] = + { (GLfloat)light.x, (GLfloat)light.y, (GLfloat)light.z, 0 }; + glLightfv(GL_LIGHT0, GL_POSITION, lightPos); + glEnable(GL_LIGHT0); + + glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1); + GLfloat ambient[4] = { 0.4f, 0.4f, 0.4f, 1.0f }; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); + + glxUnlockColor(); int i, a; + // Draw the groups; this fills the polygons, if requested. + if(showSolids) { + for(i = 0; i < SS.group.n; i++) { + SS.group.elem[i].Draw(); + } + } // First, draw the entire scene. We don't necessarily want to draw // things with normal z-buffering behaviour; e.g. we always want to // draw a line segment in front of a reference. So we have three draw // levels, and only the first gets normal depth testing. - glxUnlockColor(); for(a = 0; a <= 2; a++) { // Three levels: 0 least prominent (e.g. a reference workplane), 1 is // middle (e.g. line segment), 2 is always in front (e.g. point). - if(a == 1) glDisable(GL_DEPTH_TEST); + if(a >= 1 && showHdnLines) glDisable(GL_DEPTH_TEST); for(i = 0; i < SS.entity.n; i++) { SS.entity.elem[i].Draw(a); } } + glDisable(GL_DEPTH_TEST); // Draw the constraints for(i = 0; i < SS.constraint.n; i++) { SS.constraint.elem[i].Draw(); } - // Draw the groups; this fills the polygons, if requested. - for(i = 0; i < SS.group.n; i++) { - SS.group.elem[i].Draw(); - } - // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); glxLockColorTo(1, 1, 0); diff --git a/polygon.cpp b/polygon.cpp index 66144f7..f6fbf4e 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -76,3 +76,45 @@ void SPolygon::AddPoint(Vector p) { (l.elem[l.n-1]).l.Add(&sp); } +void SPolygon::MakeEdgesInto(SEdgeList *el) { + int i; + for(i = 0; i < l.n; i++) { + (l.elem[i]).MakeEdgesInto(el); + } +} + +Vector SPolygon::Normal(void) { + if(l.n < 1) return Vector::MakeFrom(0, 0, 0); + return (l.elem[0]).Normal(); +} + +void SContour::MakeEdgesInto(SEdgeList *el) { + int i; + for(i = 0; i < (l.n-1); i++) { + SEdge e; + e.a = l.elem[i].p; + e.b = l.elem[i+1].p; + el->l.Add(&e); + } +} + +Vector SContour::Normal(void) { + if(l.n < 3) return Vector::MakeFrom(0, 0, 0); + + Vector u = (l.elem[0].p).Minus(l.elem[1].p); + + Vector v; + double dot = 2; + // Find the edge in the contour that's closest to perpendicular to the + // first edge, since that will give best numerical stability. + for(int i = 1; i < (l.n-1); i++) { + Vector vt = (l.elem[i].p).Minus(l.elem[i+1].p); + double dott = fabs(vt.Dot(u)/(u.Magnitude()*vt.Magnitude())); + if(dott < dot) { + dot = dott; + v = vt; + } + } + return (u.Cross(v)).WithMagnitude(1); +} + diff --git a/polygon.h b/polygon.h index 61eb740..5043a2d 100644 --- a/polygon.h +++ b/polygon.h @@ -55,14 +55,19 @@ public: class SContour { public: SList l; + + void MakeEdgesInto(SEdgeList *el); + Vector Normal(void); }; class SPolygon { public: SList l; + Vector Normal(void); void AddEmptyContour(void); void AddPoint(Vector p); + void MakeEdgesInto(SEdgeList *el); void Clear(void); }; diff --git a/sketch.cpp b/sketch.cpp index 46ee226..d51abbe 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -134,39 +134,98 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz) { SS.entity.Add(&en); } +void Group::MakePolygons(void) { + faces.Clear(); + if(type == DRAWING) { + edges.l.Clear(); + int i; + for(i = 0; i < SS.entity.n; i++) { + Entity *e = &(SS.entity.elem[i]); + if(e->group.v != h.v) continue; + + e->GenerateEdges(&edges); + } + SPolygon poly; + memset(&poly, 0, sizeof(poly)); + SEdge error; + if(edges.AssemblePolygon(&poly, &error)) { + polyError.yes = false; + faces.Add(&poly); + } else { + polyError.yes = true; + polyError.notClosedAt = error; + poly.Clear(); + } + } else if(type == EXTRUDE) { + Vector translate; + translate.x = SS.GetParam(h.param(0))->val; + translate.y = SS.GetParam(h.param(1))->val; + translate.z = SS.GetParam(h.param(2))->val; + + edges.l.Clear(); + Group *src = SS.GetGroup(opA); + if(src->faces.n != 1) return; + + (src->faces.elem[0]).MakeEdgesInto(&edges); + + SPolygon poly; + SEdge error; + + // The bottom + memset(&poly, 0, sizeof(poly)); + if(!edges.AssemblePolygon(&poly, &error)) oops(); + faces.Add(&poly); + + // The sides + int i; + for(i = 0; i < edges.l.n; i++) { + SEdge *edge = &(edges.l.elem[i]); + memset(&poly, 0, sizeof(poly)); + poly.AddEmptyContour(); + poly.AddPoint(edge->a); + poly.AddPoint(edge->b); + poly.AddPoint((edge->b).Plus(translate)); + poly.AddPoint((edge->a).Plus(translate)); + poly.AddPoint(edge->a); + faces.Add(&poly); + edge->a = (edge->a).Plus(translate); + edge->b = (edge->b).Plus(translate); + } + + // The top + memset(&poly, 0, sizeof(poly)); + if(!edges.AssemblePolygon(&poly, &error)) oops(); + faces.Add(&poly); + } +} + void Group::Draw(void) { if(!visible) return; - edges.l.Clear(); - int i; - for(i = 0; i < SS.entity.n; i++) { - Entity *e = &(SS.entity.elem[i]); - if(e->group.v != h.v) continue; - - e->GenerateEdges(&edges); - } - SPolygon poly; - memset(&poly, 0, sizeof(poly)); - SEdge error; - if(edges.AssemblePolygon(&poly, &error)) { - glxColor4d(0, 0, 1, 0.1); - glxFillPolygon(&poly); - } else { + if(polyError.yes) { glxColor4d(1, 0, 0, 0.2); glLineWidth(10); glBegin(GL_LINES); - glxVertex3v(error.a); - glxVertex3v(error.b); + glxVertex3v(polyError.notClosedAt.a); + glxVertex3v(polyError.notClosedAt.b); glEnd(); glLineWidth(1); glxColor3d(1, 0, 0); glPushMatrix(); - glxTranslatev(error.b); + glxTranslatev(polyError.notClosedAt.b); glxOntoWorkplane(SS.GW.projRight, SS.GW.projUp); glxWriteText("not closed contour!"); glPopMatrix(); + } else { + int i; + glEnable(GL_LIGHTING); + GLfloat vec[] = { 0, 0, 0.5, 1.0 }; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec); + for(i = 0; i < faces.n; i++) { + glxFillPolygon(&(faces.elem[i])); + } + glDisable(GL_LIGHTING); } - poly.Clear(); } hParam Request::AddParam(IdList *param, hParam hp) { diff --git a/sketch.h b/sketch.h index b40ffb6..9c7b07c 100644 --- a/sketch.h +++ b/sketch.h @@ -85,8 +85,12 @@ public: hGroup opB; bool visible; - SEdgeList edges; - SPolygon poly; + SEdgeList edges; + SList faces; + struct { + SEdge notClosedAt; + bool yes; + } polyError; NameStr name; char *DescriptionString(void); @@ -101,6 +105,7 @@ public: hEntity Remap(hEntity in, int copyNumber); void CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz); + void MakePolygons(void); void Draw(void); SPolygon GetPolygon(void); diff --git a/solvespace.cpp b/solvespace.cpp index 927c722..4f203e3 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -60,6 +60,8 @@ void SolveSpace::GenerateAll(bool andSolve) { // Solve this group. if(andSolve) SolveGroup(g->h); } + + g->MakePolygons(); } prev.Clear(); diff --git a/textwin.cpp b/textwin.cpp index 90c1320..21b9ae0 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -217,17 +217,14 @@ void TextWindow::ShowHeader(void) { SS.GW.EnsureValidActives(); + char *cd = (SS.GW.activeWorkplane.v == Entity::FREE_IN_3D.v) ? + "free in 3d" : + SS.GetEntity(SS.GW.activeWorkplane)->DescriptionString(); + + // Navigation buttons if(SS.GW.pendingDescription) { - Printf(false, " %Bt%Ft group:%s", - SS.group.FindById(SS.GW.activeGroup)->DescriptionString()); + Printf(false, " %Bt%Ft workplane:%Fd %s", cd); } else { - // Navigation buttons - char *cd; - if(SS.GW.activeWorkplane.v == Entity::FREE_IN_3D.v) { - cd = "free in 3d"; - } else { - cd = SS.GetEntity(SS.GW.activeWorkplane)->DescriptionString(); - } Printf(false, " %Lb%f<<%E %Lh%fhome%E %Bt%Ft workplane:%Fd %s", (&TextWindow::ScreenNavigation), (&TextWindow::ScreenNavigation), @@ -255,10 +252,12 @@ void TextWindow::ShowHeader(void) { datumColor, &(SS.GW.ToggleAnyDatumShown) ); Printf(false, "%Bt%Ft " - "%Fp%Ll%D%fall-groups%E " - "%Fp%Ll%D%fconstraints%E", -hs(SS.GW.showAllGroups), (DWORD)(&SS.GW.showAllGroups), &(SS.GW.ToggleBool), -hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints), &(SS.GW.ToggleBool) + "%Fp%Ll%D%fconstraints%E " + "%Fp%Ll%D%fsolids%E " + "%Fp%Ll%D%fhidden-lines%E", +hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints), &(SS.GW.ToggleBool), +hs(SS.GW.showSolids), (DWORD)(&SS.GW.showSolids), &(SS.GW.ToggleBool), +hs(SS.GW.showHdnLines), (DWORD)(&SS.GW.showHdnLines), &(SS.GW.ToggleBool) ); } diff --git a/ui.h b/ui.h index 88b26f7..5e949a5 100644 --- a/ui.h +++ b/ui.h @@ -155,6 +155,7 @@ public: void NormalizeProjectionVectors(void); Point2d ProjectPoint(Vector p); void AnimateOnto(Quaternion quatf, Vector offsetf); + Vector VectorFromProjs(double right, double up, double forward); typedef enum { UNIT_MM = 0, @@ -215,9 +216,10 @@ public: bool showWorkplanes; bool showAxes; bool showPoints; - bool showAllGroups; bool showConstraints; bool showTextWindow; + bool showSolids; + bool showHdnLines; static void ToggleBool(int link, DWORD v); static void ToggleAnyDatumShown(int link, DWORD v);