
version of the code from SketchFlat, with all arbitrary limits removed. The TTF text is its own entity, and that entity includes the font file basename and the text. That's an extra 128 bytes in the entity, which is around a 50% increase, kind of a shame. It was simple, though. [git-p4: depot-paths = "//depot/solvespace/": change = 1814]
388 lines
12 KiB
C++
388 lines
12 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 : 2);
|
|
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;
|
|
}
|
|
|
|
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();
|
|
int i, n = (int)(15/sqrt(SS.meshTol));
|
|
Vector prev = p0;
|
|
for(i = 1; i <= n; i++) {
|
|
double t = ((double)i)/n;
|
|
Vector p =
|
|
(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)))));
|
|
LineDrawOrGetDistanceOrEdge(prev, p);
|
|
prev = p;
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
|