solvespace/drawconstraint.cpp
Jonathan Westhues 853c6cb59c 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]
2008-05-04 22:18:01 -08:00

257 lines
9.4 KiB
C++

#include "solvespace.h"
bool Constraint::HasLabel(void) {
switch(type) {
case PT_PT_DISTANCE:
return true;
default:
return false;
}
}
void Constraint::LineDrawOrGetDistance(Vector a, Vector b) {
if(dogd.drawing) {
glBegin(GL_LINE_STRIP);
glxVertex3v(a);
glxVertex3v(b);
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 Constraint::DrawOrGetDistance(Vector *labelPos) {
if(!SS.GW.showConstraints) return;
Group *g = SS.GetGroup(group);
// If the group is hidden, then the constraints are hidden and not
// able to be selected.
if(!(g->visible)) return;
// Unit vectors that describe our current view of the scene. One pixel
// long, not one actual unit.
Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale);
Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale);
Vector gn = (gr.Cross(gu)).WithMagnitude(1/SS.GW.scale);
glxColor3d(1, 0.2, 1);
switch(type) {
case PT_PT_DISTANCE: {
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;
Vector ab = ap.Minus(bp);
Vector ar = ap.Minus(ref);
// Normal to a plan containing the line and the label origin.
Vector n = ab.Cross(ar);
Vector out = ab.Cross(n).WithMagnitude(1);
out = out.ScaledBy(-out.Dot(ar));
LineDrawOrGetDistance(ap, ap.Plus(out));
LineDrawOrGetDistance(bp, bp.Plus(out));
if(dogd.drawing) {
glPushMatrix();
glxTranslatev(ref);
glxOntoWorkplane(gr, gu);
glxWriteText(exprA->Print());
glPopMatrix();
} else {
Point2d o = SS.GW.ProjectPoint(ref);
dogd.dmin = min(dogd.dmin, o.DistanceTo(dogd.mp) - 10);
}
break;
}
case POINTS_COINCIDENT: {
if(!dogd.drawing) {
for(int i = 0; i < 2; i++) {
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
// constraint cannot be selected. But that's okay.
dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3);
}
break;
}
for(int a = 0; a < 2; a++) {
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)-> PointGetNum();
glxColor3d(0.4, 0, 0.4);
glBegin(GL_QUADS);
glxVertex3v(p.Plus (r).Plus (d));
glxVertex3v(p.Plus (r).Minus(d));
glxVertex3v(p.Minus(r).Minus(d));
glxVertex3v(p.Minus(r).Plus (d));
glEnd();
}
}
break;
}
case PT_ON_LINE:
case PT_IN_PLANE: {
double s = 7;
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));
LineDrawOrGetDistance(p.Plus (r).Minus(d), p.Minus(r).Minus(d));
LineDrawOrGetDistance(p.Minus(r).Minus(d), p.Minus(r).Plus (d));
LineDrawOrGetDistance(p.Minus(r).Plus (d), p.Plus (r).Plus (d));
break;
}
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])->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);
LineDrawOrGetDistance(m.Minus(n), m.Plus(n));
}
break;
}
case SYMMETRIC: {
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;
Vector d = (i == 0) ? b : a;
d = d.Minus(tail);
// Project the direction in which the arrow is drawn normal
// to the symmetry plane; for projected symmetry constraints,
// they might not be in the same direction, even when the
// constraint is fully solved.
d = n.ScaledBy(d.Dot(n));
d = d.WithMagnitude(20/SS.GW.scale);
Vector tip = tail.Plus(d);
LineDrawOrGetDistance(tail, tip);
d = d.WithMagnitude(9/SS.GW.scale);
LineDrawOrGetDistance(tip, tip.Minus(d.RotatedAbout(gn, 0.6)));
LineDrawOrGetDistance(tip, tip.Minus(d.RotatedAbout(gn, -0.6)));
}
break;
}
case AT_MIDPOINT:
case HORIZONTAL:
case VERTICAL:
if(entityA.v) {
Vector r, u, n;
if(workplane.v == Entity::FREE_IN_3D.v) {
r = gr; u = gu; n = gn;
} else {
SS.GetEntity(workplane)->WorkplaneGetBasisVectors(&r, &u);
n = r.Cross(u);
}
// For "at midpoint", this branch is always taken.
Entity *e = SS.GetEntity(entityA);
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);
// Draw midpoint constraint on other side of line, so that
// a line can be midpoint and horizontal at same time.
if(type == AT_MIDPOINT) offset = offset.ScaledBy(-1);
if(dogd.drawing) {
glPushMatrix();
glxTranslatev(m.Plus(offset));
glxOntoWorkplane(r, u);
glxWriteTextRefCenter(
(type == HORIZONTAL) ? "H" : (
(type == VERTICAL) ? "V" : (
(type == AT_MIDPOINT) ? "M" : NULL)));
glPopMatrix();
} else {
Point2d ref = SS.GW.ProjectPoint(m.Plus(offset));
dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
}
} else {
Vector a = SS.GetEntity(ptA)->PointGetNum();
Vector b = SS.GetEntity(ptB)->PointGetNum();
Entity *w = SS.GetEntity(SS.GetEntity(ptA)->workplane);
Vector cu, cv, cn;
w->WorkplaneGetBasisVectors(&cu, &cv);
cn = w->WorkplaneGetNormalVector();
int i;
for(i = 0; i < 2; i++) {
Vector o = (i == 0) ? a : b;
Vector oo = (i == 0) ? a.Minus(b) : b.Minus(a);
Vector d = (type == HORIZONTAL) ? cu : cv;
if(oo.Dot(d) < 0) d = d.ScaledBy(-1);
Vector dp = cn.Cross(d);
d = d.WithMagnitude(14/SS.GW.scale);
Vector c = o.Minus(d);
LineDrawOrGetDistance(o, c);
d = d.WithMagnitude(3/SS.GW.scale);
dp = dp.WithMagnitude(2/SS.GW.scale);
if(dogd.drawing) {
glBegin(GL_QUADS);
glxVertex3v((c.Plus(d)).Plus(dp));
glxVertex3v((c.Minus(d)).Plus(dp));
glxVertex3v((c.Minus(d)).Minus(dp));
glxVertex3v((c.Plus(d)).Minus(dp));
glEnd();
} else {
Point2d ref = SS.GW.ProjectPoint(c);
dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-6);
}
}
}
break;
default: oops();
}
}
void Constraint::Draw(void) {
dogd.drawing = true;
DrawOrGetDistance(NULL);
}
double Constraint::GetDistance(Point2d mp) {
dogd.drawing = false;
dogd.mp = mp;
dogd.dmin = 1e12;
DrawOrGetDistance(NULL);
return dogd.dmin;
}
Vector Constraint::GetLabelPos(void) {
dogd.drawing = false;
dogd.mp.x = 0; dogd.mp.y = 0;
dogd.dmin = 1e12;
Vector p;
DrawOrGetDistance(&p);
return p;
}