diff --git a/constraint.cpp b/constraint.cpp index f89101d..7c666d7 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -49,8 +49,8 @@ void Constraint::MenuConstrain(int id) { return; } Vector n = SS.GW.projRight.Cross(SS.GW.projUp); - Vector a = SS.GetEntity(c.ptA)->PointGetCoords(); - Vector b = SS.GetEntity(c.ptB)->PointGetCoords(); + 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.exprA = Expr::FromString("0")->DeepCopyKeep(); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 84d69a4..14637ee 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -41,8 +41,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { glxColor3d(1, 0.2, 1); switch(type) { case PT_PT_DISTANCE: { - Vector ap = SS.GetEntity(ptA)->PointGetCoords(); - Vector bp = SS.GetEntity(ptB)->PointGetCoords(); + Vector ap = SS.GetEntity(ptA)->PointGetNum(); + Vector bp = SS.GetEntity(ptB)->PointGetNum(); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); if(labelPos) *labelPos = ref; @@ -74,8 +74,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { case POINTS_COINCIDENT: { if(!dogd.drawing) { for(int i = 0; i < 2; i++) { - Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> - PointGetCoords(); + Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); Point2d pp = SS.GW.ProjectPoint(p); // The point is selected within a radius of 7, from the // same center; so if the point is visible, then this @@ -89,8 +88,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale); for(int i = 0; i < 2; i++) { - Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> - PointGetCoords(); + Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); glxColor3d(0.4, 0, 0.4); glBegin(GL_QUADS); glxVertex3v(p.Plus (r).Plus (d)); @@ -107,7 +105,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { case PT_ON_LINE: case PT_IN_PLANE: { double s = 7; - Vector p = SS.GetEntity(ptA)->PointGetCoords(); + Vector p = SS.GetEntity(ptA)->PointGetNum(); Vector r = gr.WithMagnitude(s); Vector d = gu.WithMagnitude(s); LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d)); @@ -120,8 +118,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { case EQUAL_LENGTH_LINES: { for(int i = 0; i < 2; i++) { Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); - Vector a = SS.GetEntity(e->point[0])->PointGetCoords(); - Vector b = SS.GetEntity(e->point[1])->PointGetCoords(); + Vector a = SS.GetEntity(e->point[0])->PointGetNum(); + Vector 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); @@ -132,8 +130,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { } case SYMMETRIC: { - Vector a = SS.GetEntity(ptA)->PointGetCoords(); - Vector b = SS.GetEntity(ptB)->PointGetCoords(); + Vector a = SS.GetEntity(ptA)->PointGetNum(); + Vector b = SS.GetEntity(ptB)->PointGetNum(); Vector n = SS.GetEntity(entityA)->WorkplaneGetNormalVector(); for(int i = 0; i < 2; i++) { Vector tail = (i == 0) ? a : b; @@ -168,8 +166,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { } // For "at midpoint", this branch is always taken. Entity *e = SS.GetEntity(entityA); - Vector a = SS.GetEntity(e->point[0])->PointGetCoords(); - Vector b = SS.GetEntity(e->point[1])->PointGetCoords(); + Vector a = SS.GetEntity(e->point[0])->PointGetNum(); + Vector b = SS.GetEntity(e->point[1])->PointGetNum(); Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5)); Vector offset = (a.Minus(b)).Cross(n); offset = offset.WithMagnitude(13/SS.GW.scale); @@ -191,8 +189,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10); } } else { - Vector a = SS.GetEntity(ptA)->PointGetCoords(); - Vector b = SS.GetEntity(ptB)->PointGetCoords(); + Vector a = SS.GetEntity(ptA)->PointGetNum(); + Vector b = SS.GetEntity(ptB)->PointGetNum(); Entity *w = SS.GetEntity(SS.GetEntity(ptA)->workplane); Vector cu, cv, cn; diff --git a/dsc.h b/dsc.h index f558ba0..234cf01 100644 --- a/dsc.h +++ b/dsc.h @@ -9,14 +9,14 @@ class Vector; class Quaternion { public: - // a + bi + cj + dk - double a, b, c, d; + // a + (vx)*i + (vy)*j + (vz)*k + double w, vx, vy, vz; - static Quaternion MakeFrom(double a, double b, double c, double d); + static Quaternion MakeFrom(double w, double vx, double vy, double vz); static Quaternion MakeFrom(Vector u, Vector v); - Quaternion Plus(Quaternion y); - Quaternion Minus(Quaternion y); + Quaternion Plus(Quaternion b); + Quaternion Minus(Quaternion b); Quaternion ScaledBy(double s); double Magnitude(void); Quaternion WithMagnitude(double s); @@ -25,6 +25,7 @@ public: // second rows, where that matrix is generated by this quaternion Vector RotationU(void); Vector RotationV(void); + Vector RotationN(void); }; class Vector { diff --git a/entity.cpp b/entity.cpp index 920029c..b55ea84 100644 --- a/entity.cpp +++ b/entity.cpp @@ -6,11 +6,7 @@ char *Entity::DescriptionString(void) { } void Entity::WorkplaneGetBasisVectors(Vector *u, Vector *v) { - double q[4]; - for(int i = 0; i < 4; i++) { - q[i] = SS.GetParam(param[i])->val; - } - Quaternion quat = Quaternion::MakeFrom(q[0], q[1], q[2], q[3]); + Quaternion quat = SS.GetEntity(normal)->NormalGetNum(); *u = quat.RotationU(); *v = quat.RotationV(); @@ -23,34 +19,10 @@ Vector Entity::WorkplaneGetNormalVector(void) { } void Entity::WorkplaneGetBasisExprs(ExprVector *u, ExprVector *v) { - Expr *a = Expr::FromParam(param[0]); - Expr *b = Expr::FromParam(param[1]); - Expr *c = Expr::FromParam(param[2]); - Expr *d = Expr::FromParam(param[3]); + ExprQuaternion q = SS.GetEntity(normal)->NormalGetExprs(); - Expr *two = Expr::FromConstant(2); - - u->x = a->Square(); - u->x = (u->x)->Plus(b->Square()); - u->x = (u->x)->Minus(c->Square()); - u->x = (u->x)->Minus(d->Square()); - - u->y = two->Times(a->Times(d)); - u->y = (u->y)->Plus(two->Times(b->Times(c))); - - u->z = two->Times(b->Times(d)); - u->z = (u->z)->Minus(two->Times(a->Times(c))); - - v->x = two->Times(b->Times(c)); - v->x = (v->x)->Minus(two->Times(a->Times(d))); - - v->y = a->Square(); - v->y = (v->y)->Minus(b->Square()); - v->y = (v->y)->Plus(c->Square()); - v->y = (v->y)->Minus(d->Square()); - - v->z = two->Times(a->Times(b)); - v->z = (v->z)->Plus(two->Times(c->Times(d))); + *u = q.RotationU(); + *v = q.RotationV(); } ExprVector Entity::WorkplaneGetOffsetExprs(void) { @@ -98,22 +70,94 @@ void Entity::PlaneGetExprs(ExprVector *n, Expr **dn) { bool Entity::IsPoint(void) { switch(type) { case POINT_IN_3D: - // A point by (x, y, z) in our base coordinate system. These - // variables are given by param[0:2]. case POINT_IN_2D: - // A point by (u, v) in a workplane. These variables are given - // by param[0:1], and the workplane is given in workplane. - case POINT_XFRMD: - // A point by a translation of another point. The original - // point is given by point[0], and the three offsets in - // param[0:2]. - return true; + case POINT_XFRMD: return true; - default: - return false; + default: return false; } } +bool Entity::IsNormal(void) { + switch(type) { + case NORMAL_IN_3D: + case NORMAL_IN_2D: + case NORMAL_XFRMD: return true; + + default: return false; + } +} + +Quaternion Entity::NormalGetNum(void) { + Quaternion q; + switch(type) { + case NORMAL_IN_3D: + 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; + break; + + case NORMAL_IN_2D: { + Entity *wrkpl = SS.GetEntity(workplane); + Entity *norm = SS.GetEntity(wrkpl->normal); + q = norm->NormalGetNum(); + break; + } + case NORMAL_XFRMD: + q = numNormal; + break; + + default: oops(); + } + return q; +} + +void Entity::NormalForceTo(Quaternion q) { + switch(type) { + case NORMAL_IN_3D: + SS.GetParam(param[0])->val = q.w; + SS.GetParam(param[1])->val = q.vx; + SS.GetParam(param[2])->val = q.vy; + SS.GetParam(param[3])->val = q.vz; + break; + + case NORMAL_IN_2D: + case NORMAL_XFRMD: + // There's absolutely nothing to do; these are locked. + break; + + default: oops(); + } +} + +ExprQuaternion Entity::NormalGetExprs(void) { + ExprQuaternion q; + switch(type) { + case NORMAL_IN_3D: + q.w = Expr::FromParam(param[0]); + q.vx = Expr::FromParam(param[1]); + q.vy = Expr::FromParam(param[2]); + q.vz = Expr::FromParam(param[3]); + break; + + case NORMAL_IN_2D: { + Entity *wrkpl = SS.GetEntity(workplane); + Entity *norm = SS.GetEntity(wrkpl->normal); + q = norm->NormalGetExprs(); + break; + } + case NORMAL_XFRMD: + 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; + + default: oops(); + } + return q; +} + bool Entity::PointIsFromReferences(void) { return h.request().IsFromReferences(); } @@ -136,8 +180,7 @@ void Entity::PointForceTo(Vector p) { } case POINT_XFRMD: { - Vector orig = SS.GetEntity(point[0])->PointGetCoords(); - Vector trans = p.Minus(orig); + Vector trans = p.Minus(numPoint); SS.GetParam(param[0])->val = trans.x; SS.GetParam(param[1])->val = trans.y; SS.GetParam(param[2])->val = trans.z; @@ -148,7 +191,7 @@ void Entity::PointForceTo(Vector p) { } } -Vector Entity::PointGetCoords(void) { +Vector Entity::PointGetNum(void) { Vector p; switch(type) { case POINT_IN_3D: @@ -167,7 +210,7 @@ Vector Entity::PointGetCoords(void) { } case POINT_XFRMD: { - p = SS.GetEntity(point[0])->PointGetCoords(); + p = numPoint; p.x += SS.GetParam(param[0])->val; p.y += SS.GetParam(param[1])->val; p.z += SS.GetParam(param[2])->val; @@ -197,7 +240,10 @@ ExprVector Entity::PointGetExprs(void) { break; } case POINT_XFRMD: { - ExprVector orig = SS.GetEntity(point[0])->PointGetExprs(); + ExprVector orig = { + Expr::FromConstant(numPoint.x), + Expr::FromConstant(numPoint.y), + Expr::FromConstant(numPoint.z) }; ExprVector trans; trans.x = Expr::FromParam(param[0]); trans.y = Expr::FromParam(param[1]); @@ -307,13 +353,17 @@ void Entity::DrawOrGetDistance(int order) { } } - Vector v = PointGetCoords(); + Vector v = PointGetNum(); if(dogd.drawing) { double s = 3; Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); + // The usual fudge, to make this appear in front. + Vector gn = SS.GW.projRight.Cross(SS.GW.projUp); + v = v.Plus(gn.ScaledBy(4/SS.GW.scale)); + glxColor3d(0, 0.8, 0); glBegin(GL_QUADS); glxVertex3v(v.Plus (r).Plus (d)); @@ -330,12 +380,42 @@ void Entity::DrawOrGetDistance(int order) { break; } + case NORMAL_IN_3D: + case NORMAL_IN_2D: + case NORMAL_XFRMD: { + if(order >= 0 && order != 2) break; + if(!SS.GW.showNormals) break; + + hRequest hr = h.request(); + double f = 0.5; + if(hr.v == Request::HREQUEST_REFERENCE_XY.v) { + glxColor3d(0, 0, f); + } else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) { + glxColor3d(f, 0, 0); + } else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) { + glxColor3d(0, f, 0); + } else { + glxColor3d(0, 0.4, 0.4); + } + Quaternion q = NormalGetNum(); + Vector tail = SS.GetEntity(point[0])->PointGetNum(); + Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale); + Vector tip = tail.Plus(v); + LineDrawOrGetDistance(tail, tip); + + v = v.WithMagnitude(12); + Vector axis = q.RotationV(); + LineDrawOrGetDistance(tip, tip.Minus(v.RotatedAbout(axis, 0.6))); + LineDrawOrGetDistance(tip, tip.Minus(v.RotatedAbout(axis, -0.6))); + break; + } + case WORKPLANE: { if(order >= 0 && order != 0) break; if(!SS.GW.showWorkplanes) break; Vector p; - p = SS.GetEntity(point[0])->PointGetCoords(); + p = SS.GetEntity(point[0])->PointGetNum(); Vector u, v; WorkplaneGetBasisVectors(&u, &v); @@ -350,7 +430,7 @@ void Entity::DrawOrGetDistance(int order) { Vector mm = p.Minus(us).Minus(vs); Vector mp = p.Minus(us).Plus (vs); - glxColor3d(0, 0.4, 0.4); + glxColor3d(0, 0.3, 0.3); LineDrawOrGetDistance(pp, pm); LineDrawOrGetDistance(pm, mm); LineDrawOrGetDistance(mm, mp); @@ -362,23 +442,28 @@ void Entity::DrawOrGetDistance(int order) { glxOntoWorkplane(u, v); glxWriteText(DescriptionString()); glPopMatrix(); + } else { + // If a line lies in a plane, then select the line, not + // the plane. + dogd.dmin += 3; } break; } case LINE_SEGMENT: { if(order >= 0 && order != 1) break; - Vector a = SS.GetEntity(point[0])->PointGetCoords(); - Vector b = SS.GetEntity(point[1])->PointGetCoords(); + Vector a = SS.GetEntity(point[0])->PointGetNum(); + Vector b = SS.GetEntity(point[1])->PointGetNum(); LineDrawOrGetDistanceOrEdge(a, b); break; } case CUBIC: { - Vector p0 = SS.GetEntity(point[0])->PointGetCoords(); - Vector p1 = SS.GetEntity(point[1])->PointGetCoords(); - Vector p2 = SS.GetEntity(point[2])->PointGetCoords(); - Vector p3 = SS.GetEntity(point[3])->PointGetCoords(); + if(order >= 0 && order != 1) break; + Vector p0 = SS.GetEntity(point[0])->PointGetNum(); + Vector p1 = SS.GetEntity(point[1])->PointGetNum(); + Vector p2 = SS.GetEntity(point[2])->PointGetNum(); + Vector p3 = SS.GetEntity(point[3])->PointGetNum(); int i, n = 20; Vector prev = p0; for(i = 1; i <= n; i++) { @@ -394,6 +479,27 @@ void Entity::DrawOrGetDistance(int order) { break; } + case CIRCLE: { + if(order >= 0 && order != 1) break; + + Quaternion q = SS.GetEntity(normal)->NormalGetNum(); + double r = SS.GetParam(param[0])->val; + Vector center = SS.GetEntity(point[0])->PointGetNum(); + Vector u = q.RotationU(), v = q.RotationV(); + + int i, c = 20; + Vector prev = u.ScaledBy(r).Plus(center); + for(i = 0; i <= c; i++) { + double phi = (2*PI*i)/c; + Vector p = (u.ScaledBy(r*cos(phi))).Plus( + v.ScaledBy(r*sin(phi))); + p = p.Plus(center); + LineDrawOrGetDistanceOrEdge(prev, p); + prev = p; + } + break; + } + default: oops(); } diff --git a/expr.cpp b/expr.cpp index 118569d..2691c8a 100644 --- a/expr.cpp +++ b/expr.cpp @@ -53,7 +53,51 @@ Expr *ExprVector::Magnitude(void) { return r->Sqrt(); } +ExprQuaternion ExprQuaternion::FromExprs(Expr *w, Expr *vx, Expr *vy, Expr *vz) +{ + ExprQuaternion q; + q.w = w; + q.vx = vx; + q.vy = vy; + q.vz = vz; + return q; +} +ExprVector ExprQuaternion::RotationU(void) { + ExprVector u; + Expr *two = Expr::FromConstant(2); + + u.x = w->Square(); + u.x = (u.x)->Plus(vx->Square()); + u.x = (u.x)->Minus(vy->Square()); + u.x = (u.x)->Minus(vz->Square()); + + u.y = two->Times(w->Times(vz)); + u.y = (u.y)->Plus(two->Times(vx->Times(vy))); + + u.z = two->Times(vx->Times(vz)); + u.z = (u.z)->Minus(two->Times(w->Times(vy))); + + return u; +} + +ExprVector ExprQuaternion::RotationV(void) { + ExprVector v; + Expr *two = Expr::FromConstant(2); + + v.x = two->Times(vx->Times(vy)); + v.x = (v.x)->Minus(two->Times(w->Times(vz))); + + v.y = w->Square(); + v.y = (v.y)->Minus(vx->Square()); + v.y = (v.y)->Plus(vy->Square()); + v.y = (v.y)->Minus(vz->Square()); + + v.z = two->Times(w->Times(vx)); + v.z = (v.z)->Plus(two->Times(vy->Times(vz))); + + return v; +} Expr *Expr::FromParam(hParam p) { Expr *r = AllocExpr(); diff --git a/expr.h b/expr.h index 1153f48..44adf2c 100644 --- a/expr.h +++ b/expr.h @@ -131,4 +131,15 @@ public: Expr *Magnitude(void); }; +class ExprQuaternion { +public: + Expr *w, *vx, *vy, *vz; + + static ExprQuaternion FromExprs(Expr *w, Expr *vx, Expr *vy, Expr *vz); + + ExprVector RotationU(void); + ExprVector RotationV(void); +}; + #endif + diff --git a/file.cpp b/file.cpp index c6a783b..cda4037 100644 --- a/file.cpp +++ b/file.cpp @@ -75,8 +75,15 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { '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.direction.v", 'x', &(SS.sv.e.direction.v) }, + { 'e', "Entity.normal.v", 'x', &(SS.sv.e.normal.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) }, { 'c', "Constraint.h.v", 'x', &(SS.sv.c.h.v) }, { 'c', "Constraint.type", 'd', &(SS.sv.c.type) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index 5369880..a0217cb 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -54,13 +54,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "Draw Anywhere in 3d\tQ", MNU_FREE_IN_3D, 'Q', mReq }, { 1, NULL, 0, NULL }, { 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq }, -{ 1, "Datum A&xis\tX", 0, 'X', mReq }, -{ 1, "Datum Pla&ne\tN", 0, 'N', mReq }, -{ 1, "2d Coordinate S&ystem\tY", 0, 'Y', mReq }, +{ 1, "&Workplane (Coordinate S&ystem)\tY", 0, 'Y', mReq }, { 1, NULL, 0, NULL }, { 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq }, { 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', mReq }, -{ 1, "&Circle\tC", 0, 'C', mReq }, +{ 1, "&Circle\tC", MNU_CIRCLE, 'C', mReq }, { 1, "&Arc of a Circle\tA", 0, 'A', mReq }, { 1, "&Cubic Segment\t3", MNU_CUBIC, '3', mReq }, { 1, NULL, 0, NULL }, @@ -107,7 +105,7 @@ void GraphicsWindow::Init(void) { activeWorkplane = r.entity(0); showWorkplanes = true; - showAxes = true; + showNormals = true; showPoints = true; showConstraints = true; showSolids = true; @@ -265,10 +263,7 @@ void GraphicsWindow::MenuEdit(int id) { case MNU_UNSELECT_ALL: HideGraphicsEditControl(); SS.GW.ClearSelection(); - SS.GW.pendingOperation = 0; - SS.GW.pendingDescription = NULL; - SS.GW.pendingPoint.v = 0; - SS.GW.pendingConstraint.v = 0; + SS.GW.ClearPending(); SS.TW.ScreenNavigation('h', 0); SS.TW.Show(); break; @@ -335,7 +330,7 @@ void GraphicsWindow::MenuRequest(int id) { Vector pr, pu; e->WorkplaneGetBasisVectors(&pr, &pu); Quaternion quatf = Quaternion::MakeFrom(pr, pu); - Vector offsetf = SS.GetEntity(e->point[0])->PointGetCoords(); + Vector offsetf = SS.GetEntity(e->point[0])->PointGetNum(); SS.GW.AnimateOnto(quatf, offsetf); SS.GW.EnsureValidActives(); @@ -351,9 +346,10 @@ void GraphicsWindow::MenuRequest(int id) { case MNU_DATUM_POINT: s = "click to place datum point"; goto c; case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c; case MNU_CUBIC: s = "click first point of cubic segment"; goto c; + case MNU_CIRCLE: s = "click center of circle"; goto c; c: - SS.GW.pendingOperation = id; - SS.GW.pendingDescription = s; + SS.GW.pending.operation = id; + SS.GW.pending.description = s; SS.TW.Show(); break; @@ -361,14 +357,14 @@ c: } } -void GraphicsWindow::UpdateDraggedEntity(hEntity hp, double mx, double my) { +void GraphicsWindow::UpdateDraggedPoint(hEntity hp, double mx, double my) { Entity *p = SS.GetEntity(hp); - Vector pos = p->PointGetCoords(); - UpdateDraggedPoint(&pos, mx, my); + Vector pos = p->PointGetNum(); + UpdateDraggedNum(&pos, mx, my); p->PointForceTo(pos); } -void GraphicsWindow::UpdateDraggedPoint(Vector *pos, double mx, double my) { +void GraphicsWindow::UpdateDraggedNum(Vector *pos, double mx, double my) { *pos = pos->Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale)); *pos = pos->Plus(projUp.ScaledBy((my - orig.mouse.y)/scale)); @@ -384,6 +380,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, Point2d mp = { x, y }; + // If the middle button is down, then mouse movement is used to pan and + // rotate our view. This wins over everything else. if(middleDown) { hover.Clear(); @@ -405,7 +403,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, NormalizeProjectionVectors(); } else { - double s = 0.3*(PI/180); // degrees per pixel + double s = 0.3*(PI/180)*scale; // degrees per pixel projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx); projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy); @@ -421,58 +419,123 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, InvalidateGraphics(); return; } - - // Enforce a bit of static friction before we start dragging. - double dm = orig.mouse.DistanceTo(mp); - if(leftDown && dm > 3 && pendingOperation == 0) { - if(hover.entity.v && - SS.GetEntity(hover.entity)->IsPoint() && - !SS.GetEntity(hover.entity)->PointIsFromReferences()) - { - // Start dragging this point. - ClearSelection(); - pendingPoint = hover.entity; - pendingOperation = DRAGGING_POINT; - } else if(hover.constraint.v && - SS.GetConstraint(hover.constraint)->HasLabel()) - { - ClearSelection(); - pendingConstraint = hover.constraint; - pendingOperation = DRAGGING_CONSTRAINT; - } - } else if(leftDown && pendingOperation == DRAGGING_CONSTRAINT) { - Constraint *c = SS.constraint.FindById(pendingConstraint); - UpdateDraggedPoint(&(c->disp.offset), x, y); - } else if(leftDown && pendingOperation == DRAGGING_POINT) { - if(havePainted) { - UpdateDraggedEntity(pendingPoint, x, y); - SS.GenerateAll(solving == SOLVE_ALWAYS); - havePainted = false; + + if(pending.operation == 0) { + double dm = orig.mouse.DistanceTo(mp); + // If we're currently not doing anything, then see if we should + // start dragging something. + if(leftDown && dm > 3) { + if(hover.entity.v) { + Entity *e = SS.GetEntity(hover.entity); + if(e->IsPoint()) { + // Start dragging this point. + ClearSelection(); + pending.point = hover.entity; + pending.operation = DRAGGING_POINT; + } else if(e->type == Entity::CIRCLE) { + // Drag the radius. + ClearSelection(); + pending.circle = hover.entity; + pending.operation = DRAGGING_RADIUS; + } else if(e->IsNormal()) { + ClearSelection(); + pending.normal = hover.entity; + pending.operation = DRAGGING_NORMAL; + } + } else if(hover.constraint.v && + SS.GetConstraint(hover.constraint)->HasLabel()) + { + ClearSelection(); + pending.constraint = hover.constraint; + pending.operation = DRAGGING_CONSTRAINT; + } + } else { + // Otherwise, just hit test and give up + HitTestMakeSelection(mp); } + return; } - // No buttons pressed. - 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) { - UpdateDraggedEntity(pendingPoint, x, y); - HitTestMakeSelection(mp); - - hRequest hr = pendingPoint.request(); - Vector p0 = SS.GetEntity(hr.entity(1))->PointGetCoords(); - Vector p3 = SS.GetEntity(hr.entity(4))->PointGetCoords(); - Vector p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3)); - SS.GetEntity(hr.entity(2))->PointForceTo(p1); - Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3)); - SS.GetEntity(hr.entity(3))->PointForceTo(p2); - } else if(!leftDown) { - // Do our usual hit testing, for the selection. + // If the user has started an operation from the menu, but not + // completed it, then just do the selection. + if(pending.operation < FIRST_PENDING) { HitTestMakeSelection(mp); + return; } + + // We're currently dragging something; so do that. But if we haven't + // painted since the last time we solved, do nothing, because there's + // no sense solving a frame and not displaying it. + if(!havePainted) return; + switch(pending.operation) { + case DRAGGING_CONSTRAINT: { + Constraint *c = SS.constraint.FindById(pending.constraint); + UpdateDraggedNum(&(c->disp.offset), x, y); + break; + } + case DRAGGING_NEW_LINE_POINT: + HitTestMakeSelection(mp); + // and fall through + case DRAGGING_NEW_POINT: + case DRAGGING_POINT: + UpdateDraggedPoint(pending.point, x, y); + break; + + case DRAGGING_NEW_CUBIC_POINT: { + UpdateDraggedPoint(pending.point, x, y); + HitTestMakeSelection(mp); + + hRequest hr = pending.point.request(); + Vector p0 = SS.GetEntity(hr.entity(1))->PointGetNum(); + Vector p3 = SS.GetEntity(hr.entity(4))->PointGetNum(); + Vector p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3)); + SS.GetEntity(hr.entity(2))->PointForceTo(p1); + Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3)); + SS.GetEntity(hr.entity(3))->PointForceTo(p2); + break; + } + case DRAGGING_NEW_RADIUS: + case DRAGGING_RADIUS: { + Entity *circle = SS.GetEntity(pending.circle); + Vector center = SS.GetEntity(circle->point[0])->PointGetNum(); + Point2d c2 = ProjectPoint(center); + SS.GetParam(circle->param[0])->val = c2.DistanceTo(mp)*scale; + break; + } + + case DRAGGING_NORMAL: { + Entity *normal = SS.GetEntity(pending.normal); + Vector p = SS.GetEntity(normal->point[0])->PointGetNum(); + Point2d p2 = ProjectPoint(p); + + Quaternion q = normal->NormalGetNum(); + Vector u = q.RotationU(), v = q.RotationV(); + + if(ctrlDown) { + double theta = atan2(orig.mouse.y-p2.y, orig.mouse.x-p2.x); + theta -= atan2(y-p2.y, x-p2.x); + + Vector normal = orig.projRight.Cross(orig.projUp); + u = u.RotatedAbout(normal, -theta); + v = v.RotatedAbout(normal, -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); + } + orig.mouse = mp; + normal->NormalForceTo(Quaternion::MakeFrom(u, v)); + break; + } + + default: oops(); + } + SS.GenerateAll(solving == SOLVE_ALWAYS); + havePainted = false; } bool GraphicsWindow::Selection::Equals(Selection *b) { @@ -493,6 +556,10 @@ void GraphicsWindow::Selection::Draw(void) { if(constraint.v) SS.GetConstraint(constraint)->Draw(); } +void GraphicsWindow::ClearPending(void) { + memset(&pending, 0, sizeof(pending)); +} + void GraphicsWindow::HitTestMakeSelection(Point2d mp) { int i; double d, dmin = 1e12; @@ -503,7 +570,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) { for(i = 0; i < SS.entity.n; i++) { Entity *e = &(SS.entity.elem[i]); // Don't hover whatever's being dragged. - if(e->h.request().v == pendingPoint.request().v) continue; + if(e->h.request().v == pending.point.request().v) continue; d = e->GetDistance(mp); if(d < 10 && d < dmin) { @@ -599,14 +666,14 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { Constraint::ConstrainCoincident(hover.entity, (p)); \ } hRequest hr; - switch(pendingOperation) { + switch(pending.operation) { case MNU_DATUM_POINT: hr = AddRequest(Request::DATUM_POINT); SS.GetEntity(hr.entity(0))->PointForceTo(v); ClearSelection(); hover.Clear(); - pendingOperation = 0; + pending.operation = 0; break; case MNU_LINE_SEGMENT: @@ -616,10 +683,26 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { ClearSelection(); hover.Clear(); - pendingOperation = DRAGGING_NEW_LINE_POINT; - pendingPoint = hr.entity(2); - pendingDescription = "click to place next point of line"; - SS.GetEntity(pendingPoint)->PointForceTo(v); + pending.operation = DRAGGING_NEW_LINE_POINT; + pending.point = hr.entity(2); + pending.description = "click to place next point of line"; + SS.GetEntity(pending.point)->PointForceTo(v); + break; + + case MNU_CIRCLE: + hr = AddRequest(Request::CIRCLE); + SS.GetEntity(hr.entity(1))->PointForceTo(v); + SS.GetEntity(hr.entity(16))->NormalForceTo( + Quaternion::MakeFrom(SS.GW.projRight, SS.GW.projUp)); + MAYBE_PLACE(hr.entity(1)); + + ClearSelection(); hover.Clear(); + + ClearPending(); + pending.operation = DRAGGING_NEW_RADIUS; + pending.circle = hr.entity(0); + pending.description = "click to set radius"; + SS.GetParam(hr.param(0))->val = 0; break; case MNU_CUBIC: @@ -632,30 +715,28 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { ClearSelection(); hover.Clear(); - pendingOperation = DRAGGING_NEW_CUBIC_POINT; - pendingPoint = hr.entity(4); - pendingDescription = "click to place next point of cubic"; + pending.operation = DRAGGING_NEW_CUBIC_POINT; + pending.point = hr.entity(4); + pending.description = "click to place next point of cubic"; break; + case DRAGGING_RADIUS: case DRAGGING_NEW_POINT: - // The MouseMoved event has already dragged it under the cursor. - pendingOperation = 0; - pendingPoint.v = 0; + // The MouseMoved event has already dragged it as desired. + ClearPending(); break; case DRAGGING_NEW_CUBIC_POINT: if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { - Constraint::ConstrainCoincident(pendingPoint, hover.entity); + Constraint::ConstrainCoincident(pending.point, hover.entity); } - pendingOperation = 0; - pendingPoint.v = 0; + ClearPending(); break; case DRAGGING_NEW_LINE_POINT: { if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { - Constraint::ConstrainCoincident(pendingPoint, hover.entity); - pendingOperation = 0; - pendingPoint.v = 0; + Constraint::ConstrainCoincident(pending.point, hover.entity); + ClearPending(); break; } // Create a new line segment, so that we continue drawing. @@ -663,23 +744,20 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(hr.entity(1))->PointForceTo(v); // Constrain the line segments to share an endpoint - Constraint::ConstrainCoincident(pendingPoint, hr.entity(1)); + Constraint::ConstrainCoincident(pending.point, hr.entity(1)); // And drag an endpoint of the new line segment - pendingOperation = DRAGGING_NEW_LINE_POINT; - pendingPoint = hr.entity(2); - pendingDescription = "click to place next point of next line"; - SS.GetEntity(pendingPoint)->PointForceTo(v); + pending.operation = DRAGGING_NEW_LINE_POINT; + pending.point = hr.entity(2); + pending.description = "click to place next point of next line"; + SS.GetEntity(pending.point)->PointForceTo(v); break; } case 0: default: { - pendingOperation = 0; - pendingPoint.v = 0; - pendingConstraint.v = 0; - pendingDescription = NULL; + ClearPending(); if(hover.IsEmpty()) break; @@ -710,12 +788,12 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { } void GraphicsWindow::MouseLeftUp(double mx, double my) { - switch(pendingOperation) { + switch(pending.operation) { case DRAGGING_POINT: case DRAGGING_CONSTRAINT: - pendingOperation = 0; - pendingPoint.v = 0; - pendingConstraint.v = 0; + case DRAGGING_NORMAL: + case DRAGGING_RADIUS: + ClearPending(); break; default: @@ -781,9 +859,9 @@ void GraphicsWindow::ToggleBool(int link, DWORD v) { } void GraphicsWindow::ToggleAnyDatumShown(int link, DWORD v) { - bool t = !(SS.GW.showWorkplanes && SS.GW.showAxes && SS.GW.showPoints); + bool t = !(SS.GW.showWorkplanes && SS.GW.showNormals && SS.GW.showPoints); SS.GW.showWorkplanes = t; - SS.GW.showAxes = t; + SS.GW.showNormals = t; SS.GW.showPoints = t; SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS); @@ -809,7 +887,7 @@ void GraphicsWindow::Paint(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glScaled(scale*2.0/w, scale*2.0/h, scale*2.0/w); + glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/50000); double tx = projRight.Dot(offset); double ty = projUp.Dot(offset); diff --git a/sketch.cpp b/sketch.cpp index d51abbe..15c8535 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -68,7 +68,7 @@ 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)); + CopyEntity(e->h, 0, h.param(0), h.param(1), h.param(2), true); } break; @@ -93,7 +93,9 @@ hEntity Group::Remap(hEntity in, int copyNumber) { return h.entity(em.h.v); } -void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz) { +void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, + bool isExtrusion) +{ Entity *ep = SS.GetEntity(in); Entity en; @@ -119,13 +121,36 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz) { en.point[3] = Remap(ep->point[3], a); break; + case Entity::CIRCLE: + en.point[0] = Remap(ep->point[0], a); + en.normal = Remap(ep->normal, a); + en.param[0] = ep->param[0]; // XXX make numerical somehow later + break; + case Entity::POINT_IN_3D: case Entity::POINT_IN_2D: en.type = Entity::POINT_XFRMD; - en.point[0] = ep->h; en.param[0] = dx; en.param[1] = dy; en.param[2] = dz; + en.numPoint = ep->PointGetNum(); + + if(isExtrusion) { + if(a != 0) oops(); + SS.entity.Add(&en); + en.point[0] = ep->h; + en.point[1] = en.h; + en.h = Remap(ep->h, 1); + en.type = Entity::LINE_SEGMENT; + // And then this line segment gets added + } + break; + + case Entity::NORMAL_IN_3D: + case Entity::NORMAL_IN_2D: + en.type = Entity::NORMAL_XFRMD; + en.numNormal = ep->NormalGetNum(); + en.point[0] = Remap(ep->point[0], a); break; default: @@ -242,67 +267,104 @@ void Request::Generate(IdList *entity, int points = 0; int params = 0; int et = 0; + bool hasNormal = false; int i; - Group *g = SS.group.FindById(group); - Entity e; memset(&e, 0, sizeof(e)); switch(type) { case Request::WORKPLANE: - et = Entity::WORKPLANE; points = 1; params = 4; goto c; + et = Entity::WORKPLANE; + points = 1; + hasNormal = true; + break; case Request::DATUM_POINT: - et = 0; points = 1; params = 0; goto c; + et = 0; + points = 1; + break; case Request::LINE_SEGMENT: - et = Entity::LINE_SEGMENT; points = 2; params = 0; goto c; + et = Entity::LINE_SEGMENT; + points = 2; + break; + + case Request::CIRCLE: + et = Entity::CIRCLE; + points = 1; + params = 1; + hasNormal = true; + break; case Request::CUBIC: - et = Entity::CUBIC; points = 4; params = 0; goto c; -c: { - // Generate the entity that's specific to this request. - e.type = et; - e.group = group; - e.h = h.entity(0); - - // And generate entities for the points - for(i = 0; i < points; i++) { - Entity p; - memset(&p, 0, sizeof(p)); - p.workplane = workplane; - // points start from entity 1, except for datum point case - p.h = h.entity(i+(et ? 1 : 0)); - p.group = group; - - if(workplane.v == Entity::FREE_IN_3D.v) { - p.type = Entity::POINT_IN_3D; - // params for x y z - p.param[0] = AddParam(param, h.param(16 + 3*i + 0)); - p.param[1] = AddParam(param, h.param(16 + 3*i + 1)); - p.param[2] = AddParam(param, h.param(16 + 3*i + 2)); - } else { - p.type = Entity::POINT_IN_2D; - // params for u v - p.param[0] = AddParam(param, h.param(16 + 3*i + 0)); - p.param[1] = AddParam(param, h.param(16 + 3*i + 1)); - } - entity->Add(&p); - e.point[i] = p.h; - } - // And generate any params not associated with the point that - // we happen to need. - for(i = 0; i < params; i++) { - e.param[i] = AddParam(param, h.param(i)); - } - - if(et) entity->Add(&e); + et = Entity::CUBIC; + points = 4; break; - } - default: - oops(); + default: oops(); } + + // Generate the entity that's specific to this request. + e.type = et; + e.group = group; + e.workplane = workplane; + e.h = h.entity(0); + + // And generate entities for the points + for(i = 0; i < points; i++) { + Entity p; + memset(&p, 0, sizeof(p)); + p.workplane = workplane; + // points start from entity 1, except for datum point case + p.h = h.entity(i+(et ? 1 : 0)); + p.group = group; + + if(workplane.v == Entity::FREE_IN_3D.v) { + p.type = Entity::POINT_IN_3D; + // params for x y z + p.param[0] = AddParam(param, h.param(16 + 3*i + 0)); + p.param[1] = AddParam(param, h.param(16 + 3*i + 1)); + p.param[2] = AddParam(param, h.param(16 + 3*i + 2)); + } else { + p.type = Entity::POINT_IN_2D; + // params for u v + p.param[0] = AddParam(param, h.param(16 + 3*i + 0)); + p.param[1] = AddParam(param, h.param(16 + 3*i + 1)); + } + entity->Add(&p); + e.point[i] = p.h; + } + if(hasNormal) { + Entity n; + memset(&n, 0, sizeof(n)); + n.workplane = workplane; + n.h = h.entity(16); + n.group = group; + if(workplane.v == Entity::FREE_IN_3D.v) { + n.type = Entity::NORMAL_IN_3D; + n.param[0] = AddParam(param, h.param(32+0)); + n.param[1] = AddParam(param, h.param(32+1)); + n.param[2] = AddParam(param, h.param(32+2)); + n.param[3] = AddParam(param, h.param(32+3)); + } else { + n.type = Entity::NORMAL_IN_2D; + // and this is just a copy of the workplane quaternion, + // so no params required + } + if(points < 1) oops(); + // The point determines where the normal gets displayed on-screen; + // it's entirely cosmetic. + n.point[0] = e.point[0]; + entity->Add(&n); + e.normal = n.h; + } + // And generate any params not associated with the point that + // we happen to need. + for(i = 0; i < params; i++) { + e.param[i] = AddParam(param, h.param(i)); + } + + if(et) entity->Add(&e); } char *Request::DescriptionString(void) { diff --git a/sketch.h b/sketch.h index 9c7b07c..d8b42ce 100644 --- a/sketch.h +++ b/sketch.h @@ -103,7 +103,8 @@ public: // mapping list. IdList remap; hEntity Remap(hEntity in, int copyNumber); - void CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz); + void CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, + bool isExtrusion); void MakePolygons(void); void Draw(void); @@ -130,6 +131,7 @@ public: static const int DATUM_POINT = 101; static const int LINE_SEGMENT = 200; static const int CUBIC = 300; + static const int CIRCLE = 400; int type; @@ -152,22 +154,30 @@ public: static const hEntity FREE_IN_3D; - static const int WORKPLANE = 1000; static const int POINT_IN_3D = 2000; static const int POINT_IN_2D = 2001; static const int POINT_XFRMD = 2010; - static const int LINE_SEGMENT = 10000; - static const int CUBIC = 11000; - static const int EDGE_LIST = 90000; - static const int FACE_LIST = 91000; + static const int NORMAL_IN_3D = 3000; + static const int NORMAL_IN_2D = 3001; + static const int NORMAL_XFRMD = 3010; + + static const int WORKPLANE = 10000; + static const int LINE_SEGMENT = 11000; + static const int CUBIC = 12000; + static const int CIRCLE = 13000; + int type; // When it comes time to draw an entity, we look here to get the // defining variables. hParam param[4]; hEntity point[4]; - hEntity direction; + hEntity normal; + + // Derived points are a symbolic offset from a constant base. + Vector numPoint; + Quaternion numNormal; hGroup group; hEntity workplane; // or Entity::FREE_IN_3D @@ -184,12 +194,18 @@ public: bool IsPoint(void); // Applies for any of the point types - Vector PointGetCoords(void); + Vector PointGetNum(void); ExprVector PointGetExprs(void); void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v); void PointForceTo(Vector v); bool PointIsFromReferences(void); + bool IsNormal(void); + // Applies for any of the normal types + Quaternion NormalGetNum(void); + ExprQuaternion NormalGetExprs(void); + void NormalForceTo(Quaternion q); + // Applies for anything that comes with a plane bool HasPlane(void); // The plane is points P such that P dot (xn, yn, zn) - d = 0 diff --git a/solvespace.cpp b/solvespace.cpp index 4f203e3..926b9aa 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -72,28 +72,29 @@ void SolveSpace::ForceReferences(void) { // Force the values of the paramters that define the three reference // coordinate systems. static const struct { - hRequest hr; - double a, b, c, d; + hRequest hr; + Quaternion q; } Quat[] = { - { Request::HREQUEST_REFERENCE_XY, 1, 0, 0, 0, }, - { Request::HREQUEST_REFERENCE_YZ, 0.5, 0.5, 0.5, 0.5, }, - { Request::HREQUEST_REFERENCE_ZX, 0.5, -0.5, -0.5, -0.5, }, + { Request::HREQUEST_REFERENCE_XY, { 1, 0, 0, 0, } }, + { Request::HREQUEST_REFERENCE_YZ, { 0.5, 0.5, 0.5, 0.5, } }, + { Request::HREQUEST_REFERENCE_ZX, { 0.5, -0.5, -0.5, -0.5, } }, }; for(int i = 0; i < 3; i++) { hRequest hr = Quat[i].hr; + Entity *wrkpl = GetEntity(hr.entity(0)); // The origin for our coordinate system, always zero - Vector v = Vector::MakeFrom(0, 0, 0); - Entity *origin = GetEntity(hr.entity(1)); - origin->PointForceTo(v); + Entity *origin = GetEntity(wrkpl->point[0]); + origin->PointForceTo(Vector::MakeFrom(0, 0, 0)); GetParam(origin->param[0])->known = true; GetParam(origin->param[1])->known = true; GetParam(origin->param[2])->known = true; // The quaternion that defines the rotation, from the table. - Param *p; - p = GetParam(hr.param(0)); p->val = Quat[i].a; p->known = true; - p = GetParam(hr.param(1)); p->val = Quat[i].b; p->known = true; - p = GetParam(hr.param(2)); p->val = Quat[i].c; p->known = true; - p = GetParam(hr.param(3)); p->val = Quat[i].d; p->known = true; + Entity *normal = GetEntity(wrkpl->normal); + normal->NormalForceTo(Quat[i].q); + GetParam(normal->param[0])->known = true; + GetParam(normal->param[1])->known = true; + GetParam(normal->param[2])->known = true; + GetParam(normal->param[3])->known = true; } } diff --git a/solvespace.h b/solvespace.h index d1d1466..f7cc433 100644 --- a/solvespace.h +++ b/solvespace.h @@ -27,6 +27,7 @@ typedef signed long SDWORD; class Expr; class ExprVector; +class ExprQuaternion; // From the platform-specific code. int SaveFileYesNoCancel(void); diff --git a/system.cpp b/system.cpp index 6668165..57cba6a 100644 --- a/system.cpp +++ b/system.cpp @@ -86,8 +86,8 @@ void System::SortBySensitivity(void) { mat.dragged[j] = false; mat.permutation[j] = j; } - if(SS.GW.pendingPoint.v) { - Entity *p = SS.entity.FindByIdNoOops(SS.GW.pendingPoint); + if(SS.GW.pending.point.v) { + Entity *p = SS.entity.FindByIdNoOops(SS.GW.pending.point); // If we're solving an earlier group, then the pending point might // not exist in the entity tables yet. if(p) { diff --git a/textwin.cpp b/textwin.cpp index 21b9ae0..4efebb8 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -158,15 +158,15 @@ done: } void TextWindow::Show(void) { - if(!(SS.GW.pendingOperation)) SS.GW.pendingDescription = NULL; + if(!(SS.GW.pending.operation)) SS.GW.ClearPending(); ShowHeader(); - if(SS.GW.pendingDescription) { + if(SS.GW.pending.description) { // A pending operation (that must be completed with the mouse in // the graphics window) will preempt our usual display. Printf(false, ""); - Printf(false, "%s", SS.GW.pendingDescription); + Printf(false, "%s", SS.GW.pending.description); } else { switch(shown->screen) { default: @@ -222,7 +222,7 @@ void TextWindow::ShowHeader(void) { SS.GetEntity(SS.GW.activeWorkplane)->DescriptionString(); // Navigation buttons - if(SS.GW.pendingDescription) { + if(SS.GW.pending.description) { Printf(false, " %Bt%Ft workplane:%Fd %s", cd); } else { Printf(false, " %Lb%f<<%E %Lh%fhome%E %Bt%Ft workplane:%Fd %s", @@ -232,9 +232,9 @@ void TextWindow::ShowHeader(void) { } int datumColor; - if(SS.GW.showWorkplanes && SS.GW.showAxes && SS.GW.showPoints) { + if(SS.GW.showWorkplanes && SS.GW.showNormals && SS.GW.showPoints) { datumColor = 's'; // shown - } else if(!(SS.GW.showWorkplanes || SS.GW.showAxes || SS.GW.showPoints)) { + } else if(!(SS.GW.showWorkplanes || SS.GW.showNormals || SS.GW.showPoints)){ datumColor = 'h'; // hidden } else { datumColor = 'm'; // mixed @@ -243,11 +243,11 @@ void TextWindow::ShowHeader(void) { #define hs(b) ((b) ? 's' : 'h') Printf(false, "%Bt%Ftshow: " "%Fp%Ll%D%fworkplanes%E " - "%Fp%Ll%D%fvectors%E " + "%Fp%Ll%D%fnormals%E " "%Fp%Ll%D%fpoints%E " "%Fp%Ll%fany-datum%E", hs(SS.GW.showWorkplanes), (DWORD)&(SS.GW.showWorkplanes), &(SS.GW.ToggleBool), - hs(SS.GW.showAxes), (DWORD)&(SS.GW.showAxes), &(SS.GW.ToggleBool), + hs(SS.GW.showNormals), (DWORD)&(SS.GW.showNormals), &(SS.GW.ToggleBool), hs(SS.GW.showPoints), (DWORD)&(SS.GW.showPoints), &(SS.GW.ToggleBool), datumColor, &(SS.GW.ToggleAnyDatumShown) ); diff --git a/ui.h b/ui.h index ef9871d..b199bee 100644 --- a/ui.h +++ b/ui.h @@ -103,6 +103,7 @@ public: MNU_FREE_IN_3D, MNU_DATUM_POINT, MNU_LINE_SEGMENT, + MNU_CIRCLE, MNU_RECTANGLE, MNU_CUBIC, // Group @@ -169,20 +170,31 @@ public: void EnsureValidActives(); // Operations that must be completed by doing something with the mouse - // are noted here. + // are noted here. These occupy the same space as the menu ids. + static const int FIRST_PENDING = 0x0f000000; static const int DRAGGING_POINT = 0x0f000000; static const int DRAGGING_NEW_POINT = 0x0f000001; static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003; static const int DRAGGING_CONSTRAINT = 0x0f000004; - hEntity pendingPoint; - hConstraint pendingConstraint; - int pendingOperation; - char *pendingDescription; - hRequest AddRequest(int type); + static const int DRAGGING_RADIUS = 0x0f000005; + static const int DRAGGING_NORMAL = 0x0f000006; + static const int DRAGGING_NEW_RADIUS = 0x0f000007; + struct { + int operation; + hEntity point; + hEntity circle; + hEntity normal; + hConstraint constraint; + + char *description; + } pending; + void ClearPending(void); // The constraint that is being edited with the on-screen textbox. hConstraint constraintBeingEdited; + + hRequest AddRequest(int type); // The current selection. class Selection { @@ -215,7 +227,7 @@ public: // This sets what gets displayed. bool showWorkplanes; - bool showAxes; + bool showNormals; bool showPoints; bool showConstraints; bool showTextWindow; @@ -228,8 +240,8 @@ public: static const int SOLVE_ALWAYS = 1; int solving; - void UpdateDraggedPoint(Vector *pos, double mx, double my); - void UpdateDraggedEntity(hEntity hp, double mx, double my); + void UpdateDraggedNum(Vector *pos, double mx, double my); + void UpdateDraggedPoint(hEntity hp, double mx, double my); // These are called by the platform-specific code. void Paint(int w, int h); diff --git a/util.cpp b/util.cpp index 8875bf9..bfe2987 100644 --- a/util.cpp +++ b/util.cpp @@ -23,12 +23,12 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, mat[15] = a44; } -Quaternion Quaternion::MakeFrom(double a, double b, double c, double d) { +Quaternion Quaternion::MakeFrom(double w, double vx, double vy, double vz) { Quaternion q; - q.a = a; - q.b = b; - q.c = c; - q.d = d; + q.w = w; + q.vx = vx; + q.vy = vy; + q.vz = vz; return q; } @@ -40,65 +40,65 @@ Quaternion Quaternion::MakeFrom(Vector u, Vector v) double s, tr = 1 + u.x + v.y + n.z; if(tr > 1e-4) { s = 2*sqrt(tr); - q.a = s/4; - q.b = (v.z - n.y)/s; - q.c = (n.x - u.z)/s; - q.d = (u.y - v.x)/s; + q.w = s/4; + q.vx = (v.z - n.y)/s; + q.vy = (n.x - u.z)/s; + q.vz = (u.y - v.x)/s; } else { double m = max(u.x, max(v.y, n.z)); if(m == u.x) { s = 2*sqrt(1 + u.x - v.y - n.z); - q.a = (v.z - n.y)/s; - q.b = s/4; - q.c = (u.y + v.x)/s; - q.d = (n.x + u.z)/s; + q.w = (v.z - n.y)/s; + q.vx = s/4; + q.vy = (u.y + v.x)/s; + q.vz = (n.x + u.z)/s; } else if(m == v.y) { s = 2*sqrt(1 - u.x + v.y - n.z); - q.a = (n.x - u.z)/s; - q.b = (u.y + v.x)/s; - q.c = s/4; - q.d = (v.z + n.y)/s; + q.w = (n.x - u.z)/s; + q.vx = (u.y + v.x)/s; + q.vy = s/4; + q.vz = (v.z + n.y)/s; } else if(m == n.z) { s = 2*sqrt(1 - u.x - v.y + n.z); - q.a = (u.y - v.x)/s; - q.b = (n.x + u.z)/s; - q.c = (v.z + n.y)/s; - q.d = s/4; + q.w = (u.y - v.x)/s; + q.vx = (n.x + u.z)/s; + q.vy = (v.z + n.y)/s; + q.vz = s/4; } else oops(); } return q.WithMagnitude(1); } -Quaternion Quaternion::Plus(Quaternion y) { +Quaternion Quaternion::Plus(Quaternion b) { Quaternion q; - q.a = a + y.a; - q.b = b + y.b; - q.c = c + y.c; - q.d = d + y.d; + q.w = w + b.w; + q.vx = vx + b.vx; + q.vy = vy + b.vy; + q.vz = vz + b.vz; return q; } -Quaternion Quaternion::Minus(Quaternion y) { +Quaternion Quaternion::Minus(Quaternion b) { Quaternion q; - q.a = a - y.a; - q.b = b - y.b; - q.c = c - y.c; - q.d = d - y.d; + q.w = w - b.w; + q.vx = vx - b.vx; + q.vy = vy - b.vy; + q.vz = vz - b.vz; return q; } Quaternion Quaternion::ScaledBy(double s) { Quaternion q; - q.a = a*s; - q.b = b*s; - q.c = c*s; - q.d = d*s; + q.w = w*s; + q.vx = vx*s; + q.vy = vy*s; + q.vz = vz*s; return q; } double Quaternion::Magnitude(void) { - return sqrt(a*a + b*b + c*c + d*d); + return sqrt(w*w + vx*vx + vy*vy + vz*vz); } Quaternion Quaternion::WithMagnitude(double s) { @@ -107,20 +107,24 @@ Quaternion Quaternion::WithMagnitude(double s) { Vector Quaternion::RotationU(void) { Vector v; - v.x = a*a + b*b - c*c - d*d; - v.y = 2*a*d + 2*b*c; - v.z = 2*b*d - 2*a*c; + v.x = w*w + vx*vx - vy*vy - vz*vz; + v.y = 2*w *vz + 2*vx*vy; + v.z = 2*vx*vz - 2*w *vy; return v; } Vector Quaternion::RotationV(void) { Vector v; - v.x = 2*b*c - 2*a*d; - v.y = a*a - b*b + c*c - d*d; - v.z = 2*a*b + 2*c*d; + v.x = 2*vx*vy - 2*w*vz; + v.y = w*w - vx*vx + vy*vy - vz*vz; + v.z = 2*w*vx + 2*vy*vz; return v; } +Vector Quaternion::RotationN(void) { + return RotationU().Cross(RotationV()); +} + Vector Vector::MakeFrom(double x, double y, double z) { Vector v; @@ -199,17 +203,13 @@ Vector Vector::Normal(int which) { n.z = 0; n.x = y; n.y = -x; - } else { - oops(); - } + } else oops(); if(which == 0) { // That's the vector we return. } else if(which == 1) { n = this->Cross(n); - } else { - oops(); - } + } else oops(); n = n.WithMagnitude(1);