diff --git a/constraint.cpp b/constraint.cpp index cec5f11..d923092 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -18,12 +18,23 @@ void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { memset(&c, 0, sizeof(c)); c.group = SS.GW.activeGroup; c.workplane = SS.GW.activeWorkplane; - c.type = Constraint::POINTS_COINCIDENT; + c.type = POINTS_COINCIDENT; c.ptA = ptA; c.ptB = ptB; AddConstraint(&c); } +void Constraint::ConstrainHorizVert(bool horiz, hEntity ls) { + Constraint c; + memset(&c, 0, sizeof(c)); + c.group = SS.GW.activeGroup; + c.workplane = SS.GW.activeWorkplane; + if(c.workplane.v == Entity::FREE_IN_3D.v) oops(); + c.type = (horiz ? HORIZONTAL : VERTICAL); + c.entityA = ls; + AddConstraint(&c); +} + void Constraint::MenuConstrain(int id) { Constraint c; memset(&c, 0, sizeof(c)); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index fe053c5..cb0bc1f 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -31,6 +31,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { // If the group is hidden, then the constraints are hidden and not // able to be selected. if(!(g->visible)) return; + // And likewise if the group is not the active group. + if(g->h.v != SS.GW.activeGroup.v) return; // Unit vectors that describe our current view of the scene. One pixel // long, not one actual unit. diff --git a/dsc.h b/dsc.h index 234cf01..2c2c6fd 100644 --- a/dsc.h +++ b/dsc.h @@ -159,7 +159,7 @@ public: void Clear(void) { elemsAllocated = n = 0; - if(elem) free(elem); + if(elem) MemFree(elem); elem = NULL; } diff --git a/entity.cpp b/entity.cpp index b70921c..dfb5adc 100644 --- a/entity.cpp +++ b/entity.cpp @@ -288,7 +288,7 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) { void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) { LineDrawOrGetDistance(a, b); - if(dogd.edges) { + if(dogd.edges && !construction) { SEdge edge; edge.a = a; edge.b = b; dogd.edges->l.Add(&edge); @@ -325,7 +325,13 @@ void Entity::DrawOrGetDistance(int order) { // contribute a distance for the selection, but it still generates edges. if(!(g->visible) && !dogd.edges) return; - glxColor3d(1, 1, 1); + if(group.v != SS.GW.activeGroup.v) { + glxColor3d(0.5, 0.3, 0.0); + } else if(construction) { + glxColor3d(0.1, 0.7, 0.1); + } else { + glxColor3d(1, 1, 1); + } switch(type) { case POINT_XFRMD: diff --git a/file.cpp b/file.cpp index cda4037..e3a3884 100644 --- a/file.cpp +++ b/file.cpp @@ -67,6 +67,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) }, { 'e', "Entity.type", 'd', &(SS.sv.e.type) }, { 'e', "Entity.group.v", 'x', &(SS.sv.e.group.v) }, + { 'e', "Entity.construction", 'b', &(SS.sv.e.construction) }, { 'e', "Entity.param[0].v", 'x', &(SS.sv.e.param[0].v) }, { 'e', "Entity.param[1].v", 'x', &(SS.sv.e.param[1].v) }, { 'e', "Entity.param[2].v", 'x', &(SS.sv.e.param[2].v) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index e4f65d4..939e347 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -65,7 +65,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "Sym&bolic Variable\tB", 0, 'B', mReq }, { 1, "&Import From File...\tI", 0, 'I', mReq }, { 1, NULL, 0, NULL }, -{ 1, "To&ggle Construction\tG", 0, 'G', NULL }, +{ 1, "To&ggle Construction\tG", MNU_CONSTRUCTION, 'G', mReq }, { 0, "&Constrain", 0, NULL }, { 1, "&Distance / Diameter\tShift+D", MNU_DISTANCE_DIA, 'D'|S, mCon }, @@ -346,12 +346,27 @@ void GraphicsWindow::MenuRequest(int id) { case MNU_CUBIC: s = "click first point of cubic segment"; goto c; case MNU_CIRCLE: s = "click center of circle"; goto c; case MNU_WORKPLANE: s = "click origin of workplane"; goto c; + case MNU_RECTANGLE: s = "click one corner of rectangular"; goto c; c: SS.GW.pending.operation = id; SS.GW.pending.description = s; SS.TW.Show(); break; + case MNU_CONSTRUCTION: { + SS.GW.GroupSelection(); + int i; + for(i = 0; i < SS.GW.gs.entities; i++) { + hEntity he = SS.GW.gs.entity[i]; + if(!he.isFromRequest()) continue; + Request *r = SS.GetRequest(he.request()); + r->construction = !(r->construction); + } + SS.GW.ClearSelection(); + SS.GenerateAll(SS.GW.solving == GraphicsWindow::SOLVE_ALWAYS); + break; + } + default: oops(); } } @@ -687,6 +702,27 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(pending.point)->PointForceTo(v); break; + case MNU_RECTANGLE: { + hRequest lns[4]; + int i; + for(i = 0; i < 4; i++) { + lns[i] = AddRequest(Request::LINE_SEGMENT); + } + for(i = 0; i < 4; i++) { + Constraint::ConstrainCoincident( + lns[i].entity(1), lns[(i+1)%4].entity(2)); + SS.GetEntity(lns[i].entity(1))->PointForceTo(v); + SS.GetEntity(lns[i].entity(2))->PointForceTo(v); + } + for(i = 0; i < 4; i++) { + Constraint::ConstrainHorizVert((i % 2)==0, lns[i].entity(0)); + } + + pending.operation = DRAGGING_NEW_POINT; + pending.point = lns[1].entity(2); + pending.description = "click to place other corner of rectangle"; + break; + } case MNU_CIRCLE: hr = AddRequest(Request::CIRCLE); SS.GetEntity(hr.entity(1))->PointForceTo(v); diff --git a/sketch.cpp b/sketch.cpp index 6a8708e..9fd7083 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -55,15 +55,17 @@ char *Group::DescriptionString(void) { void Group::Generate(IdList *entity, IdList *param) { + Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); + gn = gn.WithMagnitude(200/SS.GW.scale); int i; switch(type) { case DRAWING: return; case EXTRUDE: - AddParam(param, h.param(0), 50); - AddParam(param, h.param(1), 50); - AddParam(param, h.param(2), 50); + AddParam(param, h.param(0), gn.x); + AddParam(param, h.param(1), gn.y); + AddParam(param, h.param(2), gn.z); for(i = 0; i < entity->n; i++) { Entity *e = &(entity->elem[i]); if(e->group.v != opA.v) continue; @@ -138,8 +140,12 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, if(isExtrusion) { if(a != 0) oops(); SS.entity.Add(&en); + + hEntity np = en.h; + memset(&en, 0, sizeof(en)); en.point[0] = ep->h; - en.point[1] = en.h; + en.point[1] = np; + en.group = h; en.h = Remap(ep->h, 1); en.type = Entity::LINE_SEGMENT; // And then this line segment gets added @@ -160,6 +166,10 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, } void Group::MakePolygons(void) { + int i; + for(i = 0; i < faces.n; i++) { + (faces.elem[i]).Clear(); + } faces.Clear(); if(type == DRAWING) { edges.l.Clear(); @@ -332,6 +342,7 @@ void Request::Generate(IdList *entity, e.type = et; e.group = group; e.workplane = workplane; + e.construction = construction; e.h = h.entity(0); // And generate entities for the points diff --git a/sketch.h b/sketch.h index 23312ae..17e1405 100644 --- a/sketch.h +++ b/sketch.h @@ -188,10 +188,15 @@ public: hGroup group; hEntity workplane; // or Entity::FREE_IN_3D + bool construction; + // For entities that are derived by a transformation, the number of // times to apply the transformation. int timesApplied; + bool HasDirection(void); + ExprVector GetDirection(void); + bool IsWorkplane(void); // The plane is points P such that P dot (xn, yn, zn) - d = 0 void WorkplaneGetPlaneExprs(ExprVector *n, Expr **d); @@ -349,6 +354,7 @@ public: static ExprVector PointInThreeSpace(hEntity workplane, Expr *u, Expr *v); static void ConstrainCoincident(hEntity ptA, hEntity ptB); + static void ConstrainHorizVert(bool horiz, hEntity lineSegment); }; class hEquation { diff --git a/solvespace.h b/solvespace.h index 381bd11..d195676 100644 --- a/solvespace.h +++ b/solvespace.h @@ -55,6 +55,7 @@ void FreeAllTemporary(void); void *MemRealloc(void *p, int n); void *MemAlloc(int n); void MemFree(void *p); +void vl(void); // debug function to validate #include "dsc.h" diff --git a/ui.h b/ui.h index 42c4d82..63ceacb 100644 --- a/ui.h +++ b/ui.h @@ -107,6 +107,7 @@ public: MNU_CIRCLE, MNU_RECTANGLE, MNU_CUBIC, + MNU_CONSTRUCTION, // Group MNU_GROUP_DRAWING, MNU_GROUP_EXTRUDE, diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 2a29742..45ee175 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -62,23 +62,48 @@ void Error(char *str, ...) // to be sloppy with our memory management, and just free everything at once // at the end. //----------------------------------------------------------------------------- -static HANDLE Heap; +static HANDLE Temp; void *AllocTemporary(int n) { - Expr *v = (Expr *)HeapAlloc(Heap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n); + void *v = HeapAlloc(Temp, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n); if(!v) oops(); - memset(v, 0, n); return v; } void FreeAllTemporary(void) { - if(Heap) HeapDestroy(Heap); - Heap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0); + if(Temp) HeapDestroy(Temp); + Temp = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0); + // This is a good place to validate, because it gets called fairly + // often. + vl(); } -void *MemRealloc(void *p, int n) { return realloc(p, n); } -void *MemAlloc(int n) { return malloc(n); } -void MemFree(void *p) { free(p); } +static HANDLE Perm; +void *MemRealloc(void *p, int n) { + if(!p) { + return MemAlloc(n); + } + + p = HeapReAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, p, n); + vl(); + if(!p) oops(); + return p; +} +void *MemAlloc(int n) { + void *p = HeapAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n); + vl(); + if(!p) oops(); + return p; +} +void MemFree(void *p) { + HeapFree(Perm, HEAP_NO_SERIALIZE, p); + vl(); +} + +void vl(void) { + if(!HeapValidate(Temp, HEAP_NO_SERIALIZE, NULL)) oops(); + if(!HeapValidate(Perm, HEAP_NO_SERIALIZE, NULL)) oops(); +} static void PaintTextWnd(HDC hdc) { @@ -750,6 +775,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, ThawWindowPos(TextWnd); ThawWindowPos(GraphicsWnd); + // Create the heap used for long-lived stuff (that gets freed piecewise). + Perm = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0); // Create the heap that we use to store Exprs and other temp stuff. FreeAllTemporary();