From e2263c69c8164a218cc42115a5aecde3331aacba Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sun, 11 May 2008 02:40:37 -0800 Subject: [PATCH] Find a memory corruption! I was getting a pointer into the entity list, and then adding a new entity to that list, and then looking at that pointer again. Not okay; the add operation might have forced a realloc. I have to watch for that. And add a "distance ratio" constraint, plus a new kind of group that comes with its own workplane. The workplane is not solved for; it's generated explicitly in terms of elements that are already solved. [git-p4: depot-paths = "//depot/solvespace/": change = 1716] --- constraint.cpp | 27 +++++++++- drawconstraint.cpp | 13 +++-- dsc.h | 1 + entity.cpp | 44 ++++++++++++++-- file.cpp | 128 +++++++++++++++++++++++++-------------------- graphicswin.cpp | 10 ++-- sketch.cpp | 108 ++++++++++++++++++++++++++++++++++++-- sketch.h | 23 +++++++- solvespace.h | 2 + system.cpp | 12 +++-- ui.h | 1 + util.cpp | 12 +++++ 12 files changed, 301 insertions(+), 80 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 5ed2fc2..5f791b4 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -69,7 +69,8 @@ void Constraint::MenuConstrain(int id) { Vector n = SS.GW.projRight.Cross(SS.GW.projUp); Vector a = SS.GetEntity(c.ptA)->PointGetNum(); Vector b = SS.GetEntity(c.ptB)->PointGetNum(); - c.disp.offset = n.Cross(a.Minus(b)).WithMagnitude(50); + c.disp.offset = n.Cross(a.Minus(b)); + c.disp.offset = (c.disp.offset).WithMagnitude(50/SS.GW.scale); } else { c.disp.offset = Vector::MakeFrom(0, 0, 0); } @@ -116,6 +117,21 @@ void Constraint::MenuConstrain(int id) { AddConstraint(&c); break; + case GraphicsWindow::MNU_RATIO: + if(gs.lineSegments == 2 && gs.n == 2) { + c.type = LENGTH_RATIO; + c.entityA = gs.entity[0]; + c.entityB = gs.entity[1]; + } else { + Error("Bad selection for length ratio constraint."); + return; + } + + c.exprA = Expr::FromString("0")->DeepCopyKeep(); + c.ModifyToSatisfy(); + AddConstraint(&c); + break; + case GraphicsWindow::MNU_AT_MIDPOINT: if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) { c.type = AT_MIDPOINT; @@ -379,6 +395,15 @@ void Constraint::Generate(IdList *l) { break; } + case LENGTH_RATIO: { + Entity *a = SS.GetEntity(entityA); + Entity *b = SS.GetEntity(entityB); + Expr *la = Distance(workplane, a->point[0], a->point[1]); + Expr *lb = Distance(workplane, b->point[0], b->point[1]); + AddEq(l, (la->Div(lb))->Minus(exprA), 0); + break; + } + case DIAMETER: { Entity *circle = SS.GetEntity(entityA); Expr *r = (SS.GetEntity(circle->distance))->DistanceGetExpr(); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 849e29d..ccba8af 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -6,6 +6,7 @@ bool Constraint::HasLabel(void) { case PT_PLANE_DISTANCE: case PT_PT_DISTANCE: case DIAMETER: + case LENGTH_RATIO: return true; default: @@ -249,7 +250,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { case PARALLEL: { for(int i = 0; i < 2; i++) { Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); - Vector n = e->VectorGetExprs().Eval(); + Vector n = e->VectorGetNum(); n = n.WithMagnitude(25/SS.GW.scale); Vector u = (gn.Cross(n)).WithMagnitude(4/SS.GW.scale); Vector p = e->VectorGetRefPoint(); @@ -260,17 +261,23 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case LENGTH_RATIO: case EQUAL_LENGTH_LINES: { + Vector a, b; for(int i = 0; i < 2; i++) { Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); - Vector a = SS.GetEntity(e->point[0])->PointGetNum(); - Vector b = SS.GetEntity(e->point[1])->PointGetNum(); + a = SS.GetEntity(e->point[0])->PointGetNum(); + b = SS.GetEntity(e->point[1])->PointGetNum(); Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3)); Vector ab = a.Minus(b); Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale); LineDrawOrGetDistance(m.Minus(n), m.Plus(n)); } + if(type == LENGTH_RATIO) { + Vector ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset); + DoLabel(ref, labelPos, gr, gu); + } break; } diff --git a/dsc.h b/dsc.h index ed10832..eabb57b 100644 --- a/dsc.h +++ b/dsc.h @@ -52,6 +52,7 @@ public: Vector ScaledBy(double s); Vector ProjectInto(hEntity wrkpl); double DivPivoting(Vector delta); + Vector ClosestOrtho(void); }; class Point2d { diff --git a/entity.cpp b/entity.cpp index 75bf51e..4306341 100644 --- a/entity.cpp +++ b/entity.cpp @@ -1,8 +1,13 @@ #include "solvespace.h" char *Entity::DescriptionString(void) { - Request *r = SS.GetRequest(h.request()); - return r->DescriptionString(); + if(h.isFromRequest()) { + Request *r = SS.GetRequest(h.request()); + return r->DescriptionString(); + } else { + Group *g = SS.GetGroup(h.group()); + return g->DescriptionString(); + } } bool Entity::HasVector(void) { @@ -35,6 +40,22 @@ ExprVector Entity::VectorGetExprs(void) { } } +Vector Entity::VectorGetNum(void) { + switch(type) { + case LINE_SEGMENT: + return (SS.GetEntity(point[0])->PointGetNum()).Minus( + SS.GetEntity(point[1])->PointGetNum()); + + case NORMAL_IN_3D: + case NORMAL_IN_2D: + case NORMAL_N_COPY: + case NORMAL_N_ROT: + return NormalN(); + + default: oops(); + } +} + Vector Entity::VectorGetRefPoint(void) { switch(type) { case LINE_SEGMENT: @@ -111,6 +132,7 @@ bool Entity::IsPoint(void) { switch(type) { case POINT_IN_3D: case POINT_IN_2D: + case POINT_N_COPY: case POINT_N_TRANS: case POINT_N_ROT_TRANS: return true; @@ -289,6 +311,10 @@ void Entity::PointForceTo(Vector p) { break; } + case POINT_N_COPY: + // Nothing to do; it's a static copy + break; + default: oops(); } } @@ -330,6 +356,11 @@ Vector Entity::PointGetNum(void) { p = p.Plus(offset); break; } + + case POINT_N_COPY: + p = numPoint; + break; + default: oops(); } return p; @@ -383,6 +414,12 @@ ExprVector Entity::PointGetExprs(void) { r = orig.Plus(trans); break; } + case POINT_N_COPY: + r.x = Expr::FromConstant(numPoint.x); + r.y = Expr::FromConstant(numPoint.y); + r.z = Expr::FromConstant(numPoint.z); + break; + default: oops(); } return r; @@ -498,6 +535,7 @@ void Entity::DrawOrGetDistance(int order) { } switch(type) { + case POINT_N_COPY: case POINT_N_TRANS: case POINT_N_ROT_TRANS: case POINT_IN_3D: @@ -563,7 +601,7 @@ void Entity::DrawOrGetDistance(int order) { Vector tip = tail.Plus(v); LineDrawOrGetDistance(tail, tip); - v = v.WithMagnitude(12); + v = v.WithMagnitude(12/SS.GW.scale); Vector axis = q.RotationV(); LineDrawOrGetDistance(tip, tip.Minus(v.RotatedAbout(axis, 0.6))); LineDrawOrGetDistance(tip, tip.Minus(v.RotatedAbout(axis, -0.6))); diff --git a/file.cpp b/file.cpp index bc596f9..6015020 100644 --- a/file.cpp +++ b/file.cpp @@ -13,7 +13,7 @@ void SolveSpace::NewFile(void) { memset(&g, 0, sizeof(g)); g.visible = true; g.name.strcpy("#references"); - g.type = Group::DRAWING; + g.type = Group::DRAWING_3D; g.h = Group::HGROUP_REFERENCES; group.Add(&g); @@ -45,66 +45,76 @@ void SolveSpace::NewFile(void) { const SolveSpace::SaveTable SolveSpace::SAVED[] = { - { 'g', "Group.h.v", 'x', &(SS.sv.g.h.v) }, - { 'g', "Group.type", 'd', &(SS.sv.g.type) }, - { 'g', "Group.name", 'N', &(SS.sv.g.name) }, - { 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) }, - { 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) }, - { 'g', "Group.solveOrder", 'd', &(SS.sv.g.solveOrder) }, - { 'g', "Group.visible", 'b', &(SS.sv.g.visible) }, - { 'g', "Group.remap", 'M', &(SS.sv.g.remap) }, + { 'g', "Group.h.v", 'x', &(SS.sv.g.h.v) }, + { 'g', "Group.type", 'd', &(SS.sv.g.type) }, + { 'g', "Group.name", 'N', &(SS.sv.g.name) }, + { 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) }, + { 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) }, + { 'g', "Group.wrkpl.type", 'd', &(SS.sv.g.wrkpl.type) }, + { 'g', "Group.wrkpl.q.w", 'f', &(SS.sv.g.wrkpl.q.w) }, + { 'g', "Group.wrkpl.q.vx", 'f', &(SS.sv.g.wrkpl.q.vx) }, + { 'g', "Group.wrkpl.q.vy", 'f', &(SS.sv.g.wrkpl.q.vy) }, + { 'g', "Group.wrkpl.q.vz", 'f', &(SS.sv.g.wrkpl.q.vz) }, + { 'g', "Group.wrkpl.origin.v", 'x', &(SS.sv.g.wrkpl.origin.v) }, + { 'g', "Group.wrkpl.entityB.v", 'x', &(SS.sv.g.wrkpl.entityB.v)}, + { 'g', "Group.wrkpl.entityC.v", 'x', &(SS.sv.g.wrkpl.entityC.v)}, + { 'g', "Group.wrkpl.swapUV", 'b', &(SS.sv.g.wrkpl.swapUV) }, + { 'g', "Group.wrkpl.negateU", 'b', &(SS.sv.g.wrkpl.negateU) }, + { 'g', "Group.wrkpl.negateV", 'b', &(SS.sv.g.wrkpl.negateV) }, + { 'g', "Group.visible", 'b', &(SS.sv.g.visible) }, + { 'g', "Group.remap", 'M', &(SS.sv.g.remap) }, - { 'p', "Param.h.v.", 'x', &(SS.sv.p.h.v) }, - { 'p', "Param.val", 'f', &(SS.sv.p.val) }, + { 'p', "Param.h.v.", 'x', &(SS.sv.p.h.v) }, + { 'p', "Param.val", 'f', &(SS.sv.p.val) }, - { 'r', "Request.h.v", 'x', &(SS.sv.r.h.v) }, - { 'r', "Request.type", 'd', &(SS.sv.r.type) }, - { 'r', "Request.workplane.v", 'x', &(SS.sv.r.workplane.v) }, - { 'r', "Request.group.v", 'x', &(SS.sv.r.group.v) }, - { 'r', "Request.name", 'N', &(SS.sv.r.name) }, - { 'r', "Request.construction", 'b', &(SS.sv.r.construction) }, + { 'r', "Request.h.v", 'x', &(SS.sv.r.h.v) }, + { 'r', "Request.type", 'd', &(SS.sv.r.type) }, + { 'r', "Request.workplane.v", 'x', &(SS.sv.r.workplane.v) }, + { 'r', "Request.group.v", 'x', &(SS.sv.r.group.v) }, + { 'r', "Request.name", 'N', &(SS.sv.r.name) }, + { 'r', "Request.construction", 'b', &(SS.sv.r.construction) }, - { '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) }, - { 'e', "Entity.param[3].v", 'x', &(SS.sv.e.param[3].v) }, - { 'e', "Entity.param[4].v", 'x', &(SS.sv.e.param[4].v) }, - { 'e', "Entity.param[5].v", 'x', &(SS.sv.e.param[5].v) }, - { 'e', "Entity.param[6].v", 'x', &(SS.sv.e.param[6].v) }, - { 'e', "Entity.point[0].v", 'x', &(SS.sv.e.point[0].v) }, - { 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].v) }, - { 'e', "Entity.point[2].v", 'x', &(SS.sv.e.point[2].v) }, - { 'e', "Entity.point[3].v", 'x', &(SS.sv.e.point[3].v) }, - { 'e', "Entity.normal.v", 'x', &(SS.sv.e.normal.v) }, - { 'e', "Entity.distance.v", 'x', &(SS.sv.e.distance.v) }, - { 'e', "Entity.workplane.v", 'x', &(SS.sv.e.workplane.v) }, - { 'e', "Entity.numPoint.x", 'f', &(SS.sv.e.numPoint.x) }, - { 'e', "Entity.numPoint.y", 'f', &(SS.sv.e.numPoint.y) }, - { 'e', "Entity.numPoint.z", 'f', &(SS.sv.e.numPoint.z) }, - { 'e', "Entity.numNormal.w", 'f', &(SS.sv.e.numNormal.w) }, - { 'e', "Entity.numNormal.vx", 'f', &(SS.sv.e.numNormal.vx) }, - { 'e', "Entity.numNormal.vy", 'f', &(SS.sv.e.numNormal.vy) }, - { 'e', "Entity.numNormal.vz", 'f', &(SS.sv.e.numNormal.vz) }, - { 'e', "Entity.numDistance", 'f', &(SS.sv.e.numDistance) }, + { '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) }, + { 'e', "Entity.param[3].v", 'x', &(SS.sv.e.param[3].v) }, + { 'e', "Entity.param[4].v", 'x', &(SS.sv.e.param[4].v) }, + { 'e', "Entity.param[5].v", 'x', &(SS.sv.e.param[5].v) }, + { 'e', "Entity.param[6].v", 'x', &(SS.sv.e.param[6].v) }, + { 'e', "Entity.point[0].v", 'x', &(SS.sv.e.point[0].v) }, + { 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].v) }, + { 'e', "Entity.point[2].v", 'x', &(SS.sv.e.point[2].v) }, + { 'e', "Entity.point[3].v", 'x', &(SS.sv.e.point[3].v) }, + { 'e', "Entity.normal.v", 'x', &(SS.sv.e.normal.v) }, + { 'e', "Entity.distance.v", 'x', &(SS.sv.e.distance.v) }, + { 'e', "Entity.workplane.v", 'x', &(SS.sv.e.workplane.v) }, + { 'e', "Entity.numPoint.x", 'f', &(SS.sv.e.numPoint.x) }, + { 'e', "Entity.numPoint.y", 'f', &(SS.sv.e.numPoint.y) }, + { 'e', "Entity.numPoint.z", 'f', &(SS.sv.e.numPoint.z) }, + { 'e', "Entity.numNormal.w", 'f', &(SS.sv.e.numNormal.w) }, + { 'e', "Entity.numNormal.vx", 'f', &(SS.sv.e.numNormal.vx) }, + { 'e', "Entity.numNormal.vy", 'f', &(SS.sv.e.numNormal.vy) }, + { 'e', "Entity.numNormal.vz", 'f', &(SS.sv.e.numNormal.vz) }, + { 'e', "Entity.numDistance", 'f', &(SS.sv.e.numDistance) }, - { 'c', "Constraint.h.v", 'x', &(SS.sv.c.h.v) }, - { 'c', "Constraint.type", 'd', &(SS.sv.c.type) }, - { 'c', "Constraint.group.v", 'x', &(SS.sv.c.group.v) }, - { 'c', "Constraint.workplane.v", 'x', &(SS.sv.c.workplane.v) }, - { 'c', "Constraint.exprA", 'E', &(SS.sv.c.exprA) }, - { 'c', "Constraint.exprB", 'E', &(SS.sv.c.exprB) }, - { 'c', "Constraint.ptA.v", 'x', &(SS.sv.c.ptA.v) }, - { 'c', "Constraint.ptB.v", 'x', &(SS.sv.c.ptB.v) }, - { 'c', "Constraint.ptC.v", 'x', &(SS.sv.c.ptC.v) }, - { 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) }, - { 'c', "Constraint.entityB.v", 'x', &(SS.sv.c.entityB.v) }, - { 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x)}, - { 'c', "Constraint.disp.offset.y", 'f', &(SS.sv.c.disp.offset.y)}, - { 'c', "Constraint.disp.offset.z", 'f', &(SS.sv.c.disp.offset.z)}, + { 'c', "Constraint.h.v", 'x', &(SS.sv.c.h.v) }, + { 'c', "Constraint.type", 'd', &(SS.sv.c.type) }, + { 'c', "Constraint.group.v", 'x', &(SS.sv.c.group.v) }, + { 'c', "Constraint.workplane.v", 'x', &(SS.sv.c.workplane.v) }, + { 'c', "Constraint.exprA", 'E', &(SS.sv.c.exprA) }, + { 'c', "Constraint.exprB", 'E', &(SS.sv.c.exprB) }, + { 'c', "Constraint.ptA.v", 'x', &(SS.sv.c.ptA.v) }, + { 'c', "Constraint.ptB.v", 'x', &(SS.sv.c.ptB.v) }, + { 'c', "Constraint.ptC.v", 'x', &(SS.sv.c.ptC.v) }, + { 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) }, + { 'c', "Constraint.entityB.v", 'x', &(SS.sv.c.entityB.v) }, + { 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) }, + { 'c', "Constraint.disp.offset.y", 'f', &(SS.sv.c.disp.offset.y) }, + { 'c', "Constraint.disp.offset.z", 'f', &(SS.sv.c.disp.offset.z) }, { 0, NULL, NULL, NULL }, }; @@ -264,16 +274,20 @@ bool SolveSpace::LoadFromFile(char *filename) { LoadUsingTable(key, val); } else if(strcmp(line, "AddGroup")==0) { SS.group.Add(&(sv.g)); + memset(&(sv.g), 0, sizeof(sv.g)); } else if(strcmp(line, "AddParam")==0) { // params are regenerated, but we want to preload the values // for initial guesses SS.param.Add(&(sv.p)); + memset(&(sv.p), 0, sizeof(sv.p)); } else if(strcmp(line, "AddEntity")==0) { // entities are regenerated } else if(strcmp(line, "AddRequest")==0) { SS.request.Add(&(sv.r)); + memset(&(sv.r), 0, sizeof(sv.r)); } else if(strcmp(line, "AddConstraint")==0) { SS.constraint.Add(&(sv.c)); + memset(&(sv.c), 0, sizeof(sv.c)); } else if(strcmp(line, "ñ÷åò±²³´SolveSpaceREVa")==0) { // do nothing, version string } else { diff --git a/graphicswin.cpp b/graphicswin.cpp index a1e6a61..4739f7a 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -78,6 +78,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, NULL, 0, NULL }, { 1, "&On Point / Curve / Plane\tShift+O", MNU_ON_ENTITY, 'O'|S, mCon }, { 1, "E&qual Length / Radius\tShift+Q", MNU_EQUAL, 'Q'|S, mCon }, +{ 1, "Length Ra&tio\tShift+T", MNU_RATIO, 'T'|S, mCon }, { 1, "At &Midpoint\tShift+M", MNU_AT_MIDPOINT, 'M'|S, mCon }, { 1, "S&ymmetric\tShift+Y", MNU_SYMMETRIC, 'Y'|S, mCon }, { 1, "Para&llel\tShift+L", MNU_PARALLEL, 'L'|S, mCon }, @@ -97,7 +98,7 @@ void GraphicsWindow::Init(void) { memset(this, 0, sizeof(*this)); offset.x = offset.y = offset.z = 0; - scale = 1; + scale = 5; projRight.x = 1; projRight.y = projRight.z = 0; projUp.y = 1; projUp.z = projUp.x = 0; @@ -150,10 +151,11 @@ void GraphicsWindow::AnimateOnto(Quaternion quatf, Vector offsetf) { quatf = quatf.ScaledBy(-1); mp = mm; } - double mo = (offset0.Minus(offsetf)).Magnitude()/scale; + double mo = (offset0.Minus(offsetf)).Magnitude()*scale; // Animate transition, unless it's a tiny move. - SDWORD dt = (mp < 0.01 && mo < 10) ? (-20) : (SDWORD)(100 + 1000*mp); + SDWORD dt = (mp < 0.01 && mo < 10) ? (-20) : + (SDWORD)(100 + 1000*mp + 0.4*mo); SDWORD tn, t0 = GetMilliseconds(); double s = 0; do { @@ -559,7 +561,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, Entity *circle = SS.GetEntity(pending.circle); Vector center = SS.GetEntity(circle->point[0])->PointGetNum(); Point2d c2 = ProjectPoint(center); - double r = c2.DistanceTo(mp)*scale; + double r = c2.DistanceTo(mp)/scale; SS.GetEntity(circle->distance)->DistanceForceTo(r); break; } diff --git a/sketch.cpp b/sketch.cpp index 6fb6838..3003deb 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -24,12 +24,53 @@ void Group::MenuGroup(int id) { memset(&g, 0, sizeof(g)); g.visible = true; + SS.GW.GroupSelection(); +#define gs (SS.GW.gs) + switch(id) { case GraphicsWindow::MNU_GROUP_3D: - g.type = DRAWING; + g.type = DRAWING_3D; g.name.strcpy("draw-in-3d"); break; + case GraphicsWindow::MNU_GROUP_WRKPL: + g.type = DRAWING_WORKPLANE; + g.name.strcpy("draw-in-plane"); + if(gs.points == 1 && gs.n == 1) { + g.wrkpl.type = WORKPLANE_BY_POINT_ORTHO; + + Vector u = SS.GW.projRight, v = SS.GW.projUp; + u = u.ClosestOrtho(); + v = v.Minus(u.ScaledBy(v.Dot(u))); + v = v.ClosestOrtho(); + + g.wrkpl.q = Quaternion::MakeFrom(u, v); + g.wrkpl.origin = gs.point[0]; + } else if(gs.points == 1 && gs.lineSegments == 2 && gs.n == 3) { + g.wrkpl.type = WORKPLANE_BY_LINE_SEGMENTS; + + g.wrkpl.origin = gs.point[0]; + g.wrkpl.entityB = gs.entity[0]; + g.wrkpl.entityC = gs.entity[1]; + + Vector ut = SS.GetEntity(g.wrkpl.entityB)->VectorGetNum(); + Vector vt = SS.GetEntity(g.wrkpl.entityC)->VectorGetNum(); + ut = ut.WithMagnitude(1); + vt = vt.WithMagnitude(1); + + if(fabs(SS.GW.projUp.Dot(vt)) < fabs(SS.GW.projUp.Dot(ut))) { + SWAP(Vector, ut, vt); + g.wrkpl.swapUV = true; + } + if(SS.GW.projRight.Dot(ut) < 0) g.wrkpl.negateU = true; + if(SS.GW.projUp. Dot(vt) < 0) g.wrkpl.negateV = true; + } else { + Error("Bad selection for new drawing in workplane."); + return; + } + SS.GW.ClearSelection(); + break; + case GraphicsWindow::MNU_GROUP_EXTRUDE: g.type = EXTRUDE; g.opA = SS.GW.activeGroup; @@ -48,6 +89,13 @@ void Group::MenuGroup(int id) { SS.group.AddAndAssignId(&g); SS.GenerateAll(SS.GW.solving == GraphicsWindow::SOLVE_ALWAYS); SS.GW.activeGroup = g.h; + if(g.type == DRAWING_WORKPLANE) { + SS.GW.activeWorkplane = g.h.entity(0); + Entity *e = SS.GetEntity(SS.GW.activeWorkplane); + Quaternion quatf = e->Normal()->NormalGetNum(); + Vector offsetf = (e->WorkplaneGetOffset()).ScaledBy(-1); + SS.GW.AnimateOnto(quatf, offsetf); + } SS.TW.Show(); } @@ -68,8 +116,54 @@ void Group::Generate(IdList *entity, gn = gn.WithMagnitude(200/SS.GW.scale); int i; switch(type) { - case DRAWING: - return; + case DRAWING_3D: + break; + + case DRAWING_WORKPLANE: { + Quaternion q; + if(wrkpl.type == WORKPLANE_BY_LINE_SEGMENTS) { + Vector u = SS.GetEntity(wrkpl.entityB)->VectorGetNum(); + Vector v = SS.GetEntity(wrkpl.entityC)->VectorGetNum(); + u = u.WithMagnitude(1); + Vector n = u.Cross(v); + v = (n.Cross(u)).WithMagnitude(1); + + if(wrkpl.swapUV) SWAP(Vector, u, v); + if(wrkpl.negateU) u = u.ScaledBy(-1); + if(wrkpl.negateV) v = v.ScaledBy(-1); + q = Quaternion::MakeFrom(u, v); + } else if(wrkpl.type == WORKPLANE_BY_POINT_ORTHO) { + // Already given, numerically. + q = wrkpl.q; + } else oops(); + + Entity normal; + memset(&normal, 0, sizeof(normal)); + normal.type = Entity::NORMAL_N_COPY; + normal.numNormal = q; + normal.point[0] = h.entity(2); + normal.group = h; + normal.h = h.entity(1); + entity->Add(&normal); + + Entity point; + memset(&point, 0, sizeof(point)); + point.type = Entity::POINT_N_COPY; + point.numPoint = SS.GetEntity(wrkpl.origin)->PointGetNum(); + point.group = h; + point.h = h.entity(2); + entity->Add(&point); + + Entity wp; + memset(&wp, 0, sizeof(wp)); + wp.type = Entity::WORKPLANE; + wp.normal = normal.h; + wp.point[0] = point.h; + wp.group = h; + wp.h = h.entity(0); + entity->Add(&wp); + break; + } case EXTRUDE: AddParam(param, h.param(0), gn.x); @@ -179,6 +273,7 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, en.distance = Remap(ep->distance, a); break; + case Entity::POINT_N_COPY: case Entity::POINT_N_TRANS: case Entity::POINT_N_ROT_TRANS: case Entity::POINT_IN_3D: @@ -205,6 +300,9 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, if(a != 0) oops(); SS.entity.Add(&en); + // Any operation on these lists may break existing pointers! + ep = SS.GetEntity(in); + hEntity np = en.h; memset(&en, 0, sizeof(en)); en.point[0] = ep->h; @@ -252,7 +350,7 @@ void Group::MakePolygons(void) { (faces.elem[i]).Clear(); } faces.Clear(); - if(type == DRAWING) { + if(type == DRAWING_3D || type == DRAWING_WORKPLANE) { edges.l.Clear(); int i; for(i = 0; i < SS.entity.n; i++) { @@ -350,7 +448,7 @@ void Group::Draw(void) { } else { int i; glEnable(GL_LIGHTING); - GLfloat vec[] = { 0, 0, 0.5, 1.0 }; + GLfloat vec[] = { 0.3f, 0.3f, 0.3f, 1.0 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec); for(i = 0; i < faces.n; i++) { glxFillPolygon(&(faces.elem[i])); diff --git a/sketch.h b/sketch.h index 4d6a187..f9c7a58 100644 --- a/sketch.h +++ b/sketch.h @@ -42,6 +42,7 @@ public: inline bool isFromRequest(void); inline hRequest request(void); + inline hGroup group(void); inline hEquation equation(int i); }; class hParam { @@ -76,19 +77,32 @@ public: int tag; hGroup h; - static const int DRAWING = 5000; + static const int DRAWING_3D = 5000; + static const int DRAWING_WORKPLANE = 5001; static const int EXTRUDE = 5010; static const int ROTATE = 5020; static const int TRANSLATE = 5030; int type; - int solveOrder; bool solved; hGroup opA; hGroup opB; bool visible; + static const int WORKPLANE_BY_POINT_ORTHO = 6000; + static const int WORKPLANE_BY_LINE_SEGMENTS = 6001; + struct { + int type; + Quaternion q; + hEntity origin; + hEntity entityB; + hEntity entityC; + bool swapUV; + bool negateU; + bool negateV; + } wrkpl; + SEdgeList edges; SList faces; struct { @@ -166,6 +180,7 @@ public: static const int POINT_IN_2D = 2001; static const int POINT_N_TRANS = 2010; static const int POINT_N_ROT_TRANS = 2011; + static const int POINT_N_COPY = 2012; static const int NORMAL_IN_3D = 3000; static const int NORMAL_IN_2D = 3001; @@ -215,6 +230,7 @@ public: bool HasVector(void); ExprVector VectorGetExprs(void); + Vector VectorGetNum(void); Vector VectorGetRefPoint(void); // For distances @@ -308,6 +324,7 @@ public: static const int PT_IN_PLANE = 40; static const int PT_ON_LINE = 41; static const int EQUAL_LENGTH_LINES = 50; + static const int LENGTH_RATIO = 51; static const int SYMMETRIC = 60; static const int AT_MIDPOINT = 70; static const int HORIZONTAL = 80; @@ -411,6 +428,8 @@ inline bool hEntity::isFromRequest(void) { if(v & 0x80000000) return false; else return true; } inline hRequest hEntity::request(void) { hRequest r; r.v = (v >> 16); return r; } +inline hGroup hEntity::group(void) + { hGroup r; r.v = (v >> 16) & 0x3fff; return r; } inline hEquation hEntity::equation(int i) { if(i != 0) oops(); hEquation r; r.v = v | 0x40000000; return r; } diff --git a/solvespace.h b/solvespace.h index a10963e..4114b1f 100644 --- a/solvespace.h +++ b/solvespace.h @@ -12,6 +12,8 @@ #define max(x, y) ((x) > (y) ? (x) : (y)) #endif +#define SWAP(T, a, b) do { T temp = (a); (a) = (b); (b) = temp; } while(0) + #define isforname(c) (isalnum(c) || (c) == '_' || (c) == '-' || (c) == '#') typedef signed long SDWORD; diff --git a/system.cpp b/system.cpp index 47303cb..fd89ea3 100644 --- a/system.cpp +++ b/system.cpp @@ -185,7 +185,6 @@ void System::SortBySensitivity(void) { entryWithOrigPos[j] = j; } -#define SWAP(T, a, b) do { T temp = (a); (a) = (b); (b) = temp; } while(0) for(j = 0; j < mat.n; j++) { int dest = j; // we are writing to position j // And the source is whichever position ahead of us can be swapped @@ -208,7 +207,7 @@ void System::SortBySensitivity(void) { } bool System::Tol(double v) { - return (fabs(v) < 0.001); + return (fabs(v) < 0.01); } void System::GaussJordan(void) { @@ -291,7 +290,10 @@ bool System::SolveLinearSystem(void) { max = fabs(mat.A.num[ip][i]); } } - if(fabs(max) < 1e-12) return false; + // Don't give up on a singular matrix unless it's really bad; the + // assumption code is responsible for identifying that condition, + // so we're not responsible for reporting that error. + if(fabs(max) < 1e-20) return false; // Swap row imax with row i for(jp = 0; jp < mat.n; jp++) { @@ -317,7 +319,7 @@ bool System::SolveLinearSystem(void) { // We've put the matrix in upper triangular form, so at this point we // can solve by back-substitution. for(i = mat.m - 1; i >= 0; i--) { - if(fabs(mat.A.num[i][i]) < 1e-10) return false; + if(fabs(mat.A.num[i][i]) < 1e-20) return false; temp = mat.B.num[i]; for(j = mat.n - 1; j > i; j--) { @@ -360,7 +362,7 @@ bool System::NewtonSolve(int tag) { // Check for convergence converged = true; for(i = 0; i < mat.m; i++) { - if(!Tol(mat.B.num[i])) { + if(fabs(mat.B.num[i]) > 1e-10) { converged = false; break; } diff --git a/ui.h b/ui.h index 1da7cf0..1181f54 100644 --- a/ui.h +++ b/ui.h @@ -117,6 +117,7 @@ public: // Constrain MNU_DISTANCE_DIA, MNU_EQUAL, + MNU_RATIO, MNU_ON_ENTITY, MNU_SYMMETRIC, MNU_AT_MIDPOINT, diff --git a/util.cpp b/util.cpp index 17dafea..99992d7 100644 --- a/util.cpp +++ b/util.cpp @@ -310,6 +310,18 @@ double Vector::DivPivoting(Vector delta) { } else oops(); } +Vector Vector::ClosestOrtho(void) { + double m = max(fabs(x), max(fabs(y), fabs(z))); + + if(m == fabs(x)) { + return MakeFrom((x > 0) ? 1 : -1, 0, 0); + } else if(m == fabs(y)) { + return MakeFrom(0, (y > 0) ? 1 : -1, 0); + } else if(m == fabs(z)) { + return MakeFrom(0, 0, (z > 0) ? 1 : -1); + } else oops(); +} + Point2d Point2d::Plus(Point2d b) { Point2d r; r.x = x + b.x;