diff --git a/Makefile b/Makefile index 237bab72..10a07acc 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\drawconstraint.obj \ $(OBJDIR)\file.obj \ $(OBJDIR)\system.obj \ + $(OBJDIR)\polygon.obj \ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 83d5061e..ee879c1d 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -33,7 +33,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector gu = SS.GW.projUp; Vector gn = gr.Cross(gu); - glxColor(1, 0.2, 1); + glxColor3d(1, 0.2, 1); switch(type) { case PT_PT_DISTANCE: { Vector ap = SS.GetEntity(ptA)->PointGetCoords(); @@ -86,7 +86,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { for(int i = 0; i < 2; i++) { Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> PointGetCoords(); - glxColor(0.4, 0, 0.4); + glxColor3d(0.4, 0, 0.4); glBegin(GL_QUADS); glxVertex3v(p.Plus (r).Plus (d)); glxVertex3v(p.Plus (r).Minus(d)); diff --git a/dsc.h b/dsc.h index 86d5e3e3..f558ba02 100644 --- a/dsc.h +++ b/dsc.h @@ -33,6 +33,7 @@ public: static Vector MakeFrom(double x, double y, double z); + bool Equals(Vector v); Vector Plus(Vector b); Vector Minus(Vector b); Vector Negated(void); diff --git a/entity.cpp b/entity.cpp index 6e1dbc08..4df3d3e2 100644 --- a/entity.cpp +++ b/entity.cpp @@ -211,13 +211,31 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) { } } +void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) { + LineDrawOrGetDistance(a, b); + if(dogd.edges) { + SEdge edge; + edge.a = a; edge.b = b; + dogd.edges->l.Add(&edge); + } +} + void Entity::Draw(int order) { dogd.drawing = true; + dogd.edges = NULL; DrawOrGetDistance(order); } +void Entity::GenerateEdges(SEdgeList *el) { + dogd.drawing = false; + dogd.edges = el; + DrawOrGetDistance(-1); + dogd.edges = NULL; +} + double Entity::GetDistance(Point2d mp) { dogd.drawing = false; + dogd.edges = NULL; dogd.mp = mp; dogd.dmin = 1e12; @@ -227,7 +245,7 @@ double Entity::GetDistance(Point2d mp) { } void Entity::DrawOrGetDistance(int order) { - glxColor(1, 1, 1); + glxColor3d(1, 1, 1); switch(type) { case POINT_IN_3D: @@ -245,7 +263,7 @@ void Entity::DrawOrGetDistance(int order) { Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); - glxColor(0, 0.8, 0); + glxColor3d(0, 0.8, 0); glBegin(GL_QUADS); glxVertex3v(v.Plus (r).Plus (d)); glxVertex3v(v.Plus (r).Minus(d)); @@ -279,7 +297,7 @@ void Entity::DrawOrGetDistance(int order) { Vector mm = p.Minus(us).Minus(vs); Vector mp = p.Minus(us).Plus (vs); - glxColor(0, 0.4, 0.4); + glxColor3d(0, 0.4, 0.4); LineDrawOrGetDistance(pp, pm); LineDrawOrGetDistance(pm, mm); LineDrawOrGetDistance(mm, mp); @@ -299,7 +317,7 @@ void Entity::DrawOrGetDistance(int order) { if(order >= 0 && order != 1) break; Vector a = SS.GetEntity(assoc[0])->PointGetCoords(); Vector b = SS.GetEntity(assoc[1])->PointGetCoords(); - LineDrawOrGetDistance(a, b); + LineDrawOrGetDistanceOrEdge(a, b); break; } diff --git a/glhelper.cpp b/glhelper.cpp index 2f2ff444..57fd5684 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -60,7 +60,7 @@ void glxOntoCsys(Vector u, Vector v) void glxLockColorTo(double r, double g, double b) { ColorLocked = false; - glxColor(r, g, b); + glxColor3d(r, g, b); ColorLocked = true; } @@ -69,9 +69,45 @@ void glxUnlockColor(void) ColorLocked = false; } -void glxColor(double r, double g, double b) +void glxColor3d(double r, double g, double b) { - if(!ColorLocked) { - glColor3f((GLfloat)r, (GLfloat)g, (GLfloat)b); - } + if(!ColorLocked) glColor3d(r, g, b); } + +void glxColor4d(double r, double g, double b, double a) +{ + if(!ColorLocked) glColor4d(r, g, b, a); +} + +static void __stdcall Vertex(Vector *p) { + glxVertex3v(*p); +} +void glxFillPolygon(SPolygon *p) +{ + int i, j; + + GLUtesselator *gt = gluNewTess(); + typedef void __stdcall cf(void); + gluTessCallback(gt, GLU_TESS_BEGIN, (cf *)glBegin); + gluTessCallback(gt, GLU_TESS_END, (cf *)glEnd); + gluTessCallback(gt, GLU_TESS_VERTEX, (cf *)Vertex); + gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); + + gluTessBeginPolygon(gt, NULL); + for(i = 0; i < p->l.n; i++) { + SContour *sc = &(p->l.elem[i]); + gluTessBeginContour(gt); + for(j = 0; j < (sc->l.n-1); j++) { + SPoint *sp = &(sc->l.elem[j]); + double ap[3]; + ap[0] = sp->p.x; + ap[1] = sp->p.y; + ap[2] = sp->p.z; + gluTessVertex(gt, ap, &(sp->p)); + } + gluTessEndContour(gt); + } + gluTessEndPolygon(gt); + gluDeleteTess(gt); +} + diff --git a/graphicswin.cpp b/graphicswin.cpp index 4633ef87..1b58a952 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -280,7 +280,21 @@ void GraphicsWindow::MenuEdit(int id) { if(s->entity.v) { r = s->entity.request(); } - if(r.v && !r.IsFromReferences()) SS.request.Tag(r, 1); + if(r.v && !r.IsFromReferences()) { + SS.request.Tag(r, 1); + int j; + for(j = 0; j < SS.constraint.n; j++) { + Constraint *c = &(SS.constraint.elem[j]); + if(((c->ptA).request().v == r.v) || + ((c->ptB).request().v == r.v) || + ((c->ptC).request().v == r.v) || + ((c->entityA).request().v == r.v) || + ((c->entityB).request().v == r.v)) + { + SS.constraint.Tag(c->h, 1); + } + } + } if(s->constraint.v) { SS.constraint.Tag(s->constraint, 1); } @@ -561,19 +575,22 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { hRequest hr; switch(pendingOperation) { case MNU_DATUM_POINT: - ClearSelection(); hover.Clear(); - hr = AddRequest(Request::DATUM_POINT); SS.GetEntity(hr.entity(0))->PointForceTo(v); + ClearSelection(); hover.Clear(); + pendingOperation = 0; break; case MNU_LINE_SEGMENT: - ClearSelection(); hover.Clear(); - hr = AddRequest(Request::LINE_SEGMENT); SS.GetEntity(hr.entity(1))->PointForceTo(v); + if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { + Constraint::ConstrainCoincident(hover.entity, hr.entity(1)); + } + + ClearSelection(); hover.Clear(); pendingOperation = DRAGGING_NEW_LINE_POINT; pendingPoint = hr.entity(2); @@ -776,6 +793,11 @@ void GraphicsWindow::Paint(int w, int h) { 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 new file mode 100644 index 00000000..66144f7a --- /dev/null +++ b/polygon.cpp @@ -0,0 +1,78 @@ +#include "solvespace.h" + +bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt) { + dest->Clear(); + l.ClearTags(); + + for(;;) { + Vector first, last; + int i; + for(i = 0; i < l.n; i++) { + if(!l.elem[i].tag) { + first = l.elem[i].a; + last = l.elem[i].b; + l.elem[i].tag = 1; + break; + } + } + if(i >= l.n) { + return true; + } + + dest->AddEmptyContour(); + dest->AddPoint(first); + dest->AddPoint(last); + do { + for(i = 0; i < l.n; i++) { + SEdge *se = &(l.elem[i]); + if(se->tag) continue; + + if(se->a.Equals(last)) { + dest->AddPoint(se->b); + last = se->b; + se->tag = 1; + break; + } + if(se->b.Equals(last)) { + dest->AddPoint(se->a); + last = se->a; + se->tag = 1; + break; + } + } + if(i >= l.n) { + // Couldn't assemble a closed contour; mark where. + errorAt->a = first; + errorAt->b = last; + return false; + } + + } while(!last.Equals(first)); + } +} + +void SPolygon::Clear(void) { + int i; + for(i = 0; i < l.n; i++) { + (l.elem[i]).l.Clear(); + } + l.Clear(); +} + +void SPolygon::AddEmptyContour(void) { + SContour c; + memset(&c, 0, sizeof(c)); + l.Add(&c); +} + +void SPolygon::AddPoint(Vector p) { + if(l.n < 1) oops(); + + SPoint sp; + sp.tag = 0; + sp.p = p; + + // Add to the last contour in the list + (l.elem[l.n-1]).l.Add(&sp); +} + diff --git a/polygon.h b/polygon.h index c1355724..61eb740a 100644 --- a/polygon.h +++ b/polygon.h @@ -2,16 +2,40 @@ #ifndef __POLYGON_H #define __POLYGON_H +class SPolygon; + template class SList { public: T *elem; int n; int elemsAllocated; + + void Add(T *t) { + if(n >= elemsAllocated) { + elemsAllocated = (elemsAllocated + 32)*2; + elem = (T *)MemRealloc(elem, elemsAllocated*sizeof(elem[0])); + } + elem[n++] = *t; + } + + void ClearTags(void) { + int i; + for(i = 0; i < n; i++) { + elem[i].tag = 0; + } + } + + void Clear(void) { + if(elem) MemFree(elem); + elem = NULL; + n = elemsAllocated = 0; + } }; class SEdge { public: + int tag; Vector a, b; }; @@ -19,16 +43,27 @@ class SEdgeList { public: SList l; + bool AssemblePolygon(SPolygon *dest, SEdge *errorAt); +}; + +class SPoint { +public: + int tag; + Vector p; }; class SContour { public: - SList l; + SList l; }; class SPolygon { public: SList l; + + void AddEmptyContour(void); + void AddPoint(Vector p); + void Clear(void); }; class SPolyhedron { diff --git a/sketch.cpp b/sketch.cpp index 89b7f6f9..ff5077f7 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -17,6 +17,34 @@ char *Group::DescriptionString(void) { return ret; } +void Group::Draw(void) { + edges.l.Clear(); + int i; + for(i = 0; i < SS.entity.n; i++) { + Entity *e = &(SS.entity.elem[i]); + hRequest hr = e->h.request(); + if(SS.GetRequest(hr)->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.15); + glxFillPolygon(&poly); + } else { + glxColor4d(1, 0, 0, 0.3); + glLineWidth(10); + glBegin(GL_LINES); + glxVertex3v(error.a); + glxVertex3v(error.b); + glEnd(); + glLineWidth(1); + } + poly.Clear(); +} + hParam Request::AddParam(IdList *param, hParam hp) { Param pa; memset(&pa, 0, sizeof(pa)); diff --git a/sketch.h b/sketch.h index a7453979..a474e3ca 100644 --- a/sketch.h +++ b/sketch.h @@ -65,10 +65,15 @@ public: bool visible; + SEdgeList edges; + SPolygon poly; + NameStr name; char *DescriptionString(void); + void Draw(void); + SPolygon GetPolygon(void); }; @@ -170,14 +175,18 @@ public: // Routines to draw and hit-test the representation of the entity // on-screen. struct { - bool drawing; - Point2d mp; - double dmin; - } dogd; + bool drawing; + Point2d mp; + double dmin; + SEdgeList *edges; + } dogd; // state for drawing or getting distance (for hit testing) void LineDrawOrGetDistance(Vector a, Vector b); + void LineDrawOrGetDistanceOrEdge(Vector a, Vector b); void DrawOrGetDistance(int order); + void Draw(int order); double GetDistance(Point2d mp); + void GenerateEdges(SEdgeList *el); char *DescriptionString(void); }; @@ -253,9 +262,9 @@ public: static void MenuConstrain(int id); struct { - bool drawing; - Point2d mp; - double dmin; + bool drawing; + Point2d mp; + double dmin; } dogd; // state for drawing or getting distance (for hit testing) void LineDrawOrGetDistance(Vector a, Vector b); void DrawOrGetDistance(Vector *labelPos); diff --git a/solvespace.cpp b/solvespace.cpp index 0cb48228..ac696171 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -156,6 +156,8 @@ void SolveSpace::MenuFile(int id) { case GraphicsWindow::MNU_NEW: SS.NewFile(); SS.GenerateAll(); + SS.GW.Init(); + SS.TW.Init(); break; case GraphicsWindow::MNU_OPEN: diff --git a/solvespace.h b/solvespace.h index 410ffbd0..cd3f0d3f 100644 --- a/solvespace.h +++ b/solvespace.h @@ -64,12 +64,14 @@ void MemFree(void *p); // Utility functions that are provided in the platform-independent code. void glxVertex3v(Vector u); +void glxFillPolygon(SPolygon *p); void glxWriteText(char *str); void glxTranslatev(Vector u); void glxOntoCsys(Vector u, Vector v); void glxLockColorTo(double r, double g, double b); void glxUnlockColor(void); -void glxColor(double r, double g, double b); +void glxColor3d(double r, double g, double b); +void glxColor4d(double r, double g, double b, double a); #define arraylen(x) (sizeof((x))/sizeof((x)[0])) diff --git a/util.cpp b/util.cpp index 8cc4f614..8c5f7747 100644 --- a/util.cpp +++ b/util.cpp @@ -102,6 +102,13 @@ Vector Vector::MakeFrom(double x, double y, double z) { return v; } +bool Vector::Equals(Vector v) { + double tol = 0.1; + if(fabs(x - v.x) > tol) return false; + if(fabs(y - v.y) > tol) return false; + if(fabs(z - v.z) > tol) return false; + return true; +} Vector Vector::Plus(Vector b) { Vector r;