684 lines
25 KiB
C++
684 lines
25 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::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 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(otherAngle) da = da.ScaledBy(-1);
|
|
|
|
if(workplane.v != Entity::FREE_IN_3D.v) {
|
|
a0 = a0.ProjectInto(workplane);
|
|
b0 = b0.ProjectInto(workplane);
|
|
da = da.ProjectVectorInto(workplane);
|
|
db = db.ProjectVectorInto(workplane);
|
|
}
|
|
|
|
// Make an orthogonal coordinate system from those directions
|
|
Vector dn = da.Cross(db); // normal to both
|
|
Vector dna = dn.Cross(da); // normal to da
|
|
Vector dnb = dn.Cross(db); // normal to db
|
|
// At the intersection of the lines
|
|
// a0 + pa*da = b0 + pb*db (where pa, pb are scalar params)
|
|
// So dot this equation against dna and dnb to get two equations
|
|
// to solve for da and db
|
|
double pb = ((a0.Minus(b0)).Dot(dna))/(db.Dot(dna));
|
|
double pa = -((a0.Minus(b0)).Dot(dnb))/(da.Dot(dnb));
|
|
|
|
Vector pi = a0.Plus(da.ScaledBy(pa));
|
|
Vector ref;
|
|
if(pi.Equals(b0.Plus(db.ScaledBy(pb)))) {
|
|
ref = pi.Plus(disp.offset);
|
|
// We draw in a coordinate system centered at pi, with
|
|
// basis vectors da and dna.
|
|
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 = a->VectorGetRefPoint().Plus(b->VectorGetRefPoint());
|
|
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();
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|