412 lines
13 KiB
C++
412 lines
13 KiB
C++
#include "solvespace.h"
|
|
|
|
void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
|
|
if(dogd.drawing) {
|
|
// Draw lines from active group in front of those from previous
|
|
glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3);
|
|
glBegin(GL_LINES);
|
|
glxVertex3v(a);
|
|
glxVertex3v(b);
|
|
glEnd();
|
|
glxDepthRangeOffset(0);
|
|
} else {
|
|
Point2d ap = SS.GW.ProjectPoint(a);
|
|
Point2d bp = SS.GW.ProjectPoint(b);
|
|
|
|
double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
|
|
// A little bit easier to select in the active group
|
|
if(group.v == SS.GW.activeGroup.v) d -= 1;
|
|
dogd.dmin = min(dogd.dmin, d);
|
|
}
|
|
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
|
|
}
|
|
|
|
void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) {
|
|
LineDrawOrGetDistance(a, b);
|
|
if(dogd.edges && !construction) {
|
|
dogd.edges->AddEdge(a, b);
|
|
}
|
|
}
|
|
|
|
void Entity::DrawAll(void) {
|
|
// This handles points and line segments as a special case, because I
|
|
// seem to be able to get a huge speedup that way, by consolidating
|
|
// stuff to gl.
|
|
int i;
|
|
if(SS.GW.showPoints) {
|
|
double s = 3.5/SS.GW.scale;
|
|
Vector r = SS.GW.projRight.ScaledBy(s);
|
|
Vector d = SS.GW.projUp.ScaledBy(s);
|
|
glxColor3d(0, 0.8, 0);
|
|
glxDepthRangeOffset(6);
|
|
glBegin(GL_QUADS);
|
|
for(i = 0; i < SS.entity.n; i++) {
|
|
Entity *e = &(SS.entity.elem[i]);
|
|
if(!e->IsPoint()) continue;
|
|
if(!(SS.GetGroup(e->group)->visible)) continue;
|
|
if(SS.GroupsInOrder(SS.GW.activeGroup, e->group)) continue;
|
|
if(e->forceHidden) continue;
|
|
|
|
Vector v = e->PointGetNum();
|
|
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();
|
|
glxDepthRangeOffset(0);
|
|
}
|
|
|
|
glLineWidth(1.5);
|
|
for(i = 0; i < SS.entity.n; i++) {
|
|
Entity *e = &(SS.entity.elem[i]);
|
|
if(e->IsPoint())
|
|
{
|
|
continue; // already handled
|
|
}
|
|
e->Draw();
|
|
}
|
|
glLineWidth(1);
|
|
}
|
|
|
|
void Entity::Draw(void) {
|
|
dogd.drawing = true;
|
|
dogd.edges = NULL;
|
|
DrawOrGetDistance();
|
|
}
|
|
|
|
void Entity::GenerateEdges(SEdgeList *el) {
|
|
dogd.drawing = false;
|
|
dogd.edges = el;
|
|
DrawOrGetDistance();
|
|
dogd.edges = NULL;
|
|
}
|
|
|
|
double Entity::GetDistance(Point2d mp) {
|
|
dogd.drawing = false;
|
|
dogd.edges = NULL;
|
|
dogd.mp = mp;
|
|
dogd.dmin = 1e12;
|
|
|
|
DrawOrGetDistance();
|
|
|
|
return dogd.dmin;
|
|
}
|
|
|
|
Vector Entity::GetReferencePos(void) {
|
|
dogd.drawing = false;
|
|
dogd.edges = NULL;
|
|
|
|
dogd.refp = SS.GW.offset.ScaledBy(-1);
|
|
DrawOrGetDistance();
|
|
|
|
return dogd.refp;
|
|
}
|
|
|
|
bool Entity::IsVisible(void) {
|
|
Group *g = SS.GetGroup(group);
|
|
|
|
if(g->h.v == Group::HGROUP_REFERENCES.v && IsNormal()) {
|
|
// The reference normals are always shown
|
|
return true;
|
|
}
|
|
if(!g->visible) return false;
|
|
if(SS.GroupsInOrder(SS.GW.activeGroup, group)) return false;
|
|
|
|
// Don't check if points are hidden; this gets called only for
|
|
// selected or hovered points, and those should always be shown.
|
|
if(IsNormal() && !SS.GW.showNormals) return false;
|
|
|
|
if(!SS.GW.showWorkplanes) {
|
|
if(IsWorkplane() && !h.isFromRequest()) {
|
|
if(g->h.v != SS.GW.activeGroup.v) {
|
|
// The group-associated workplanes are hidden outside
|
|
// their group.
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(forceHidden) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Vector Entity::BezierEval(double t, Vector p0, Vector p1, Vector p2, Vector p3)
|
|
{
|
|
return (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)))));
|
|
}
|
|
|
|
void Entity::BezierPwl(double ta, double tb,
|
|
Vector p0, Vector p1, Vector p2, Vector p3)
|
|
{
|
|
Vector pa = BezierEval(ta, p0, p1, p2, p3);
|
|
Vector pb = BezierEval(tb, p0, p1, p2, p3);
|
|
|
|
double tm1 = (2*ta + tb) / 3;
|
|
double tm2 = (ta + 2*tb) / 3;
|
|
|
|
Vector pm1 = BezierEval(tm1, p0, p1, p2, p3);
|
|
Vector pm2 = BezierEval(tm2, p0, p1, p2, p3);
|
|
|
|
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)),
|
|
pm2.DistanceToLine(pa, pb.Minus(pa)));
|
|
|
|
double tol = SS.chordTol/SS.GW.scale;
|
|
|
|
if((tb - ta) < 0.01 || d < tol) {
|
|
LineDrawOrGetDistanceOrEdge(pa, pb);
|
|
} else {
|
|
double tm = (ta + tb) / 2;
|
|
BezierPwl(ta, tm, p0, p1, p2, p3);
|
|
BezierPwl(tm, tb, p0, p1, p2, p3);
|
|
}
|
|
}
|
|
|
|
|
|
void Entity::DrawOrGetDistance(void) {
|
|
// 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(!dogd.edges) {
|
|
if(!IsVisible()) return;
|
|
}
|
|
|
|
Group *g = SS.GetGroup(group);
|
|
|
|
if(group.v != SS.GW.activeGroup.v) {
|
|
glxColor3d(0.5, 0.3, 0.0);
|
|
} else if(construction) {
|
|
glxColor3d(0.1, 0.7, 0.1);
|
|
} else {
|
|
glxColor3d(1, 1, 1);
|
|
}
|
|
|
|
switch(type) {
|
|
case POINT_N_COPY:
|
|
case POINT_N_TRANS:
|
|
case POINT_N_ROT_TRANS:
|
|
case POINT_N_ROT_AA:
|
|
case POINT_IN_3D:
|
|
case POINT_IN_2D: {
|
|
Vector v = PointGetNum();
|
|
dogd.refp = v;
|
|
|
|
if(dogd.drawing) {
|
|
double s = 3.5;
|
|
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);
|
|
glxDepthRangeOffset(6);
|
|
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();
|
|
glxDepthRangeOffset(0);
|
|
} else {
|
|
Point2d pp = SS.GW.ProjectPoint(v);
|
|
dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NORMAL_N_COPY:
|
|
case NORMAL_N_ROT:
|
|
case NORMAL_N_ROT_AA:
|
|
case NORMAL_IN_3D:
|
|
case NORMAL_IN_2D: {
|
|
int i;
|
|
for(i = 0; i < 2; i++) {
|
|
hRequest hr = h.request();
|
|
double f = (i == 0 ? 0.4 : 1);
|
|
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
|
|
glxColor3d(0, 0, f);
|
|
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
|
|
glxColor3d(f, 0, 0);
|
|
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
|
|
glxColor3d(0, f, 0);
|
|
} else {
|
|
glxColor3d(0, 0.4, 0.4);
|
|
if(i > 0) break;
|
|
}
|
|
|
|
Quaternion q = NormalGetNum();
|
|
Vector tail;
|
|
if(i == 0) {
|
|
tail = SS.GetEntity(point[0])->PointGetNum();
|
|
glLineWidth(1);
|
|
} else {
|
|
// Draw an extra copy of the x, y, and z axes, that's
|
|
// always in the corner of the view and at the front.
|
|
// So those are always available, perhaps useful.
|
|
double s = SS.GW.scale;
|
|
double h = 60 - SS.GW.height/2;
|
|
double w = 60 - SS.GW.width/2;
|
|
tail = SS.GW.projRight.ScaledBy(w/s).Plus(
|
|
SS.GW.projUp. ScaledBy(h/s)).Minus(SS.GW.offset);
|
|
glxDepthRangeLockToFront(true);
|
|
glLineWidth(2);
|
|
}
|
|
|
|
Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
|
|
Vector tip = tail.Plus(v);
|
|
LineDrawOrGetDistance(tail, tip);
|
|
|
|
v = v.WithMagnitude(12/SS.GW.scale);
|
|
Vector axis = q.RotationV();
|
|
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
|
|
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
|
|
}
|
|
glxDepthRangeLockToFront(false);
|
|
glLineWidth(1.5);
|
|
break;
|
|
}
|
|
|
|
case DISTANCE:
|
|
case DISTANCE_N_COPY:
|
|
// These are used only as data structures, nothing to display.
|
|
break;
|
|
|
|
case WORKPLANE: {
|
|
Vector p;
|
|
p = SS.GetEntity(point[0])->PointGetNum();
|
|
|
|
Vector u = Normal()->NormalU();
|
|
Vector v = Normal()->NormalV();
|
|
|
|
double s = (min(SS.GW.width, SS.GW.height))*0.45/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), mm2 = mm;
|
|
Vector mp = p.Minus(us).Plus (vs);
|
|
|
|
glLineWidth(1);
|
|
glxColor3d(0, 0.3, 0.3);
|
|
glEnable(GL_LINE_STIPPLE);
|
|
glLineStipple(3, 0x1111);
|
|
if(!h.isFromRequest()) {
|
|
mm = mm.Plus(v.ScaledBy(60/SS.GW.scale));
|
|
mm2 = mm2.Plus(u.ScaledBy(60/SS.GW.scale));
|
|
LineDrawOrGetDistance(mm2, mm);
|
|
}
|
|
LineDrawOrGetDistance(pp, pm);
|
|
LineDrawOrGetDistance(pm, mm2);
|
|
LineDrawOrGetDistance(mm, mp);
|
|
LineDrawOrGetDistance(mp, pp);
|
|
glDisable(GL_LINE_STIPPLE);
|
|
glLineWidth(1.5);
|
|
|
|
char *str = DescriptionString()+5;
|
|
if(dogd.drawing) {
|
|
glPushMatrix();
|
|
glxTranslatev(mm2);
|
|
glxOntoWorkplane(u, v);
|
|
glxWriteText(str);
|
|
glPopMatrix();
|
|
} else {
|
|
Vector pos = mm2.Plus(u.ScaledBy(glxStrWidth(str)/2)).Plus(
|
|
v.ScaledBy(glxStrHeight()/2));
|
|
Point2d pp = SS.GW.ProjectPoint(pos);
|
|
dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10);
|
|
// If a line lies in a plane, then select the line, not
|
|
// the plane.
|
|
dogd.dmin += 3;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case LINE_SEGMENT: {
|
|
Vector a = SS.GetEntity(point[0])->PointGetNum();
|
|
Vector b = SS.GetEntity(point[1])->PointGetNum();
|
|
LineDrawOrGetDistanceOrEdge(a, b);
|
|
break;
|
|
}
|
|
|
|
case CUBIC: {
|
|
Vector p0 = SS.GetEntity(point[0])->PointGetNum();
|
|
Vector p1 = SS.GetEntity(point[1])->PointGetNum();
|
|
Vector p2 = SS.GetEntity(point[2])->PointGetNum();
|
|
Vector p3 = SS.GetEntity(point[3])->PointGetNum();
|
|
BezierPwl(0, 1, p0, p1, p2, p3);
|
|
break;
|
|
}
|
|
|
|
case ARC_OF_CIRCLE: {
|
|
Vector c = SS.GetEntity(point[0])->PointGetNum();
|
|
Vector pa = SS.GetEntity(point[1])->PointGetNum();
|
|
Vector pb = SS.GetEntity(point[2])->PointGetNum();
|
|
Quaternion q = SS.GetEntity(normal)->NormalGetNum();
|
|
Vector u = q.RotationU(), v = q.RotationV();
|
|
|
|
double ra = (pa.Minus(c)).Magnitude();
|
|
double rb = (pb.Minus(c)).Magnitude();
|
|
|
|
double thetaa, thetab, dtheta;
|
|
ArcGetAngles(&thetaa, &thetab, &dtheta);
|
|
|
|
int i, n = 3 + (int)(SS.CircleSides(ra)*dtheta/(2*PI));
|
|
Vector prev = pa;
|
|
for(i = 1; i <= n; i++) {
|
|
double theta = thetaa + (dtheta*i)/n;
|
|
double r = ra + ((rb - ra)*i)/n;
|
|
Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta)));
|
|
Vector p = c.Plus(d.ScaledBy(r));
|
|
LineDrawOrGetDistanceOrEdge(prev, p);
|
|
prev = p;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CIRCLE: {
|
|
Quaternion q = SS.GetEntity(normal)->NormalGetNum();
|
|
double r = SS.GetEntity(distance)->DistanceGetNum();
|
|
Vector center = SS.GetEntity(point[0])->PointGetNum();
|
|
Vector u = q.RotationU(), v = q.RotationV();
|
|
|
|
int i, c = SS.CircleSides(r);
|
|
Vector prev = u.ScaledBy(r).Plus(center);
|
|
for(i = 1; i <= c; i++) {
|
|
double phi = (2*PI*i)/c;
|
|
Vector p = (u.ScaledBy(r*cos(phi))).Plus(
|
|
v.ScaledBy(r*sin(phi)));
|
|
p = p.Plus(center);
|
|
LineDrawOrGetDistanceOrEdge(prev, p);
|
|
prev = p;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TTF_TEXT: {
|
|
Vector topLeft = SS.GetEntity(point[0])->PointGetNum();
|
|
Vector botLeft = SS.GetEntity(point[1])->PointGetNum();
|
|
Vector n = Normal()->NormalN();
|
|
Vector v = topLeft.Minus(botLeft);
|
|
Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
|
|
|
|
SS.fonts.PlotString(font.str, str.str, 0, h, botLeft, u, v);
|
|
break;
|
|
}
|
|
|
|
case FACE_NORMAL_PT:
|
|
case FACE_XPROD:
|
|
case FACE_N_ROT_TRANS:
|
|
case FACE_N_TRANS:
|
|
case FACE_N_ROT_AA:
|
|
// Do nothing; these are drawn with the triangle mesh
|
|
break;
|
|
|
|
default:
|
|
oops();
|
|
}
|
|
}
|
|
|