solvespace/drawconstraint.cpp
Jonathan Westhues 0a56a63417 Add a trim command. I can now do circles (or arcs) against lines,
or lines against lines. The constraints get rather screwed up
afterwards, of course.

So make arcs with the endpoints coincident into circles, instead
of nothings; since the first split of a circle produces that.

And don't warn after deleting just point-coincident or horiz/vert
constraints as a dependency; that's just a nuisance, because it
happens too often.

[git-p4: depot-paths = "//depot/solvespace/": change = 1884]
2009-01-03 04:27:33 -08:00

762 lines
27 KiB
C++

#include "solvespace.h"
bool Constraint::HasLabel(void) {
switch(type) {
case PT_LINE_DISTANCE:
case PT_PLANE_DISTANCE:
case PT_FACE_DISTANCE:
case PT_PT_DISTANCE:
case DIAMETER:
case LENGTH_RATIO:
case ANGLE:
case COMMENT:
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);
}
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
}
double Constraint::EllipticalInterpolation(double rx, double ry, double theta) {
double ex = rx*cos(theta);
double ey = ry*sin(theta);
double v = sqrt(ex*ex + ey*ey);
return v;
}
char *Constraint::Label(void) {
static char Ret[1024];
if(type == ANGLE) {
sprintf(Ret, "%.2f", valA);
} else if(type == LENGTH_RATIO) {
sprintf(Ret, "%.3f:1", valA);
} else if(type == COMMENT) {
strcpy(Ret, comment.str);
} else {
// valA has units of distance
strcpy(Ret, SS.MmToString(fabs(valA)));
}
if(reference) {
strcat(Ret, " REF");
}
return Ret;
}
void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) {
char *s = Label();
double swidth = glxStrWidth(s), sheight = glxStrHeight();
if(labelPos) {
// labelPos is from the top left corner (for the text box used to
// edit things), but ref is from the center.
*labelPos = ref.Minus(gr.WithMagnitude(swidth/2)).Minus(
gu.WithMagnitude(sheight/2));
}
if(dogd.drawing) {
glPushMatrix();
glxTranslatev(ref);
glxOntoWorkplane(gr, gu);
glxWriteTextRefCenter(s);
glPopMatrix();
} else {
double l = swidth/2 - sheight/2;
l = max(l, 5/SS.GW.scale);
Point2d a = SS.GW.ProjectPoint(ref.Minus(gr.WithMagnitude(l)));
Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l)));
double d = dogd.mp.DistanceToLine(a, b.Minus(a), true);
dogd.dmin = min(dogd.dmin, d - 3);
dogd.refp = ref;
}
}
void Constraint::DoProjectedPoint(Vector *r) {
Vector p = r->ProjectInto(workplane);
glLineStipple(4, 0x5555);
glEnable(GL_LINE_STIPPLE);
LineDrawOrGetDistance(p, *r);
glDisable(GL_LINE_STIPPLE);
*r = p;
}
void Constraint::DoEqualLenTicks(Vector a, Vector b, Vector gn) {
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));
}
void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db,
Vector offset, Vector *ref)
{
Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale);
Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale);
if(workplane.v != Entity::FREE_IN_3D.v) {
a0 = a0.ProjectInto(workplane);
b0 = b0.ProjectInto(workplane);
da = da.ProjectVectorInto(workplane);
db = db.ProjectVectorInto(workplane);
}
bool skew;
Vector pi = Vector::AtIntersectionOfLines(a0, a0.Plus(da),
b0, b0.Plus(db), &skew);
if(!skew) {
*ref = pi.Plus(offset);
// We draw in a coordinate system centered at the intersection point.
// One basis vector is da, and the other is normal to da and in
// the plane that contains our lines (so normal to its normal).
Vector dna = (da.Cross(db)).Cross(da);
da = da.WithMagnitude(1); dna = dna.WithMagnitude(1);
Vector rm = (*ref).Minus(pi);
double rda = rm.Dot(da), rdna = rm.Dot(dna);
double r = sqrt(rda*rda + rdna*rdna);
double c = (da.Dot(db))/(da.Magnitude()*db.Magnitude());
double thetaf = acos(c);
Vector m = da.ScaledBy(cos(thetaf/2)).Plus(
dna.ScaledBy(sin(thetaf/2)));
if(m.Dot(rm) < 0) {
da = da.ScaledBy(-1); dna = dna.ScaledBy(-1);
}
Vector prev = da.ScaledBy(r).Plus(pi);
int i, n = 30;
for(i = 0; i <= n; i++) {
double theta = (i*thetaf)/n;
Vector p = da. ScaledBy(r*cos(theta)).Plus(
dna.ScaledBy(r*sin(theta))).Plus(pi);
LineDrawOrGetDistance(prev, p);
prev = p;
}
double tl = atan2(rm.Dot(gu), rm.Dot(gr));
double adj = EllipticalInterpolation(
glxStrWidth(Label())/2, glxStrHeight()/2, tl);
*ref = (*ref).Plus(rm.WithMagnitude(adj + 3/SS.GW.scale));
} else {
// The lines are skew; no wonderful way to illustrate that.
*ref = a0.Plus(b0);
*ref = (*ref).ScaledBy(0.5).Plus(disp.offset);
glPushMatrix();
gu = gu.WithMagnitude(1);
glxTranslatev((*ref).Plus(gu.ScaledBy(-1.5*glxStrHeight())));
glxOntoWorkplane(gr, gu);
glxWriteTextRefCenter("angle between skew lines");
glPopMatrix();
}
}
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;
// And likewise if the group is not the active group.
if(g->h.v != SS.GW.activeGroup.v) 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.1, 1);
switch(type) {
case PT_PT_DISTANCE: {
Vector ap = SS.GetEntity(ptA)->PointGetNum();
Vector bp = SS.GetEntity(ptB)->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
DoProjectedPoint(&ap);
DoProjectedPoint(&bp);
}
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
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));
DoLabel(ref, labelPos, gr, gu);
break;
}
case PT_FACE_DISTANCE:
case PT_PLANE_DISTANCE: {
Vector pt = SS.GetEntity(ptA)->PointGetNum();
Entity *enta = SS.GetEntity(entityA);
Vector n, p;
if(type == PT_PLANE_DISTANCE) {
n = enta->Normal()->NormalN();
p = enta->WorkplaneGetOffset();
} else {
n = enta->FaceGetNormalNum();
p = enta->FaceGetPointNum();
}
double d = (p.Minus(pt)).Dot(n);
Vector closest = pt.Plus(n.WithMagnitude(d));
LineDrawOrGetDistance(pt, closest);
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset);
DoLabel(ref, labelPos, gr, gu);
break;
}
case PT_LINE_DISTANCE: {
Vector pt = SS.GetEntity(ptA)->PointGetNum();
Entity *line = SS.GetEntity(entityA);
Vector lA = SS.GetEntity(line->point[0])->PointGetNum();
Vector lB = SS.GetEntity(line->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
lA = lA.ProjectInto(workplane);
lB = lB.ProjectInto(workplane);
DoProjectedPoint(&pt);
}
// Find the closest point on the line
Vector closest = pt.ClosestPointOnLine(lA, (lA.Minus(lB)));
LineDrawOrGetDistance(pt, closest);
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset);
DoLabel(ref, labelPos, gr, gu);
if(workplane.v != Entity::FREE_IN_3D.v) {
// Draw the projection marker from the closest point on the
// projected line to the projected point on the real line.
Vector lAB = (lA.Minus(lB));
double t = (lA.Minus(closest)).DivPivoting(lAB);
Vector lA = SS.GetEntity(line->point[0])->PointGetNum();
Vector lB = SS.GetEntity(line->point[1])->PointGetNum();
Vector c2 = (lA.ScaledBy(1-t)).Plus(lB.ScaledBy(t));
DoProjectedPoint(&c2);
}
break;
}
case DIAMETER: {
Entity *circle = SS.GetEntity(entityA);
Vector center = SS.GetEntity(circle->point[0])->PointGetNum();
double r = circle->CircleGetRadiusNum();
Vector ref = center.Plus(disp.offset);
double theta = atan2(disp.offset.Dot(gu), disp.offset.Dot(gr));
double adj = EllipticalInterpolation(
glxStrWidth(Label())/2, glxStrHeight()/2, theta);
Vector mark = ref.Minus(center);
mark = mark.WithMagnitude(mark.Magnitude()-r);
LineDrawOrGetDistance(ref.Minus(mark.WithMagnitude(adj)),
ref.Minus(mark));
DoLabel(ref, labelPos, gr, gu);
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);
dogd.refp = p;
}
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_CIRCLE:
case PT_ON_LINE:
case PT_ON_FACE:
case PT_IN_PLANE: {
double s = 8/SS.GW.scale;
Vector p = SS.GetEntity(ptA)->PointGetNum();
Vector r, d;
if(type == PT_ON_FACE) {
Vector n = SS.GetEntity(entityA)->FaceGetNormalNum();
r = n.Normal(0);
d = n.Normal(1);
} else if(type == PT_IN_PLANE) {
Entity *n = SS.GetEntity(entityA)->Normal();
r = n->NormalU();
d = n->NormalV();
} else {
r = gr;
d = gu;
s *= (6.0/8); // draw these a little smaller
}
r = r.WithMagnitude(s); d = d.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 SAME_ORIENTATION: {
for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);
Quaternion q = e->NormalGetNum();
Vector n = q.RotationN().WithMagnitude(25/SS.GW.scale);
Vector u = q.RotationU().WithMagnitude(6/SS.GW.scale);
Vector p = SS.GetEntity(e->point[0])->PointGetNum();
p = p.Plus(n.WithMagnitude(10/SS.GW.scale));
LineDrawOrGetDistance(p.Plus(u), p.Minus(u).Plus(n));
LineDrawOrGetDistance(p.Minus(u), p.Plus(u).Plus(n));
}
break;
}
case EQUAL_ANGLE: {
Vector ref;
Entity *a = SS.GetEntity(entityA);
Entity *b = SS.GetEntity(entityB);
Entity *c = SS.GetEntity(entityC);
Entity *d = SS.GetEntity(entityD);
Vector a0 = a->VectorGetRefPoint();
Vector b0 = b->VectorGetRefPoint();
Vector c0 = c->VectorGetRefPoint();
Vector d0 = d->VectorGetRefPoint();
Vector da = a->VectorGetNum();
Vector db = b->VectorGetNum();
Vector dc = c->VectorGetNum();
Vector dd = d->VectorGetNum();
if(other) da = da.ScaledBy(-1);
DoArcForAngle(a0, da, b0, db,
da.WithMagnitude(40/SS.GW.scale), &ref);
DoArcForAngle(c0, dc, d0, dd,
dc.WithMagnitude(40/SS.GW.scale), &ref);
break;
}
case ANGLE: {
Entity *a = SS.GetEntity(entityA);
Entity *b = SS.GetEntity(entityB);
Vector a0 = a->VectorGetRefPoint();
Vector b0 = b->VectorGetRefPoint();
Vector da = a->VectorGetNum();
Vector db = b->VectorGetNum();
if(other) da = da.ScaledBy(-1);
Vector ref;
DoArcForAngle(a0, da, b0, db, disp.offset, &ref);
DoLabel(ref, labelPos, gr, gu);
break;
}
case PERPENDICULAR: {
Vector u, v;
Vector rn, ru;
if(workplane.v == Entity::FREE_IN_3D.v) {
rn = gn;
ru = gu;
} else {
Entity *normal = SS.GetEntity(workplane)->Normal();
rn = normal->NormalN();
ru = normal->NormalV(); // ru meaning r_up, not u/v
}
for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);
if(i == 0) {
// Calculate orientation of perpendicular sign only
// once, so that it's the same both times it's drawn
u = e->VectorGetNum();
u = u.WithMagnitude(16/SS.GW.scale);
v = (rn.Cross(u)).WithMagnitude(16/SS.GW.scale);
if(fabs(u.Dot(ru)) < fabs(v.Dot(ru))) {
SWAP(Vector, u, v);
}
if(u.Dot(ru) < 0) u = u.ScaledBy(-1);
}
Vector p = e->VectorGetRefPoint();
Vector s = p.Plus(u).Plus(v);
LineDrawOrGetDistance(s, s.Plus(v));
Vector m = s.Plus(v.ScaledBy(0.5));
LineDrawOrGetDistance(m, m.Plus(u));
}
break;
}
case CUBIC_LINE_TANGENT:
case ARC_LINE_TANGENT: {
Vector textAt, u, v;
if(type == ARC_LINE_TANGENT) {
Entity *arc = SS.GetEntity(entityA);
Entity *norm = SS.GetEntity(arc->normal);
Vector c = SS.GetEntity(arc->point[0])->PointGetNum();
Vector p =
SS.GetEntity(arc->point[other ? 2 : 1])->PointGetNum();
Vector r = p.Minus(c);
textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale));
u = norm->NormalU();
v = norm->NormalV();
} else {
Vector n;
if(workplane.v == Entity::FREE_IN_3D.v) {
u = gr;
v = gu;
n = gn;
} else {
Entity *wn = SS.GetEntity(workplane)->Normal();
u = wn->NormalU();
v = wn->NormalV();
n = wn->NormalN();
}
Entity *cubic = SS.GetEntity(entityA);
Vector p =
SS.GetEntity(cubic->point[other ? 3 : 0])->PointGetNum();
Vector dir = SS.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir);
textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale));
}
if(dogd.drawing) {
glPushMatrix();
glxTranslatev(textAt);
glxOntoWorkplane(u, v);
glxWriteTextRefCenter("T");
glPopMatrix();
} else {
dogd.refp = textAt;
Point2d ref = SS.GW.ProjectPoint(dogd.refp);
dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
}
break;
}
case PARALLEL: {
for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);
Vector n = e->VectorGetNum();
n = n.WithMagnitude(25/SS.GW.scale);
Vector u = (gn.Cross(n)).WithMagnitude(4/SS.GW.scale);
Vector p = e->VectorGetRefPoint();
LineDrawOrGetDistance(p.Plus(u), p.Plus(u).Plus(n));
LineDrawOrGetDistance(p.Minus(u), p.Minus(u).Plus(n));
}
break;
}
case EQUAL_RADIUS: {
for(int i = 0; i < 2; i++) {
Entity *circ = SS.GetEntity(i == 0 ? entityA : entityB);
Vector center = SS.GetEntity(circ->point[0])->PointGetNum();
double r = circ->CircleGetRadiusNum();
Quaternion q = circ->Normal()->NormalGetNum();
Vector u = q.RotationU(), v = q.RotationV();
double theta;
if(circ->type == Entity::CIRCLE) {
theta = PI/2;
} else if(circ->type == Entity::ARC_OF_CIRCLE) {
double thetaa, thetab, dtheta;
circ->ArcGetAngles(&thetaa, &thetab, &dtheta);
theta = thetaa + dtheta/2;
} else oops();
Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta)));
d = d.ScaledBy(r);
Vector p = center.Plus(d);
Vector tick = d.WithMagnitude(10/SS.GW.scale);
LineDrawOrGetDistance(p.Plus(tick), p.Minus(tick));
}
break;
}
case LENGTH_RATIO:
case EQUAL_LENGTH_LINES: {
Vector a, b;
for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);
a = SS.GetEntity(e->point[0])->PointGetNum();
b = SS.GetEntity(e->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
DoProjectedPoint(&a);
DoProjectedPoint(&b);
}
DoEqualLenTicks(a, b, gn);
}
if(type == LENGTH_RATIO) {
Vector ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset);
DoLabel(ref, labelPos, gr, gu);
}
break;
}
case EQ_LEN_PT_LINE_D: {
Entity *forLen = SS.GetEntity(entityA);
Vector a = SS.GetEntity(forLen->point[0])->PointGetNum(),
b = SS.GetEntity(forLen->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
DoProjectedPoint(&a);
DoProjectedPoint(&b);
}
DoEqualLenTicks(a, b, gn);
Entity *ln = SS.GetEntity(entityB);
Vector la = SS.GetEntity(ln->point[0])->PointGetNum(),
lb = SS.GetEntity(ln->point[1])->PointGetNum();
Vector pt = SS.GetEntity(ptA)->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
DoProjectedPoint(&pt);
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
}
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
LineDrawOrGetDistance(pt, closest);
DoEqualLenTicks(pt, closest, gn);
break;
}
case EQ_PT_LN_DISTANCES: {
for(int i = 0; i < 2; i++) {
Entity *ln = SS.GetEntity(i == 0 ? entityA : entityB);
Vector la = SS.GetEntity(ln->point[0])->PointGetNum(),
lb = SS.GetEntity(ln->point[1])->PointGetNum();
Entity *pte = SS.GetEntity(i == 0 ? ptA : ptB);
Vector pt = pte->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
DoProjectedPoint(&pt);
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
}
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
LineDrawOrGetDistance(pt, closest);
DoEqualLenTicks(pt, closest, gn);
}
break;
}
{
Vector n;
case SYMMETRIC:
n = SS.GetEntity(entityA)->Normal()->NormalN(); goto s;
case SYMMETRIC_HORIZ:
n = SS.GetEntity(workplane)->Normal()->NormalU(); goto s;
case SYMMETRIC_VERT:
n = SS.GetEntity(workplane)->Normal()->NormalV(); goto s;
case SYMMETRIC_LINE: {
Entity *ln = SS.GetEntity(entityA);
Vector la = SS.GetEntity(ln->point[0])->PointGetNum(),
lb = SS.GetEntity(ln->point[1])->PointGetNum();
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
n = lb.Minus(la);
Vector nw = SS.GetEntity(workplane)->Normal()->NormalN();
n = n.RotatedAbout(nw, PI/2);
goto s;
}
s:
Vector a = SS.GetEntity(ptA)->PointGetNum();
Vector b = SS.GetEntity(ptB)->PointGetNum();
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 {
r = SS.GetEntity(workplane)->Normal()->NormalU();
u = SS.GetEntity(workplane)->Normal()->NormalV();
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 {
dogd.refp = m.Plus(offset);
Point2d ref = SS.GW.ProjectPoint(dogd.refp);
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(workplane);
Vector cu = w->Normal()->NormalU();
Vector cv = w->Normal()->NormalV();
Vector cn = w->Normal()->NormalN();
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;
case COMMENT:
DoLabel(disp.offset, labelPos, gr, gu);
break;
default: oops();
}
}
void Constraint::Draw(void) {
dogd.drawing = true;
glLineWidth(1);
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;
}
Vector Constraint::GetReferencePos(void) {
dogd.drawing = false;
dogd.refp = SS.GW.offset.ScaledBy(-1);
DrawOrGetDistance(NULL);
return dogd.refp;
}