
introduced by the bsp routines. It's usually, though not always, possible to generate a watertight mesh. The occasions where it's not look ugly, floating point issues, no quick fix. And use those to generate a list of edges where two different faces meet, which I can emphasize for cosmetic reasons (and some UI to specify whether to do that, and with what color). And make the right mouse button rotate the model, since that was previously doing nothing. [git-p4: depot-paths = "//depot/solvespace/": change = 1821]
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;
|
|
}
|
|
|