A big change, to add a concept of normals. These are "oriented

vectors", represented by unit quaternions. This permits me to add
circles, where the normal defines the plane of the circle.

Still many things painful. The interface for editing normals is not
so intuitive, and it's not yet clear how I would e.g. export a
circle entity and recreate it properly, since that entity has a
param not associated with a normal or point.

And the transformed points/normals do not yet support rotations.
That will be necessary soon.

[git-p4: depot-paths = "//depot/solvespace/": change = 1705]
This commit is contained in:
Jonathan Westhues 2008-05-04 22:18:01 -08:00
parent 01885736e6
commit 853c6cb59c
16 changed files with 653 additions and 316 deletions

View File

@ -49,8 +49,8 @@ void Constraint::MenuConstrain(int id) {
return; return;
} }
Vector n = SS.GW.projRight.Cross(SS.GW.projUp); Vector n = SS.GW.projRight.Cross(SS.GW.projUp);
Vector a = SS.GetEntity(c.ptA)->PointGetCoords(); Vector a = SS.GetEntity(c.ptA)->PointGetNum();
Vector b = SS.GetEntity(c.ptB)->PointGetCoords(); 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)).WithMagnitude(50);
c.exprA = Expr::FromString("0")->DeepCopyKeep(); c.exprA = Expr::FromString("0")->DeepCopyKeep();

View File

@ -41,8 +41,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
glxColor3d(1, 0.2, 1); glxColor3d(1, 0.2, 1);
switch(type) { switch(type) {
case PT_PT_DISTANCE: { case PT_PT_DISTANCE: {
Vector ap = SS.GetEntity(ptA)->PointGetCoords(); Vector ap = SS.GetEntity(ptA)->PointGetNum();
Vector bp = SS.GetEntity(ptB)->PointGetCoords(); Vector bp = SS.GetEntity(ptB)->PointGetNum();
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
if(labelPos) *labelPos = ref; if(labelPos) *labelPos = ref;
@ -74,8 +74,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
case POINTS_COINCIDENT: { case POINTS_COINCIDENT: {
if(!dogd.drawing) { if(!dogd.drawing) {
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum();
PointGetCoords();
Point2d pp = SS.GW.ProjectPoint(p); Point2d pp = SS.GW.ProjectPoint(p);
// The point is selected within a radius of 7, from the // The point is selected within a radius of 7, from the
// same center; so if the point is visible, then this // 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 r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale);
Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale);
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> Vector p = SS.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum();
PointGetCoords();
glxColor3d(0.4, 0, 0.4); glxColor3d(0.4, 0, 0.4);
glBegin(GL_QUADS); glBegin(GL_QUADS);
glxVertex3v(p.Plus (r).Plus (d)); glxVertex3v(p.Plus (r).Plus (d));
@ -107,7 +105,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
case PT_ON_LINE: case PT_ON_LINE:
case PT_IN_PLANE: { case PT_IN_PLANE: {
double s = 7; double s = 7;
Vector p = SS.GetEntity(ptA)->PointGetCoords(); Vector p = SS.GetEntity(ptA)->PointGetNum();
Vector r = gr.WithMagnitude(s); Vector r = gr.WithMagnitude(s);
Vector d = gu.WithMagnitude(s); Vector d = gu.WithMagnitude(s);
LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d)); LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d));
@ -120,8 +118,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
case EQUAL_LENGTH_LINES: { case EQUAL_LENGTH_LINES: {
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);
Vector a = SS.GetEntity(e->point[0])->PointGetCoords(); Vector a = SS.GetEntity(e->point[0])->PointGetNum();
Vector b = SS.GetEntity(e->point[1])->PointGetCoords(); Vector b = SS.GetEntity(e->point[1])->PointGetNum();
Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3)); Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3));
Vector ab = a.Minus(b); Vector ab = a.Minus(b);
Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale); Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale);
@ -132,8 +130,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
} }
case SYMMETRIC: { case SYMMETRIC: {
Vector a = SS.GetEntity(ptA)->PointGetCoords(); Vector a = SS.GetEntity(ptA)->PointGetNum();
Vector b = SS.GetEntity(ptB)->PointGetCoords(); Vector b = SS.GetEntity(ptB)->PointGetNum();
Vector n = SS.GetEntity(entityA)->WorkplaneGetNormalVector(); Vector n = SS.GetEntity(entityA)->WorkplaneGetNormalVector();
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Vector tail = (i == 0) ? a : b; Vector tail = (i == 0) ? a : b;
@ -168,8 +166,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
} }
// For "at midpoint", this branch is always taken. // For "at midpoint", this branch is always taken.
Entity *e = SS.GetEntity(entityA); Entity *e = SS.GetEntity(entityA);
Vector a = SS.GetEntity(e->point[0])->PointGetCoords(); Vector a = SS.GetEntity(e->point[0])->PointGetNum();
Vector b = SS.GetEntity(e->point[1])->PointGetCoords(); Vector b = SS.GetEntity(e->point[1])->PointGetNum();
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5)); Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
Vector offset = (a.Minus(b)).Cross(n); Vector offset = (a.Minus(b)).Cross(n);
offset = offset.WithMagnitude(13/SS.GW.scale); 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); dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
} }
} else { } else {
Vector a = SS.GetEntity(ptA)->PointGetCoords(); Vector a = SS.GetEntity(ptA)->PointGetNum();
Vector b = SS.GetEntity(ptB)->PointGetCoords(); Vector b = SS.GetEntity(ptB)->PointGetNum();
Entity *w = SS.GetEntity(SS.GetEntity(ptA)->workplane); Entity *w = SS.GetEntity(SS.GetEntity(ptA)->workplane);
Vector cu, cv, cn; Vector cu, cv, cn;

11
dsc.h
View File

@ -9,14 +9,14 @@ class Vector;
class Quaternion { class Quaternion {
public: public:
// a + bi + cj + dk // a + (vx)*i + (vy)*j + (vz)*k
double a, b, c, d; 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); static Quaternion MakeFrom(Vector u, Vector v);
Quaternion Plus(Quaternion y); Quaternion Plus(Quaternion b);
Quaternion Minus(Quaternion y); Quaternion Minus(Quaternion b);
Quaternion ScaledBy(double s); Quaternion ScaledBy(double s);
double Magnitude(void); double Magnitude(void);
Quaternion WithMagnitude(double s); Quaternion WithMagnitude(double s);
@ -25,6 +25,7 @@ public:
// second rows, where that matrix is generated by this quaternion // second rows, where that matrix is generated by this quaternion
Vector RotationU(void); Vector RotationU(void);
Vector RotationV(void); Vector RotationV(void);
Vector RotationN(void);
}; };
class Vector { class Vector {

View File

@ -6,11 +6,7 @@ char *Entity::DescriptionString(void) {
} }
void Entity::WorkplaneGetBasisVectors(Vector *u, Vector *v) { void Entity::WorkplaneGetBasisVectors(Vector *u, Vector *v) {
double q[4]; Quaternion quat = SS.GetEntity(normal)->NormalGetNum();
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]);
*u = quat.RotationU(); *u = quat.RotationU();
*v = quat.RotationV(); *v = quat.RotationV();
@ -23,34 +19,10 @@ Vector Entity::WorkplaneGetNormalVector(void) {
} }
void Entity::WorkplaneGetBasisExprs(ExprVector *u, ExprVector *v) { void Entity::WorkplaneGetBasisExprs(ExprVector *u, ExprVector *v) {
Expr *a = Expr::FromParam(param[0]); ExprQuaternion q = SS.GetEntity(normal)->NormalGetExprs();
Expr *b = Expr::FromParam(param[1]);
Expr *c = Expr::FromParam(param[2]);
Expr *d = Expr::FromParam(param[3]);
Expr *two = Expr::FromConstant(2); *u = q.RotationU();
*v = q.RotationV();
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)));
} }
ExprVector Entity::WorkplaneGetOffsetExprs(void) { ExprVector Entity::WorkplaneGetOffsetExprs(void) {
@ -98,22 +70,94 @@ void Entity::PlaneGetExprs(ExprVector *n, Expr **dn) {
bool Entity::IsPoint(void) { bool Entity::IsPoint(void) {
switch(type) { switch(type) {
case POINT_IN_3D: 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: case POINT_IN_2D:
// A point by (u, v) in a workplane. These variables are given case POINT_XFRMD: return true;
// 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;
default: default: return false;
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) { bool Entity::PointIsFromReferences(void) {
return h.request().IsFromReferences(); return h.request().IsFromReferences();
} }
@ -136,8 +180,7 @@ void Entity::PointForceTo(Vector p) {
} }
case POINT_XFRMD: { case POINT_XFRMD: {
Vector orig = SS.GetEntity(point[0])->PointGetCoords(); Vector trans = p.Minus(numPoint);
Vector trans = p.Minus(orig);
SS.GetParam(param[0])->val = trans.x; SS.GetParam(param[0])->val = trans.x;
SS.GetParam(param[1])->val = trans.y; SS.GetParam(param[1])->val = trans.y;
SS.GetParam(param[2])->val = trans.z; 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; Vector p;
switch(type) { switch(type) {
case POINT_IN_3D: case POINT_IN_3D:
@ -167,7 +210,7 @@ Vector Entity::PointGetCoords(void) {
} }
case POINT_XFRMD: { case POINT_XFRMD: {
p = SS.GetEntity(point[0])->PointGetCoords(); p = numPoint;
p.x += SS.GetParam(param[0])->val; p.x += SS.GetParam(param[0])->val;
p.y += SS.GetParam(param[1])->val; p.y += SS.GetParam(param[1])->val;
p.z += SS.GetParam(param[2])->val; p.z += SS.GetParam(param[2])->val;
@ -197,7 +240,10 @@ ExprVector Entity::PointGetExprs(void) {
break; break;
} }
case POINT_XFRMD: { 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; ExprVector trans;
trans.x = Expr::FromParam(param[0]); trans.x = Expr::FromParam(param[0]);
trans.y = Expr::FromParam(param[1]); trans.y = Expr::FromParam(param[1]);
@ -307,13 +353,17 @@ void Entity::DrawOrGetDistance(int order) {
} }
} }
Vector v = PointGetCoords(); Vector v = PointGetNum();
if(dogd.drawing) { if(dogd.drawing) {
double s = 3; double s = 3;
Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
Vector d = SS.GW.projUp.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); glxColor3d(0, 0.8, 0);
glBegin(GL_QUADS); glBegin(GL_QUADS);
glxVertex3v(v.Plus (r).Plus (d)); glxVertex3v(v.Plus (r).Plus (d));
@ -330,12 +380,42 @@ void Entity::DrawOrGetDistance(int order) {
break; 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: { case WORKPLANE: {
if(order >= 0 && order != 0) break; if(order >= 0 && order != 0) break;
if(!SS.GW.showWorkplanes) break; if(!SS.GW.showWorkplanes) break;
Vector p; Vector p;
p = SS.GetEntity(point[0])->PointGetCoords(); p = SS.GetEntity(point[0])->PointGetNum();
Vector u, v; Vector u, v;
WorkplaneGetBasisVectors(&u, &v); WorkplaneGetBasisVectors(&u, &v);
@ -350,7 +430,7 @@ void Entity::DrawOrGetDistance(int order) {
Vector mm = p.Minus(us).Minus(vs); Vector mm = p.Minus(us).Minus(vs);
Vector mp = p.Minus(us).Plus (vs); Vector mp = p.Minus(us).Plus (vs);
glxColor3d(0, 0.4, 0.4); glxColor3d(0, 0.3, 0.3);
LineDrawOrGetDistance(pp, pm); LineDrawOrGetDistance(pp, pm);
LineDrawOrGetDistance(pm, mm); LineDrawOrGetDistance(pm, mm);
LineDrawOrGetDistance(mm, mp); LineDrawOrGetDistance(mm, mp);
@ -362,23 +442,28 @@ void Entity::DrawOrGetDistance(int order) {
glxOntoWorkplane(u, v); glxOntoWorkplane(u, v);
glxWriteText(DescriptionString()); glxWriteText(DescriptionString());
glPopMatrix(); glPopMatrix();
} else {
// If a line lies in a plane, then select the line, not
// the plane.
dogd.dmin += 3;
} }
break; break;
} }
case LINE_SEGMENT: { case LINE_SEGMENT: {
if(order >= 0 && order != 1) break; if(order >= 0 && order != 1) break;
Vector a = SS.GetEntity(point[0])->PointGetCoords(); Vector a = SS.GetEntity(point[0])->PointGetNum();
Vector b = SS.GetEntity(point[1])->PointGetCoords(); Vector b = SS.GetEntity(point[1])->PointGetNum();
LineDrawOrGetDistanceOrEdge(a, b); LineDrawOrGetDistanceOrEdge(a, b);
break; break;
} }
case CUBIC: { case CUBIC: {
Vector p0 = SS.GetEntity(point[0])->PointGetCoords(); if(order >= 0 && order != 1) break;
Vector p1 = SS.GetEntity(point[1])->PointGetCoords(); Vector p0 = SS.GetEntity(point[0])->PointGetNum();
Vector p2 = SS.GetEntity(point[2])->PointGetCoords(); Vector p1 = SS.GetEntity(point[1])->PointGetNum();
Vector p3 = SS.GetEntity(point[3])->PointGetCoords(); Vector p2 = SS.GetEntity(point[2])->PointGetNum();
Vector p3 = SS.GetEntity(point[3])->PointGetNum();
int i, n = 20; int i, n = 20;
Vector prev = p0; Vector prev = p0;
for(i = 1; i <= n; i++) { for(i = 1; i <= n; i++) {
@ -394,6 +479,27 @@ void Entity::DrawOrGetDistance(int order) {
break; 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: default:
oops(); oops();
} }

View File

@ -53,7 +53,51 @@ Expr *ExprVector::Magnitude(void) {
return r->Sqrt(); 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 *Expr::FromParam(hParam p) {
Expr *r = AllocExpr(); Expr *r = AllocExpr();

11
expr.h
View File

@ -131,4 +131,15 @@ public:
Expr *Magnitude(void); 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 #endif

View File

@ -75,8 +75,15 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].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[2].v", 'x', &(SS.sv.e.point[2].v) },
{ 'e', "Entity.point[3].v", 'x', &(SS.sv.e.point[3].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.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.h.v", 'x', &(SS.sv.c.h.v) },
{ 'c', "Constraint.type", 'd', &(SS.sv.c.type) }, { 'c', "Constraint.type", 'd', &(SS.sv.c.type) },

View File

@ -54,13 +54,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Draw Anywhere in 3d\tQ", MNU_FREE_IN_3D, 'Q', mReq }, { 1, "Draw Anywhere in 3d\tQ", MNU_FREE_IN_3D, 'Q', mReq },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq }, { 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq },
{ 1, "Datum A&xis\tX", 0, 'X', mReq }, { 1, "&Workplane (Coordinate S&ystem)\tY", 0, 'Y', mReq },
{ 1, "Datum Pla&ne\tN", 0, 'N', mReq },
{ 1, "2d Coordinate S&ystem\tY", 0, 'Y', mReq },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq }, { 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq },
{ 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', 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, "&Arc of a Circle\tA", 0, 'A', mReq },
{ 1, "&Cubic Segment\t3", MNU_CUBIC, '3', mReq }, { 1, "&Cubic Segment\t3", MNU_CUBIC, '3', mReq },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
@ -107,7 +105,7 @@ void GraphicsWindow::Init(void) {
activeWorkplane = r.entity(0); activeWorkplane = r.entity(0);
showWorkplanes = true; showWorkplanes = true;
showAxes = true; showNormals = true;
showPoints = true; showPoints = true;
showConstraints = true; showConstraints = true;
showSolids = true; showSolids = true;
@ -265,10 +263,7 @@ void GraphicsWindow::MenuEdit(int id) {
case MNU_UNSELECT_ALL: case MNU_UNSELECT_ALL:
HideGraphicsEditControl(); HideGraphicsEditControl();
SS.GW.ClearSelection(); SS.GW.ClearSelection();
SS.GW.pendingOperation = 0; SS.GW.ClearPending();
SS.GW.pendingDescription = NULL;
SS.GW.pendingPoint.v = 0;
SS.GW.pendingConstraint.v = 0;
SS.TW.ScreenNavigation('h', 0); SS.TW.ScreenNavigation('h', 0);
SS.TW.Show(); SS.TW.Show();
break; break;
@ -335,7 +330,7 @@ void GraphicsWindow::MenuRequest(int id) {
Vector pr, pu; Vector pr, pu;
e->WorkplaneGetBasisVectors(&pr, &pu); e->WorkplaneGetBasisVectors(&pr, &pu);
Quaternion quatf = Quaternion::MakeFrom(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.AnimateOnto(quatf, offsetf);
SS.GW.EnsureValidActives(); 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_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_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_CUBIC: s = "click first point of cubic segment"; goto c;
case MNU_CIRCLE: s = "click center of circle"; goto c;
c: c:
SS.GW.pendingOperation = id; SS.GW.pending.operation = id;
SS.GW.pendingDescription = s; SS.GW.pending.description = s;
SS.TW.Show(); SS.TW.Show();
break; 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); Entity *p = SS.GetEntity(hp);
Vector pos = p->PointGetCoords(); Vector pos = p->PointGetNum();
UpdateDraggedPoint(&pos, mx, my); UpdateDraggedNum(&pos, mx, my);
p->PointForceTo(pos); 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(projRight.ScaledBy((mx - orig.mouse.x)/scale));
*pos = pos->Plus(projUp.ScaledBy((my - orig.mouse.y)/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 }; 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) { if(middleDown) {
hover.Clear(); hover.Clear();
@ -405,7 +403,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
NormalizeProjectionVectors(); NormalizeProjectionVectors();
} else { } 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); projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy); projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);
@ -422,57 +420,122 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
return; return;
} }
// Enforce a bit of static friction before we start dragging. if(pending.operation == 0) {
double dm = orig.mouse.DistanceTo(mp); double dm = orig.mouse.DistanceTo(mp);
if(leftDown && dm > 3 && pendingOperation == 0) { // If we're currently not doing anything, then see if we should
if(hover.entity.v && // start dragging something.
SS.GetEntity(hover.entity)->IsPoint() && if(leftDown && dm > 3) {
!SS.GetEntity(hover.entity)->PointIsFromReferences()) if(hover.entity.v) {
{ Entity *e = SS.GetEntity(hover.entity);
// Start dragging this point. if(e->IsPoint()) {
ClearSelection(); // Start dragging this point.
pendingPoint = hover.entity; ClearSelection();
pendingOperation = DRAGGING_POINT; pending.point = hover.entity;
} else if(hover.constraint.v && pending.operation = DRAGGING_POINT;
SS.GetConstraint(hover.constraint)->HasLabel()) } else if(e->type == Entity::CIRCLE) {
{ // Drag the radius.
ClearSelection(); ClearSelection();
pendingConstraint = hover.constraint; pending.circle = hover.entity;
pendingOperation = DRAGGING_CONSTRAINT; pending.operation = DRAGGING_RADIUS;
} } else if(e->IsNormal()) {
} else if(leftDown && pendingOperation == DRAGGING_CONSTRAINT) { ClearSelection();
Constraint *c = SS.constraint.FindById(pendingConstraint); pending.normal = hover.entity;
UpdateDraggedPoint(&(c->disp.offset), x, y); pending.operation = DRAGGING_NORMAL;
} else if(leftDown && pendingOperation == DRAGGING_POINT) { }
if(havePainted) { } else if(hover.constraint.v &&
UpdateDraggedEntity(pendingPoint, x, y); SS.GetConstraint(hover.constraint)->HasLabel())
SS.GenerateAll(solving == SOLVE_ALWAYS); {
havePainted = false; ClearSelection();
pending.constraint = hover.constraint;
pending.operation = DRAGGING_CONSTRAINT;
}
} else {
// Otherwise, just hit test and give up
HitTestMakeSelection(mp);
} }
return;
} }
// No buttons pressed. // If the user has started an operation from the menu, but not
if(pendingOperation == DRAGGING_NEW_POINT || // completed it, then just do the selection.
pendingOperation == DRAGGING_NEW_LINE_POINT) if(pending.operation < FIRST_PENDING) {
{
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.
HitTestMakeSelection(mp); 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) { bool GraphicsWindow::Selection::Equals(Selection *b) {
@ -493,6 +556,10 @@ void GraphicsWindow::Selection::Draw(void) {
if(constraint.v) SS.GetConstraint(constraint)->Draw(); if(constraint.v) SS.GetConstraint(constraint)->Draw();
} }
void GraphicsWindow::ClearPending(void) {
memset(&pending, 0, sizeof(pending));
}
void GraphicsWindow::HitTestMakeSelection(Point2d mp) { void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
int i; int i;
double d, dmin = 1e12; double d, dmin = 1e12;
@ -503,7 +570,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
for(i = 0; i < SS.entity.n; i++) { for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]); Entity *e = &(SS.entity.elem[i]);
// Don't hover whatever's being dragged. // 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); d = e->GetDistance(mp);
if(d < 10 && d < dmin) { if(d < 10 && d < dmin) {
@ -599,14 +666,14 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
Constraint::ConstrainCoincident(hover.entity, (p)); \ Constraint::ConstrainCoincident(hover.entity, (p)); \
} }
hRequest hr; hRequest hr;
switch(pendingOperation) { switch(pending.operation) {
case MNU_DATUM_POINT: case MNU_DATUM_POINT:
hr = AddRequest(Request::DATUM_POINT); hr = AddRequest(Request::DATUM_POINT);
SS.GetEntity(hr.entity(0))->PointForceTo(v); SS.GetEntity(hr.entity(0))->PointForceTo(v);
ClearSelection(); hover.Clear(); ClearSelection(); hover.Clear();
pendingOperation = 0; pending.operation = 0;
break; break;
case MNU_LINE_SEGMENT: case MNU_LINE_SEGMENT:
@ -616,10 +683,26 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
ClearSelection(); hover.Clear(); ClearSelection(); hover.Clear();
pendingOperation = DRAGGING_NEW_LINE_POINT; pending.operation = DRAGGING_NEW_LINE_POINT;
pendingPoint = hr.entity(2); pending.point = hr.entity(2);
pendingDescription = "click to place next point of line"; pending.description = "click to place next point of line";
SS.GetEntity(pendingPoint)->PointForceTo(v); 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; break;
case MNU_CUBIC: case MNU_CUBIC:
@ -632,30 +715,28 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
ClearSelection(); hover.Clear(); ClearSelection(); hover.Clear();
pendingOperation = DRAGGING_NEW_CUBIC_POINT; pending.operation = DRAGGING_NEW_CUBIC_POINT;
pendingPoint = hr.entity(4); pending.point = hr.entity(4);
pendingDescription = "click to place next point of cubic"; pending.description = "click to place next point of cubic";
break; break;
case DRAGGING_RADIUS:
case DRAGGING_NEW_POINT: case DRAGGING_NEW_POINT:
// The MouseMoved event has already dragged it under the cursor. // The MouseMoved event has already dragged it as desired.
pendingOperation = 0; ClearPending();
pendingPoint.v = 0;
break; break;
case DRAGGING_NEW_CUBIC_POINT: case DRAGGING_NEW_CUBIC_POINT:
if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) {
Constraint::ConstrainCoincident(pendingPoint, hover.entity); Constraint::ConstrainCoincident(pending.point, hover.entity);
} }
pendingOperation = 0; ClearPending();
pendingPoint.v = 0;
break; break;
case DRAGGING_NEW_LINE_POINT: { case DRAGGING_NEW_LINE_POINT: {
if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) {
Constraint::ConstrainCoincident(pendingPoint, hover.entity); Constraint::ConstrainCoincident(pending.point, hover.entity);
pendingOperation = 0; ClearPending();
pendingPoint.v = 0;
break; break;
} }
// Create a new line segment, so that we continue drawing. // 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); SS.GetEntity(hr.entity(1))->PointForceTo(v);
// Constrain the line segments to share an endpoint // 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 // And drag an endpoint of the new line segment
pendingOperation = DRAGGING_NEW_LINE_POINT; pending.operation = DRAGGING_NEW_LINE_POINT;
pendingPoint = hr.entity(2); pending.point = hr.entity(2);
pendingDescription = "click to place next point of next line"; pending.description = "click to place next point of next line";
SS.GetEntity(pendingPoint)->PointForceTo(v); SS.GetEntity(pending.point)->PointForceTo(v);
break; break;
} }
case 0: case 0:
default: { default: {
pendingOperation = 0; ClearPending();
pendingPoint.v = 0;
pendingConstraint.v = 0;
pendingDescription = NULL;
if(hover.IsEmpty()) break; if(hover.IsEmpty()) break;
@ -710,12 +788,12 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
} }
void GraphicsWindow::MouseLeftUp(double mx, double my) { void GraphicsWindow::MouseLeftUp(double mx, double my) {
switch(pendingOperation) { switch(pending.operation) {
case DRAGGING_POINT: case DRAGGING_POINT:
case DRAGGING_CONSTRAINT: case DRAGGING_CONSTRAINT:
pendingOperation = 0; case DRAGGING_NORMAL:
pendingPoint.v = 0; case DRAGGING_RADIUS:
pendingConstraint.v = 0; ClearPending();
break; break;
default: default:
@ -781,9 +859,9 @@ void GraphicsWindow::ToggleBool(int link, DWORD v) {
} }
void GraphicsWindow::ToggleAnyDatumShown(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.showWorkplanes = t;
SS.GW.showAxes = t; SS.GW.showNormals = t;
SS.GW.showPoints = t; SS.GW.showPoints = t;
SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS); SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS);
@ -809,7 +887,7 @@ void GraphicsWindow::Paint(int w, int h) {
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); 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 tx = projRight.Dot(offset);
double ty = projUp.Dot(offset); double ty = projUp.Dot(offset);

View File

@ -68,7 +68,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
Entity *e = &(entity->elem[i]); Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue; 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; break;
@ -93,7 +93,9 @@ hEntity Group::Remap(hEntity in, int copyNumber) {
return h.entity(em.h.v); 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 *ep = SS.GetEntity(in);
Entity en; 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); en.point[3] = Remap(ep->point[3], a);
break; 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_3D:
case Entity::POINT_IN_2D: case Entity::POINT_IN_2D:
en.type = Entity::POINT_XFRMD; en.type = Entity::POINT_XFRMD;
en.point[0] = ep->h;
en.param[0] = dx; en.param[0] = dx;
en.param[1] = dy; en.param[1] = dy;
en.param[2] = dz; 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; break;
default: default:
@ -242,67 +267,104 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
int points = 0; int points = 0;
int params = 0; int params = 0;
int et = 0; int et = 0;
bool hasNormal = false;
int i; int i;
Group *g = SS.group.FindById(group);
Entity e; Entity e;
memset(&e, 0, sizeof(e)); memset(&e, 0, sizeof(e));
switch(type) { switch(type) {
case Request::WORKPLANE: case Request::WORKPLANE:
et = Entity::WORKPLANE; points = 1; params = 4; goto c; et = Entity::WORKPLANE;
points = 1;
hasNormal = true;
break;
case Request::DATUM_POINT: case Request::DATUM_POINT:
et = 0; points = 1; params = 0; goto c; et = 0;
points = 1;
break;
case Request::LINE_SEGMENT: 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: case Request::CUBIC:
et = Entity::CUBIC; points = 4; params = 0; goto c; et = Entity::CUBIC;
c: { points = 4;
// 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);
break; break;
}
default: default: oops();
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) { char *Request::DescriptionString(void) {

View File

@ -103,7 +103,8 @@ public:
// mapping list. // mapping list.
IdList<EntityMap,EntityId> remap; IdList<EntityMap,EntityId> remap;
hEntity Remap(hEntity in, int copyNumber); 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 MakePolygons(void);
void Draw(void); void Draw(void);
@ -130,6 +131,7 @@ public:
static const int DATUM_POINT = 101; static const int DATUM_POINT = 101;
static const int LINE_SEGMENT = 200; static const int LINE_SEGMENT = 200;
static const int CUBIC = 300; static const int CUBIC = 300;
static const int CIRCLE = 400;
int type; int type;
@ -152,22 +154,30 @@ public:
static const hEntity FREE_IN_3D; static const hEntity FREE_IN_3D;
static const int WORKPLANE = 1000;
static const int POINT_IN_3D = 2000; static const int POINT_IN_3D = 2000;
static const int POINT_IN_2D = 2001; static const int POINT_IN_2D = 2001;
static const int POINT_XFRMD = 2010; 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 NORMAL_IN_3D = 3000;
static const int FACE_LIST = 91000; 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; int type;
// When it comes time to draw an entity, we look here to get the // When it comes time to draw an entity, we look here to get the
// defining variables. // defining variables.
hParam param[4]; hParam param[4];
hEntity point[4]; hEntity point[4];
hEntity direction; hEntity normal;
// Derived points are a symbolic offset from a constant base.
Vector numPoint;
Quaternion numNormal;
hGroup group; hGroup group;
hEntity workplane; // or Entity::FREE_IN_3D hEntity workplane; // or Entity::FREE_IN_3D
@ -184,12 +194,18 @@ public:
bool IsPoint(void); bool IsPoint(void);
// Applies for any of the point types // Applies for any of the point types
Vector PointGetCoords(void); Vector PointGetNum(void);
ExprVector PointGetExprs(void); ExprVector PointGetExprs(void);
void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v); void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v);
void PointForceTo(Vector v); void PointForceTo(Vector v);
bool PointIsFromReferences(void); 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 // Applies for anything that comes with a plane
bool HasPlane(void); bool HasPlane(void);
// The plane is points P such that P dot (xn, yn, zn) - d = 0 // The plane is points P such that P dot (xn, yn, zn) - d = 0

View File

@ -72,28 +72,29 @@ void SolveSpace::ForceReferences(void) {
// Force the values of the paramters that define the three reference // Force the values of the paramters that define the three reference
// coordinate systems. // coordinate systems.
static const struct { static const struct {
hRequest hr; hRequest hr;
double a, b, c, d; Quaternion q;
} Quat[] = { } Quat[] = {
{ Request::HREQUEST_REFERENCE_XY, 1, 0, 0, 0, }, { Request::HREQUEST_REFERENCE_XY, { 1, 0, 0, 0, } },
{ Request::HREQUEST_REFERENCE_YZ, 0.5, 0.5, 0.5, 0.5, }, { 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_ZX, { 0.5, -0.5, -0.5, -0.5, } },
}; };
for(int i = 0; i < 3; i++) { for(int i = 0; i < 3; i++) {
hRequest hr = Quat[i].hr; hRequest hr = Quat[i].hr;
Entity *wrkpl = GetEntity(hr.entity(0));
// The origin for our coordinate system, always zero // The origin for our coordinate system, always zero
Vector v = Vector::MakeFrom(0, 0, 0); Entity *origin = GetEntity(wrkpl->point[0]);
Entity *origin = GetEntity(hr.entity(1)); origin->PointForceTo(Vector::MakeFrom(0, 0, 0));
origin->PointForceTo(v);
GetParam(origin->param[0])->known = true; GetParam(origin->param[0])->known = true;
GetParam(origin->param[1])->known = true; GetParam(origin->param[1])->known = true;
GetParam(origin->param[2])->known = true; GetParam(origin->param[2])->known = true;
// The quaternion that defines the rotation, from the table. // The quaternion that defines the rotation, from the table.
Param *p; Entity *normal = GetEntity(wrkpl->normal);
p = GetParam(hr.param(0)); p->val = Quat[i].a; p->known = true; normal->NormalForceTo(Quat[i].q);
p = GetParam(hr.param(1)); p->val = Quat[i].b; p->known = true; GetParam(normal->param[0])->known = true;
p = GetParam(hr.param(2)); p->val = Quat[i].c; p->known = true; GetParam(normal->param[1])->known = true;
p = GetParam(hr.param(3)); p->val = Quat[i].d; p->known = true; GetParam(normal->param[2])->known = true;
GetParam(normal->param[3])->known = true;
} }
} }

View File

@ -27,6 +27,7 @@ typedef signed long SDWORD;
class Expr; class Expr;
class ExprVector; class ExprVector;
class ExprQuaternion;
// From the platform-specific code. // From the platform-specific code.
int SaveFileYesNoCancel(void); int SaveFileYesNoCancel(void);

View File

@ -86,8 +86,8 @@ void System::SortBySensitivity(void) {
mat.dragged[j] = false; mat.dragged[j] = false;
mat.permutation[j] = j; mat.permutation[j] = j;
} }
if(SS.GW.pendingPoint.v) { if(SS.GW.pending.point.v) {
Entity *p = SS.entity.FindByIdNoOops(SS.GW.pendingPoint); Entity *p = SS.entity.FindByIdNoOops(SS.GW.pending.point);
// If we're solving an earlier group, then the pending point might // If we're solving an earlier group, then the pending point might
// not exist in the entity tables yet. // not exist in the entity tables yet.
if(p) { if(p) {

View File

@ -158,15 +158,15 @@ done:
} }
void TextWindow::Show(void) { void TextWindow::Show(void) {
if(!(SS.GW.pendingOperation)) SS.GW.pendingDescription = NULL; if(!(SS.GW.pending.operation)) SS.GW.ClearPending();
ShowHeader(); ShowHeader();
if(SS.GW.pendingDescription) { if(SS.GW.pending.description) {
// A pending operation (that must be completed with the mouse in // A pending operation (that must be completed with the mouse in
// the graphics window) will preempt our usual display. // the graphics window) will preempt our usual display.
Printf(false, ""); Printf(false, "");
Printf(false, "%s", SS.GW.pendingDescription); Printf(false, "%s", SS.GW.pending.description);
} else { } else {
switch(shown->screen) { switch(shown->screen) {
default: default:
@ -222,7 +222,7 @@ void TextWindow::ShowHeader(void) {
SS.GetEntity(SS.GW.activeWorkplane)->DescriptionString(); SS.GetEntity(SS.GW.activeWorkplane)->DescriptionString();
// Navigation buttons // Navigation buttons
if(SS.GW.pendingDescription) { if(SS.GW.pending.description) {
Printf(false, " %Bt%Ft workplane:%Fd %s", cd); Printf(false, " %Bt%Ft workplane:%Fd %s", cd);
} else { } else {
Printf(false, " %Lb%f<<%E %Lh%fhome%E %Bt%Ft workplane:%Fd %s", Printf(false, " %Lb%f<<%E %Lh%fhome%E %Bt%Ft workplane:%Fd %s",
@ -232,9 +232,9 @@ void TextWindow::ShowHeader(void) {
} }
int datumColor; 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 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 datumColor = 'h'; // hidden
} else { } else {
datumColor = 'm'; // mixed datumColor = 'm'; // mixed
@ -243,11 +243,11 @@ void TextWindow::ShowHeader(void) {
#define hs(b) ((b) ? 's' : 'h') #define hs(b) ((b) ? 's' : 'h')
Printf(false, "%Bt%Ftshow: " Printf(false, "%Bt%Ftshow: "
"%Fp%Ll%D%fworkplanes%E " "%Fp%Ll%D%fworkplanes%E "
"%Fp%Ll%D%fvectors%E " "%Fp%Ll%D%fnormals%E "
"%Fp%Ll%D%fpoints%E " "%Fp%Ll%D%fpoints%E "
"%Fp%Ll%fany-datum%E", "%Fp%Ll%fany-datum%E",
hs(SS.GW.showWorkplanes), (DWORD)&(SS.GW.showWorkplanes), &(SS.GW.ToggleBool), 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), hs(SS.GW.showPoints), (DWORD)&(SS.GW.showPoints), &(SS.GW.ToggleBool),
datumColor, &(SS.GW.ToggleAnyDatumShown) datumColor, &(SS.GW.ToggleAnyDatumShown)
); );

30
ui.h
View File

@ -103,6 +103,7 @@ public:
MNU_FREE_IN_3D, MNU_FREE_IN_3D,
MNU_DATUM_POINT, MNU_DATUM_POINT,
MNU_LINE_SEGMENT, MNU_LINE_SEGMENT,
MNU_CIRCLE,
MNU_RECTANGLE, MNU_RECTANGLE,
MNU_CUBIC, MNU_CUBIC,
// Group // Group
@ -169,21 +170,32 @@ public:
void EnsureValidActives(); void EnsureValidActives();
// Operations that must be completed by doing something with the mouse // 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_POINT = 0x0f000000;
static const int DRAGGING_NEW_POINT = 0x0f000001; static const int DRAGGING_NEW_POINT = 0x0f000001;
static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; static const int DRAGGING_NEW_LINE_POINT = 0x0f000002;
static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003; static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003;
static const int DRAGGING_CONSTRAINT = 0x0f000004; static const int DRAGGING_CONSTRAINT = 0x0f000004;
hEntity pendingPoint; static const int DRAGGING_RADIUS = 0x0f000005;
hConstraint pendingConstraint; static const int DRAGGING_NORMAL = 0x0f000006;
int pendingOperation; static const int DRAGGING_NEW_RADIUS = 0x0f000007;
char *pendingDescription; struct {
hRequest AddRequest(int type); 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. // The constraint that is being edited with the on-screen textbox.
hConstraint constraintBeingEdited; hConstraint constraintBeingEdited;
hRequest AddRequest(int type);
// The current selection. // The current selection.
class Selection { class Selection {
public: public:
@ -215,7 +227,7 @@ public:
// This sets what gets displayed. // This sets what gets displayed.
bool showWorkplanes; bool showWorkplanes;
bool showAxes; bool showNormals;
bool showPoints; bool showPoints;
bool showConstraints; bool showConstraints;
bool showTextWindow; bool showTextWindow;
@ -228,8 +240,8 @@ public:
static const int SOLVE_ALWAYS = 1; static const int SOLVE_ALWAYS = 1;
int solving; int solving;
void UpdateDraggedPoint(Vector *pos, double mx, double my); void UpdateDraggedNum(Vector *pos, double mx, double my);
void UpdateDraggedEntity(hEntity hp, double mx, double my); void UpdateDraggedPoint(hEntity hp, double mx, double my);
// These are called by the platform-specific code. // These are called by the platform-specific code.
void Paint(int w, int h); void Paint(int w, int h);

View File

@ -23,12 +23,12 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14,
mat[15] = a44; 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; Quaternion q;
q.a = a; q.w = w;
q.b = b; q.vx = vx;
q.c = c; q.vy = vy;
q.d = d; q.vz = vz;
return q; return q;
} }
@ -40,65 +40,65 @@ Quaternion Quaternion::MakeFrom(Vector u, Vector v)
double s, tr = 1 + u.x + v.y + n.z; double s, tr = 1 + u.x + v.y + n.z;
if(tr > 1e-4) { if(tr > 1e-4) {
s = 2*sqrt(tr); s = 2*sqrt(tr);
q.a = s/4; q.w = s/4;
q.b = (v.z - n.y)/s; q.vx = (v.z - n.y)/s;
q.c = (n.x - u.z)/s; q.vy = (n.x - u.z)/s;
q.d = (u.y - v.x)/s; q.vz = (u.y - v.x)/s;
} else { } else {
double m = max(u.x, max(v.y, n.z)); double m = max(u.x, max(v.y, n.z));
if(m == u.x) { if(m == u.x) {
s = 2*sqrt(1 + u.x - v.y - n.z); s = 2*sqrt(1 + u.x - v.y - n.z);
q.a = (v.z - n.y)/s; q.w = (v.z - n.y)/s;
q.b = s/4; q.vx = s/4;
q.c = (u.y + v.x)/s; q.vy = (u.y + v.x)/s;
q.d = (n.x + u.z)/s; q.vz = (n.x + u.z)/s;
} else if(m == v.y) { } else if(m == v.y) {
s = 2*sqrt(1 - u.x + v.y - n.z); s = 2*sqrt(1 - u.x + v.y - n.z);
q.a = (n.x - u.z)/s; q.w = (n.x - u.z)/s;
q.b = (u.y + v.x)/s; q.vx = (u.y + v.x)/s;
q.c = s/4; q.vy = s/4;
q.d = (v.z + n.y)/s; q.vz = (v.z + n.y)/s;
} else if(m == n.z) { } else if(m == n.z) {
s = 2*sqrt(1 - u.x - v.y + n.z); s = 2*sqrt(1 - u.x - v.y + n.z);
q.a = (u.y - v.x)/s; q.w = (u.y - v.x)/s;
q.b = (n.x + u.z)/s; q.vx = (n.x + u.z)/s;
q.c = (v.z + n.y)/s; q.vy = (v.z + n.y)/s;
q.d = s/4; q.vz = s/4;
} else oops(); } else oops();
} }
return q.WithMagnitude(1); return q.WithMagnitude(1);
} }
Quaternion Quaternion::Plus(Quaternion y) { Quaternion Quaternion::Plus(Quaternion b) {
Quaternion q; Quaternion q;
q.a = a + y.a; q.w = w + b.w;
q.b = b + y.b; q.vx = vx + b.vx;
q.c = c + y.c; q.vy = vy + b.vy;
q.d = d + y.d; q.vz = vz + b.vz;
return q; return q;
} }
Quaternion Quaternion::Minus(Quaternion y) { Quaternion Quaternion::Minus(Quaternion b) {
Quaternion q; Quaternion q;
q.a = a - y.a; q.w = w - b.w;
q.b = b - y.b; q.vx = vx - b.vx;
q.c = c - y.c; q.vy = vy - b.vy;
q.d = d - y.d; q.vz = vz - b.vz;
return q; return q;
} }
Quaternion Quaternion::ScaledBy(double s) { Quaternion Quaternion::ScaledBy(double s) {
Quaternion q; Quaternion q;
q.a = a*s; q.w = w*s;
q.b = b*s; q.vx = vx*s;
q.c = c*s; q.vy = vy*s;
q.d = d*s; q.vz = vz*s;
return q; return q;
} }
double Quaternion::Magnitude(void) { 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) { Quaternion Quaternion::WithMagnitude(double s) {
@ -107,20 +107,24 @@ Quaternion Quaternion::WithMagnitude(double s) {
Vector Quaternion::RotationU(void) { Vector Quaternion::RotationU(void) {
Vector v; Vector v;
v.x = a*a + b*b - c*c - d*d; v.x = w*w + vx*vx - vy*vy - vz*vz;
v.y = 2*a*d + 2*b*c; v.y = 2*w *vz + 2*vx*vy;
v.z = 2*b*d - 2*a*c; v.z = 2*vx*vz - 2*w *vy;
return v; return v;
} }
Vector Quaternion::RotationV(void) { Vector Quaternion::RotationV(void) {
Vector v; Vector v;
v.x = 2*b*c - 2*a*d; v.x = 2*vx*vy - 2*w*vz;
v.y = a*a - b*b + c*c - d*d; v.y = w*w - vx*vx + vy*vy - vz*vz;
v.z = 2*a*b + 2*c*d; v.z = 2*w*vx + 2*vy*vz;
return v; return v;
} }
Vector Quaternion::RotationN(void) {
return RotationU().Cross(RotationV());
}
Vector Vector::MakeFrom(double x, double y, double z) { Vector Vector::MakeFrom(double x, double y, double z) {
Vector v; Vector v;
@ -199,17 +203,13 @@ Vector Vector::Normal(int which) {
n.z = 0; n.z = 0;
n.x = y; n.x = y;
n.y = -x; n.y = -x;
} else { } else oops();
oops();
}
if(which == 0) { if(which == 0) {
// That's the vector we return. // That's the vector we return.
} else if(which == 1) { } else if(which == 1) {
n = this->Cross(n); n = this->Cross(n);
} else { } else oops();
oops();
}
n = n.WithMagnitude(1); n = n.WithMagnitude(1);