#include "solvespace.h" char *Entity::DescriptionString(void) { Request *r = SS.GetRequest(h.request()); return r->DescriptionString(); } 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]); *u = quat.RotationU(); *v = quat.RotationV(); } Vector Entity::WorkplaneGetNormalVector(void) { Vector u, v; WorkplaneGetBasisVectors(&u, &v); return u.Cross(v); } 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]); 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))); } ExprVector Entity::WorkplaneGetOffsetExprs(void) { return SS.GetEntity(point[0])->PointGetExprs(); } bool Entity::HasPlane(void) { switch(type) { case WORKPLANE: return true; default: return false; } } void Entity::PlaneGetExprs(ExprVector *n, Expr **dn) { if(type == WORKPLANE) { Expr *a = Expr::FromParam(param[0]); Expr *b = Expr::FromParam(param[1]); Expr *c = Expr::FromParam(param[2]); Expr *d = Expr::FromParam(param[3]); Expr *two = Expr::FromConstant(2); // Convert the quaternion to our plane's normal vector. n->x = two->Times(a->Times(c)); n->x = (n->x)->Plus (two->Times(b->Times(d))); n->y = two->Times(c->Times(d)); n->y = (n->y)->Minus(two->Times(a->Times(b))); n->z = a->Square(); n->z = (n->z)->Minus(b->Square()); n->z = (n->z)->Minus(c->Square()); n->z = (n->z)->Plus (d->Square()); ExprVector p0 = SS.GetEntity(point[0])->PointGetExprs(); // The plane is n dot (p - p0) = 0, or // n dot p - n dot p0 = 0 // so dn = n dot p0 *dn = p0.Dot(*n); } else { oops(); } } 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; default: return false; } } bool Entity::PointIsFromReferences(void) { return h.request().IsFromReferences(); } void Entity::PointForceTo(Vector p) { switch(type) { case POINT_IN_3D: SS.GetParam(param[0])->val = p.x; SS.GetParam(param[1])->val = p.y; SS.GetParam(param[2])->val = p.z; break; case POINT_IN_2D: { Entity *c = SS.GetEntity(workplane); Vector u, v; c->WorkplaneGetBasisVectors(&u, &v); SS.GetParam(param[0])->val = p.Dot(u); SS.GetParam(param[1])->val = p.Dot(v); break; } case POINT_XFRMD: { Vector orig = SS.GetEntity(point[0])->PointGetCoords(); Vector trans = p.Minus(orig); SS.GetParam(param[0])->val = trans.x; SS.GetParam(param[1])->val = trans.y; SS.GetParam(param[2])->val = trans.z; break; } default: oops(); } } Vector Entity::PointGetCoords(void) { Vector p; switch(type) { case POINT_IN_3D: p.x = SS.GetParam(param[0])->val; p.y = SS.GetParam(param[1])->val; p.z = SS.GetParam(param[2])->val; break; case POINT_IN_2D: { Entity *c = SS.GetEntity(workplane); Vector u, v; c->WorkplaneGetBasisVectors(&u, &v); p = u.ScaledBy(SS.GetParam(param[0])->val); p = p.Plus(v.ScaledBy(SS.GetParam(param[1])->val)); break; } case POINT_XFRMD: { p = SS.GetEntity(point[0])->PointGetCoords(); p.x += SS.GetParam(param[0])->val; p.y += SS.GetParam(param[1])->val; p.z += SS.GetParam(param[2])->val; break; } default: oops(); } return p; } ExprVector Entity::PointGetExprs(void) { ExprVector r; switch(type) { case POINT_IN_3D: r.x = Expr::FromParam(param[0]); r.y = Expr::FromParam(param[1]); r.z = Expr::FromParam(param[2]); break; case POINT_IN_2D: { Entity *c = SS.GetEntity(workplane); ExprVector u, v; c->WorkplaneGetBasisExprs(&u, &v); r = u.ScaledBy(Expr::FromParam(param[0])); r = r.Plus(v.ScaledBy(Expr::FromParam(param[1]))); break; } case POINT_XFRMD: { ExprVector orig = SS.GetEntity(point[0])->PointGetExprs(); ExprVector trans; trans.x = Expr::FromParam(param[0]); trans.y = Expr::FromParam(param[1]); trans.z = Expr::FromParam(param[2]); r = orig.Plus(trans); break; } default: oops(); } return r; } void Entity::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) { if(type == POINT_IN_2D && workplane.v == wrkpl.v) { // They want our coordinates in the form that we've written them, // very nice. *u = Expr::FromParam(param[0]); *v = Expr::FromParam(param[1]); } else { // Get the offset and basis vectors for this weird exotic csys. Entity *w = SS.GetEntity(wrkpl); ExprVector wp = w->WorkplaneGetOffsetExprs(); ExprVector wu, wv; w->WorkplaneGetBasisExprs(&wu, &wv); // Get our coordinates in three-space, and project them into that // coordinate system. ExprVector ev = PointGetExprs(); ev = ev.Minus(wp); *u = ev.Dot(wu); *v = ev.Dot(wv); } } void Entity::LineDrawOrGetDistance(Vector a, Vector b) { if(dogd.drawing) { // This fudge guarantees that the line will get drawn in front of // anything else at the "same" depth in the z-buffer, so that it // goes in front of the shaded stuff. Vector n = SS.GW.projRight.Cross(SS.GW.projUp); n = n.WithMagnitude(1.2/SS.GW.scale); glBegin(GL_LINE_STRIP); glxVertex3v(a.Plus(n)); glxVertex3v(b.Plus(n)); glEnd(); } else { Point2d ap = SS.GW.ProjectPoint(a); Point2d bp = SS.GW.ProjectPoint(b); double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); dogd.dmin = min(dogd.dmin, d); } } void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) { LineDrawOrGetDistance(a, b); if(dogd.edges) { SEdge edge; edge.a = a; edge.b = b; dogd.edges->l.Add(&edge); } } void Entity::Draw(int order) { dogd.drawing = true; dogd.edges = NULL; DrawOrGetDistance(order); } void Entity::GenerateEdges(SEdgeList *el) { dogd.drawing = false; dogd.edges = el; DrawOrGetDistance(-1); dogd.edges = NULL; } double Entity::GetDistance(Point2d mp) { dogd.drawing = false; dogd.edges = NULL; dogd.mp = mp; dogd.dmin = 1e12; DrawOrGetDistance(-1); return dogd.dmin; } void Entity::DrawOrGetDistance(int order) { Group *g = SS.GetGroup(group); // If an entity is invisible, then it doesn't get shown, and it doesn't // contribute a distance for the selection, but it still generates edges. if(!(g->visible) && !dogd.edges) return; glxColor3d(1, 1, 1); switch(type) { case POINT_XFRMD: case POINT_IN_3D: case POINT_IN_2D: { if(order >= 0 && order != 2) break; if(!SS.GW.showPoints) break; if(h.isFromRequest()) { Entity *isfor = SS.GetEntity(h.request().entity(0)); if(!SS.GW.showWorkplanes && isfor->type == Entity::WORKPLANE) { break; } } Vector v = PointGetCoords(); 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); glxColor3d(0, 0.8, 0); glBegin(GL_QUADS); glxVertex3v(v.Plus (r).Plus (d)); glxVertex3v(v.Plus (r).Minus(d)); glxVertex3v(v.Minus(r).Minus(d)); glxVertex3v(v.Minus(r).Plus (d)); glEnd(); } else { Point2d pp = SS.GW.ProjectPoint(v); // Make a free point slightly easier to select, so that with // coincident points, we select the free one. dogd.dmin = pp.DistanceTo(dogd.mp) - 4; } break; } case WORKPLANE: { if(order >= 0 && order != 0) break; if(!SS.GW.showWorkplanes) break; Vector p; p = SS.GetEntity(point[0])->PointGetCoords(); Vector u, v; WorkplaneGetBasisVectors(&u, &v); double s = (min(SS.GW.width, SS.GW.height))*0.4/SS.GW.scale; Vector us = u.ScaledBy(s); Vector vs = v.ScaledBy(s); Vector pp = p.Plus (us).Plus (vs); Vector pm = p.Plus (us).Minus(vs); Vector mm = p.Minus(us).Minus(vs); Vector mp = p.Minus(us).Plus (vs); glxColor3d(0, 0.4, 0.4); LineDrawOrGetDistance(pp, pm); LineDrawOrGetDistance(pm, mm); LineDrawOrGetDistance(mm, mp); LineDrawOrGetDistance(mp, pp); if(dogd.drawing) { glPushMatrix(); glxTranslatev(mm); glxOntoWorkplane(u, v); glxWriteText(DescriptionString()); glPopMatrix(); } break; } case LINE_SEGMENT: { if(order >= 0 && order != 1) break; Vector a = SS.GetEntity(point[0])->PointGetCoords(); Vector b = SS.GetEntity(point[1])->PointGetCoords(); 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(); int i, n = 20; Vector prev = p0; for(i = 1; i <= n; i++) { double t = ((double)i)/n; Vector p = (p0.ScaledBy((1 - t)*(1 - t)*(1 - t))).Plus( (p1.ScaledBy(3*t*(1 - t)*(1 - t))).Plus( (p2.ScaledBy(3*t*t*(1 - t))).Plus( (p3.ScaledBy(t*t*t))))); LineDrawOrGetDistanceOrEdge(prev, p); prev = p; } break; } default: oops(); } }