From 6042fb3e0f42b95438c2e609f6aa0f5794f69e35 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sat, 10 May 2008 22:09:46 -0800 Subject: [PATCH] Add transformed points and normals with a rotation (as well as a translation; or equivalently, rotation about an arbitrary axis). Those will be important for step and repeats, and for imported parts. Also fix a terrible memory corruption bug: I was freeing the remap list after I loaded it from the file, but the code that put that into the SS.group list made only a shallow copy. [git-p4: depot-paths = "//depot/solvespace/": change = 1715] --- dsc.h | 3 ++ entity.cpp | 135 +++++++++++++++++++++++++++++++++++++++++------- expr.cpp | 24 +++++++++ expr.h | 3 ++ file.cpp | 9 +++- graphicswin.cpp | 53 +++++++++++++++++-- sketch.cpp | 99 ++++++++++++++++++++++++++++++----- sketch.h | 29 ++++++++--- solvespace.cpp | 2 + system.cpp | 4 +- ui.h | 2 + util.cpp | 23 +++++++++ 12 files changed, 340 insertions(+), 46 deletions(-) diff --git a/dsc.h b/dsc.h index b9441a5b..ed108323 100644 --- a/dsc.h +++ b/dsc.h @@ -28,6 +28,9 @@ public: Vector RotationU(void); Vector RotationV(void); Vector RotationN(void); + Vector Rotate(Vector p); + + Quaternion Times(Quaternion b); }; class Vector { diff --git a/entity.cpp b/entity.cpp index 09ea21d5..75bf51ea 100644 --- a/entity.cpp +++ b/entity.cpp @@ -10,7 +10,8 @@ bool Entity::HasVector(void) { case LINE_SEGMENT: case NORMAL_IN_3D: case NORMAL_IN_2D: - case NORMAL_XFRMD: + case NORMAL_N_COPY: + case NORMAL_N_ROT: return true; default: @@ -26,7 +27,8 @@ ExprVector Entity::VectorGetExprs(void) { case NORMAL_IN_3D: case NORMAL_IN_2D: - case NORMAL_XFRMD: + case NORMAL_N_COPY: + case NORMAL_N_ROT: return NormalExprsN(); default: oops(); @@ -41,7 +43,8 @@ Vector Entity::VectorGetRefPoint(void) { case NORMAL_IN_3D: case NORMAL_IN_2D: - case NORMAL_XFRMD: + case NORMAL_N_COPY: + case NORMAL_N_ROT: return SS.GetEntity(point[0])->PointGetNum(); default: oops(); @@ -81,21 +84,21 @@ void Entity::WorkplaneGetPlaneExprs(ExprVector *n, Expr **dn) { double Entity::DistanceGetNum(void) { if(type == DISTANCE) { return SS.GetParam(param[0])->val; - } else if(type == DISTANCE_XFRMD) { + } else if(type == DISTANCE_N_COPY) { return numDistance; } else oops(); } Expr *Entity::DistanceGetExpr(void) { if(type == DISTANCE) { return Expr::FromParam(param[0]); - } else if(type == DISTANCE_XFRMD) { + } else if(type == DISTANCE_N_COPY) { return Expr::FromConstant(numDistance); } else oops(); } void Entity::DistanceForceTo(double v) { if(type == DISTANCE) { (SS.GetParam(param[0]))->val = v; - } else if(type == DISTANCE_XFRMD) { + } else if(type == DISTANCE_N_COPY) { // do nothing, it's locked } else oops(); } @@ -108,9 +111,12 @@ bool Entity::IsPoint(void) { switch(type) { case POINT_IN_3D: case POINT_IN_2D: - case POINT_XFRMD: return true; + case POINT_N_TRANS: + case POINT_N_ROT_TRANS: + return true; - default: return false; + default: + return false; } } @@ -118,7 +124,9 @@ bool Entity::IsNormal(void) { switch(type) { case NORMAL_IN_3D: case NORMAL_IN_2D: - case NORMAL_XFRMD: return true; + case NORMAL_N_COPY: + case NORMAL_N_ROT: + return true; default: return false; } @@ -140,10 +148,18 @@ Quaternion Entity::NormalGetNum(void) { q = norm->NormalGetNum(); break; } - case NORMAL_XFRMD: + case NORMAL_N_COPY: q = numNormal; break; + case NORMAL_N_ROT: + q.w = SS.GetParam(param[0])->val; + q.vx = SS.GetParam(param[1])->val; + q.vy = SS.GetParam(param[2])->val; + q.vz = SS.GetParam(param[3])->val; + q = q.Times(numNormal); + break; + default: oops(); } return q; @@ -159,10 +175,13 @@ void Entity::NormalForceTo(Quaternion q) { break; case NORMAL_IN_2D: - case NORMAL_XFRMD: + case NORMAL_N_COPY: // There's absolutely nothing to do; these are locked. break; + case NORMAL_N_ROT: + break; + default: oops(); } } @@ -203,13 +222,29 @@ ExprQuaternion Entity::NormalGetExprs(void) { q = norm->NormalGetExprs(); break; } - case NORMAL_XFRMD: + case NORMAL_N_COPY: q.w = Expr::FromConstant(numNormal.w); q.vx = Expr::FromConstant(numNormal.vx); q.vy = Expr::FromConstant(numNormal.vy); q.vz = Expr::FromConstant(numNormal.vz); break; + case NORMAL_N_ROT: { + ExprQuaternion orig; + orig.w = Expr::FromConstant(numNormal.w); + orig.vx = Expr::FromConstant(numNormal.vx); + orig.vy = Expr::FromConstant(numNormal.vy); + orig.vz = Expr::FromConstant(numNormal.vz); + + q.w = Expr::FromParam(param[0]); + q.vx = Expr::FromParam(param[1]); + q.vy = Expr::FromParam(param[2]); + q.vz = Expr::FromParam(param[3]); + + q = q.Times(orig); + break; + } + default: oops(); } return q; @@ -235,7 +270,7 @@ void Entity::PointForceTo(Vector p) { break; } - case POINT_XFRMD: { + case POINT_N_TRANS: { Vector trans = p.Minus(numPoint); SS.GetParam(param[0])->val = trans.x; SS.GetParam(param[1])->val = trans.y; @@ -243,6 +278,17 @@ void Entity::PointForceTo(Vector p) { break; } + case POINT_N_ROT_TRANS: { + // Force only the translation; leave the rotation unchanged. But + // remember that we're working with respect to the rotated + // point. + Vector trans = p.Minus(PointGetQuaternion().Rotate(numPoint)); + SS.GetParam(param[0])->val = trans.x; + SS.GetParam(param[1])->val = trans.y; + SS.GetParam(param[2])->val = trans.z; + break; + } + default: oops(); } } @@ -266,13 +312,24 @@ Vector Entity::PointGetNum(void) { break; } - case POINT_XFRMD: { + case POINT_N_TRANS: { p = numPoint; p.x += SS.GetParam(param[0])->val; p.y += SS.GetParam(param[1])->val; p.z += SS.GetParam(param[2])->val; break; } + + case POINT_N_ROT_TRANS: { + Vector offset = Vector::MakeFrom( + SS.GetParam(param[0])->val, + SS.GetParam(param[1])->val, + SS.GetParam(param[2])->val); + Quaternion q = PointGetQuaternion(); + p = q.Rotate(numPoint); + p = p.Plus(offset); + break; + } default: oops(); } return p; @@ -296,7 +353,7 @@ ExprVector Entity::PointGetExprs(void) { r = r.Plus(v.ScaledBy(Expr::FromParam(param[1]))); break; } - case POINT_XFRMD: { + case POINT_N_TRANS: { ExprVector orig = { Expr::FromConstant(numPoint.x), Expr::FromConstant(numPoint.y), @@ -308,6 +365,24 @@ ExprVector Entity::PointGetExprs(void) { r = orig.Plus(trans); break; } + case POINT_N_ROT_TRANS: { + ExprVector orig = { + Expr::FromConstant(numPoint.x), + Expr::FromConstant(numPoint.y), + Expr::FromConstant(numPoint.z) }; + ExprVector trans = { + Expr::FromParam(param[0]), + Expr::FromParam(param[1]), + Expr::FromParam(param[2]) }; + ExprQuaternion q = { + Expr::FromParam(param[3]), + Expr::FromParam(param[4]), + Expr::FromParam(param[5]), + Expr::FromParam(param[6]) }; + orig = q.Rotate(orig); + r = orig.Plus(trans); + break; + } default: oops(); } return r; @@ -335,6 +410,26 @@ void Entity::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) { } } +void Entity::PointForceQuaternionTo(Quaternion q) { + if(type != POINT_N_ROT_TRANS) oops(); + + SS.GetParam(param[3])->val = q.w; + SS.GetParam(param[4])->val = q.vx; + SS.GetParam(param[5])->val = q.vy; + SS.GetParam(param[6])->val = q.vz; +} + +Quaternion Entity::PointGetQuaternion(void) { + if(type != POINT_N_ROT_TRANS) oops(); + + Quaternion q; + q.w = SS.GetParam(param[3])->val; + q.vx = SS.GetParam(param[4])->val; + q.vy = SS.GetParam(param[5])->val; + q.vz = SS.GetParam(param[6])->val; + return q; +} + void Entity::LineDrawOrGetDistance(Vector a, Vector b) { if(dogd.drawing) { // This fudge guarantees that the line will get drawn in front of @@ -403,7 +498,8 @@ void Entity::DrawOrGetDistance(int order) { } switch(type) { - case POINT_XFRMD: + case POINT_N_TRANS: + case POINT_N_ROT_TRANS: case POINT_IN_3D: case POINT_IN_2D: { if(order >= 0 && order != 2) break; @@ -443,9 +539,10 @@ void Entity::DrawOrGetDistance(int order) { break; } + case NORMAL_N_COPY: + case NORMAL_N_ROT: case NORMAL_IN_3D: - case NORMAL_IN_2D: - case NORMAL_XFRMD: { + case NORMAL_IN_2D: { if(order >= 0 && order != 2) break; if(!SS.GW.showNormals) break; @@ -474,7 +571,7 @@ void Entity::DrawOrGetDistance(int order) { } case DISTANCE: - case DISTANCE_XFRMD: + case DISTANCE_N_COPY: // These are used only as data structures, nothing to display. break; diff --git a/expr.cpp b/expr.cpp index dc3f827a..afb30c55 100644 --- a/expr.cpp +++ b/expr.cpp @@ -125,6 +125,29 @@ ExprVector ExprQuaternion::RotationN(void) { return n; } +ExprVector ExprQuaternion::Rotate(ExprVector p) { + // Express the point in the new basis + return (RotationU().ScaledBy(p.x)).Plus( + RotationV().ScaledBy(p.y)).Plus( + RotationN().ScaledBy(p.z)); +} + +ExprQuaternion ExprQuaternion::Times(ExprQuaternion b) { + Expr *sa = w, *sb = b.w; + ExprVector va = { vx, vy, vz }; + ExprVector vb = { b.vx, b.vy, b.vz }; + + ExprQuaternion r; + r.w = (sa->Times(sb))->Minus(va.Dot(vb)); + ExprVector vr = vb.ScaledBy(sa).Plus( + va.ScaledBy(sb).Plus( + va.Cross(vb))); + r.vx = vr.x; + r.vy = vr.y; + r.vz = vr.z; + return r; +} + Expr *ExprQuaternion::Magnitude(void) { return ((w ->Square())->Plus( (vx->Square())->Plus( @@ -132,6 +155,7 @@ Expr *ExprQuaternion::Magnitude(void) { (vz->Square())))))->Sqrt(); } + Expr *Expr::FromParam(hParam p) { Expr *r = AllocExpr(); r->op = PARAM; diff --git a/expr.h b/expr.h index b55ca484..bf1873eb 100644 --- a/expr.h +++ b/expr.h @@ -144,6 +144,9 @@ public: ExprVector RotationV(void); ExprVector RotationN(void); + ExprVector Rotate(ExprVector p); + ExprQuaternion Times(ExprQuaternion b); + Expr *Magnitude(void); }; diff --git a/file.cpp b/file.cpp index c8a6e732..bc596f9d 100644 --- a/file.cpp +++ b/file.cpp @@ -72,6 +72,9 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { '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) }, @@ -205,7 +208,11 @@ void SolveSpace::LoadUsingTable(char *key, char *val) { case 'M': { IdList *m = (IdList *)p; - m->Clear(); + // Don't clear this list! When the group gets added, it + // makes a shallow copy, so that would result in us + // freeing memory that we want to keep around. Just + // zero it out so that new memory is allocated. + memset(m, 0, sizeof(*m)); for(;;) { EntityMap em; char line2[1024]; diff --git a/graphicswin.cpp b/graphicswin.cpp index 937f66c2..a1e6a611 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -42,8 +42,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "New &Drawing in 3d\tShift+Ctrl+D", MNU_GROUP_3D, 'D'|S|C, mGrp }, { 1, "New Drawing in Workplane\tShift+Ctrl+W",MNU_GROUP_WRKPL, 'W'|S|C, mGrp }, { 1, NULL, 0, NULL }, -{ 1, "New Step and Repeat &Translating", 0, 0, NULL }, -{ 1, "New Step and Repeat &Rotating", 0, 0, NULL }, +{ 1, "New Step &Translating\tShift+Ctrl+R", MNU_GROUP_TRANS, 'T'|S|C,mGrp }, +{ 1, "New Step &Rotating\tShift+Ctrl+T", MNU_GROUP_ROT, 'R'|S|C,mGrp }, { 1, NULL, 0, 0, NULL }, { 1, "New Extrusion\tShift+Ctrl+X", MNU_GROUP_EXTRUDE, 'X'|S|C,mGrp }, { 1, NULL, 0, 0, NULL }, @@ -494,10 +494,53 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, HitTestMakeSelection(mp); // and fall through case DRAGGING_NEW_POINT: - case DRAGGING_POINT: - UpdateDraggedPoint(pending.point, x, y); - break; + case DRAGGING_POINT: { + Entity *p = SS.GetEntity(pending.point); + if((p->type == Entity::POINT_N_ROT_TRANS) && + (shiftDown || ctrlDown)) + { + // These points also come with a rotation, which the user can + // edit by pressing shift or control. + Quaternion q = p->PointGetQuaternion(); + Vector p3 = p->PointGetNum(); + Point2d p2 = ProjectPoint(p3); + Vector u = q.RotationU(), v = q.RotationV(); + if(ctrlDown) { + double d = mp.DistanceTo(p2); + if(d < 25) { + // Don't start dragging the position about the normal + // until we're a little ways out, to get a reasonable + // reference pos + orig.mouse = mp; + break; + } + double theta = atan2(orig.mouse.y-p2.y, orig.mouse.x-p2.x); + theta -= atan2(y-p2.y, x-p2.x); + + Vector gn = projRight.Cross(projUp); + u = u.RotatedAbout(gn, -theta); + v = v.RotatedAbout(gn, -theta); + } else { + double dx = -(x - orig.mouse.x); + double dy = -(y - orig.mouse.y); + double s = 0.3*(PI/180); // degrees per pixel + u = u.RotatedAbout(orig.projUp, -s*dx); + u = u.RotatedAbout(orig.projRight, s*dy); + v = v.RotatedAbout(orig.projUp, -s*dx); + v = v.RotatedAbout(orig.projRight, s*dy); + } + q = Quaternion::MakeFrom(u, v); + p->PointForceQuaternionTo(q); + // Let's rotate about the selected point; so fix up the + // translation so that that point didn't move. + p->PointForceTo(p3); + orig.mouse = mp; + } else { + UpdateDraggedPoint(pending.point, x, y); + } + break; + } case DRAGGING_NEW_CUBIC_POINT: { UpdateDraggedPoint(pending.point, x, y); HitTestMakeSelection(mp); diff --git a/sketch.cpp b/sketch.cpp index 7c1c4e0b..6fb6838d 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -2,6 +2,8 @@ const hEntity Entity::FREE_IN_3D = { 0 }; const hEntity Entity::NO_ENTITY = { 0 }; +const hParam Param::NO_PARAM = { 0 }; +#define NO_PARAM (Param::NO_PARAM) const hGroup Group::HGROUP_REFERENCES = { 1 }; const hRequest Request::HREQUEST_REFERENCE_XY = { 1 }; @@ -25,15 +27,21 @@ void Group::MenuGroup(int id) { switch(id) { case GraphicsWindow::MNU_GROUP_3D: g.type = DRAWING; - g.name.strcpy("drawing"); + g.name.strcpy("draw-in-3d"); break; case GraphicsWindow::MNU_GROUP_EXTRUDE: g.type = EXTRUDE; - g.opA.v = 2; + g.opA = SS.GW.activeGroup; g.name.strcpy("extrude"); break; + case GraphicsWindow::MNU_GROUP_ROT: + g.type = ROTATE; + g.opA = SS.GW.activeGroup; + g.name.strcpy("rotate"); + break; + default: oops(); } @@ -71,7 +79,32 @@ void Group::Generate(IdList *entity, Entity *e = &(entity->elem[i]); if(e->group.v != opA.v) continue; - CopyEntity(e->h, 0, h.param(0), h.param(1), h.param(2), true); + CopyEntity(e->h, 0, + h.param(0), h.param(1), h.param(2), + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + true, true); + } + break; + + case ROTATE: + // The translation vector + AddParam(param, h.param(0), 100); + AddParam(param, h.param(1), 100); + AddParam(param, h.param(2), 100); + // The rotation quaternion + AddParam(param, h.param(3), 1); + AddParam(param, h.param(4), 0); + AddParam(param, h.param(5), 0); + AddParam(param, h.param(6), 0); + + for(i = 0; i < entity->n; i++) { + Entity *e = &(entity->elem[i]); + if(e->group.v != opA.v) continue; + + CopyEntity(e->h, 0, + h.param(0), h.param(1), h.param(2), + h.param(3), h.param(4), h.param(5), h.param(6), + false, false); } break; @@ -79,6 +112,21 @@ void Group::Generate(IdList *entity, } } +void Group::GenerateEquations(IdList *l) { + if(type == ROTATE) { + // Normalize the quaternion + ExprQuaternion q = { + Expr::FromParam(h.param(3)), + Expr::FromParam(h.param(4)), + Expr::FromParam(h.param(5)), + Expr::FromParam(h.param(6)) }; + Equation eq; + eq.e = (q.Magnitude())->Minus(Expr::FromConstant(1)); + eq.h = h.equation(0); + l->Add(&eq); + } +} + hEntity Group::Remap(hEntity in, int copyNumber) { int i; for(i = 0; i < remap.n; i++) { @@ -97,7 +145,8 @@ hEntity Group::Remap(hEntity in, int copyNumber) { } void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, - bool isExtrusion) + hParam qw, hParam qvx, hParam qvy, hParam qvz, + bool transOnly, bool isExtrusion) { Entity *ep = SS.GetEntity(in); @@ -130,13 +179,27 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, en.distance = Remap(ep->distance, a); break; + case Entity::POINT_N_TRANS: + case Entity::POINT_N_ROT_TRANS: case Entity::POINT_IN_3D: case Entity::POINT_IN_2D: - en.type = Entity::POINT_XFRMD; - en.param[0] = dx; - en.param[1] = dy; - en.param[2] = dz; - en.numPoint = ep->PointGetNum(); + if(transOnly) { + en.type = Entity::POINT_N_TRANS; + en.param[0] = dx; + en.param[1] = dy; + en.param[2] = dz; + en.numPoint = ep->PointGetNum(); + } else { + en.type = Entity::POINT_N_ROT_TRANS; + en.param[0] = dx; + en.param[1] = dy; + en.param[2] = dz; + en.param[3] = qw; + en.param[4] = qvx; + en.param[5] = qvy; + en.param[6] = qvz; + en.numPoint = ep->PointGetNum(); + } if(isExtrusion) { if(a != 0) oops(); @@ -153,15 +216,27 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, } break; + case Entity::NORMAL_N_COPY: + case Entity::NORMAL_N_ROT: case Entity::NORMAL_IN_3D: case Entity::NORMAL_IN_2D: - en.type = Entity::NORMAL_XFRMD; - en.numNormal = ep->NormalGetNum(); + if(transOnly) { + en.type = Entity::NORMAL_N_COPY; + en.numNormal = ep->NormalGetNum(); + } else { + en.type = Entity::NORMAL_N_ROT; + en.numNormal = ep->NormalGetNum(); + en.param[0] = qw; + en.param[1] = qvx; + en.param[2] = qvy; + en.param[3] = qvz; + } en.point[0] = Remap(ep->point[0], a); break; + case Entity::DISTANCE_N_COPY: case Entity::DISTANCE: - en.type = Entity::DISTANCE_XFRMD; + en.type = Entity::DISTANCE_N_COPY; en.numDistance = ep->DistanceGetNum(); break; diff --git a/sketch.h b/sketch.h index f14c7f1e..4d6a1875 100644 --- a/sketch.h +++ b/sketch.h @@ -22,6 +22,7 @@ public: inline hEntity entity(int i); inline hParam param(int i); + inline hEquation equation(int i); }; class hRequest { public: @@ -77,6 +78,8 @@ public: static const int DRAWING = 5000; static const int EXTRUDE = 5010; + static const int ROTATE = 5020; + static const int TRANSLATE = 5030; int type; int solveOrder; @@ -105,7 +108,10 @@ public: IdList remap; hEntity Remap(hEntity in, int copyNumber); void CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, - bool isExtrusion); + hParam qw, hParam qvx, hParam qvy, hParam qvz, + bool transOnly, bool isExtrusion); + + void GenerateEquations(IdList *l); void MakePolygons(void); void Draw(void); @@ -158,7 +164,8 @@ public: static const int POINT_IN_3D = 2000; static const int POINT_IN_2D = 2001; - static const int POINT_XFRMD = 2010; + static const int POINT_N_TRANS = 2010; + static const int POINT_N_ROT_TRANS = 2011; static const int NORMAL_IN_3D = 3000; static const int NORMAL_IN_2D = 3001; @@ -168,10 +175,11 @@ public: // u = (sin theta)*uw - (cos theta)*vw // v = nw static const int NORMAL_IN_PLANE = 3002; - static const int NORMAL_XFRMD = 3010; + static const int NORMAL_N_COPY = 3010; + static const int NORMAL_N_ROT = 3011; static const int DISTANCE = 4000; - static const int DISTANCE_XFRMD = 4001; + static const int DISTANCE_N_COPY = 4001; static const int WORKPLANE = 10000; static const int LINE_SEGMENT = 11000; @@ -187,7 +195,7 @@ public: hEntity distance; // The only types that have their own params are points, normals, // and directions. - hParam param[4]; + hParam param[7]; // Transformed points/normals/distances have their numerical value. Vector numPoint; @@ -228,6 +236,9 @@ public: void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v); void PointForceTo(Vector v); bool PointIsFromReferences(void); + // These apply only the POINT_N_ROT_TRANS, which has an assoc rotation + Quaternion PointGetQuaternion(void); + void PointForceQuaternionTo(Quaternion q); bool IsNormal(void); // Applies for any of the normal types @@ -275,6 +286,8 @@ public: // Used only in the solver hParam substd; + + static const hParam NO_PARAM; }; @@ -282,7 +295,7 @@ class hConstraint { public: DWORD v; - hEquation equation(int i); + inline hEquation equation(int i); }; class Constraint { @@ -380,6 +393,8 @@ inline hEntity hGroup::entity(int i) { hEntity r; r.v = 0x80000000 | (v << 16) | i; return r; } inline hParam hGroup::param(int i) { hParam r; r.v = 0x80000000 | (v << 16) | i; return r; } +inline hEquation hGroup::equation(int i) + { if(i != 0) oops(); hEquation r; r.v = v | 0x80000000; return r; } inline bool hRequest::IsFromReferences(void) { if(v == Request::HREQUEST_REFERENCE_XY.v) return true; @@ -397,7 +412,7 @@ inline bool hEntity::isFromRequest(void) inline hRequest hEntity::request(void) { hRequest r; r.v = (v >> 16); return r; } inline hEquation hEntity::equation(int i) - { if(i != 0) oops(); hEquation r; r.v = v | 0x80000000; return r; } + { if(i != 0) oops(); hEquation r; r.v = v | 0x40000000; return r; } inline hRequest hParam::request(void) { hRequest r; r.v = (v >> 16); return r; } diff --git a/solvespace.cpp b/solvespace.cpp index fc511900..bfadcb56 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -134,6 +134,8 @@ bool SolveSpace::SolveGroup(hGroup hg) { e->GenerateEquations(&(sys.eq)); } + // And from the groups themselves + g->GenerateEquations(&(sys.eq)); bool r = sys.Solve(); FreeAllTemporary(); diff --git a/system.cpp b/system.cpp index e2db3e4d..47303cbe 100644 --- a/system.cpp +++ b/system.cpp @@ -56,6 +56,7 @@ bool System::IsDragged(hParam p) { Entity *pt = SS.entity.FindByIdNoOops(SS.GW.pending.point); if(pt) { switch(pt->type) { + case Entity::POINT_N_TRANS: case Entity::POINT_IN_3D: if(p.v == (pt->param[0]).v) return true; if(p.v == (pt->param[1]).v) return true; @@ -63,7 +64,6 @@ bool System::IsDragged(hParam p) { break; case Entity::POINT_IN_2D: - case Entity::POINT_XFRMD: if(p.v == (pt->param[0]).v) return true; if(p.v == (pt->param[1]).v) return true; break; @@ -399,7 +399,7 @@ bool System::Solve(void) { SortBySensitivity(); -/* dbp("write/eval jacboian=%d", GetMilliseconds() - in); +/* for(i = 0; i < mat.m; i++) { dbp("function %d: %s", i, mat.B.sym[i]->Print()); } diff --git a/ui.h b/ui.h index 4760870f..1da7cf0c 100644 --- a/ui.h +++ b/ui.h @@ -112,6 +112,8 @@ public: MNU_GROUP_3D, MNU_GROUP_WRKPL, MNU_GROUP_EXTRUDE, + MNU_GROUP_ROT, + MNU_GROUP_TRANS, // Constrain MNU_DISTANCE_DIA, MNU_EQUAL, diff --git a/util.cpp b/util.cpp index c311018b..17dafea2 100644 --- a/util.cpp +++ b/util.cpp @@ -125,6 +125,29 @@ Vector Quaternion::RotationN(void) { return RotationU().Cross(RotationV()); } +Vector Quaternion::Rotate(Vector p) { + // Express the point in the new basis + return (RotationU().ScaledBy(p.x)).Plus( + RotationV().ScaledBy(p.y)).Plus( + RotationN().ScaledBy(p.z)); +} + +Quaternion Quaternion::Times(Quaternion b) { + double sa = w, sb = b.w; + Vector va = { vx, vy, vz }; + Vector vb = { b.vx, b.vy, b.vz }; + + Quaternion r; + r.w = sa*sb - va.Dot(vb); + Vector vr = vb.ScaledBy(sa).Plus( + va.ScaledBy(sb).Plus( + va.Cross(vb))); + r.vx = vr.x; + r.vy = vr.y; + r.vz = vr.z; + return r; +} + Vector Vector::MakeFrom(double x, double y, double z) { Vector v;