void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { if(dogd.drawing) { // Draw comments in the specified style, but everything else in the // default style for constraints. hStyle hs; if(type == COMMENT && disp.style.v) { hs = disp.style; } else { hs.v = Style::CONSTRAINT; } if(dogd.sel) { dogd.sel->AddEdge(a, b, hs.v); } else { // The only constraints with styles should be comments, so don't // check otherwise, save looking up the styles constantly. if(type == COMMENT && Style::Width(hs) >= 3.0) { ssglFatLine(a, b, Style::Width(hs) / SS.GW.scale); } else { glBegin(GL_LINE_STRIP); ssglVertex3v(a); ssglVertex3v(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); }
void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) { if(dogd.drawing) { // Draw lines from active group in front of those from previous ssglDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3); // Narrow lines are drawn as lines, but fat lines must be drawn as // filled polygons, to get the line join style right. if(!maybeFat || dogd.lineWidth < 3) { glBegin(GL_LINES); ssglVertex3v(a); ssglVertex3v(b); glEnd(); } else { ssglFatLine(a, b, dogd.lineWidth/SS.GW.scale); } ssglDepthRangeOffset(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 GraphicsWindow::Selection::Draw(void) { Vector refp = Vector::From(0, 0, 0); if(entity.v) { Entity *e = SK.GetEntity(entity); e->Draw(); if(emphasized) refp = e->GetReferencePos(); } if(constraint.v) { Constraint *c = SK.GetConstraint(constraint); c->Draw(); if(emphasized) refp = c->GetReferencePos(); } if(emphasized && (constraint.v || entity.v)) { // We want to emphasize this constraint or entity, by drawing a thick // line from the top left corner of the screen to the reference point // of that entity or constraint. double s = 0.501/SS.GW.scale; Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s); topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s)); topLeft = topLeft.Minus(SS.GW.offset); glLineWidth(40); RgbColor rgb = Style::Color(Style::HOVERED); glColor4d(rgb.redF(), rgb.greenF(), rgb.blueF(), 0.2); glBegin(GL_LINES); ssglVertex3v(topLeft); ssglVertex3v(refp); glEnd(); glLineWidth(1); } }
void ssglFatLine(Vector a, Vector b, double width) { // The half-width of the line we're drawing. double hw = width / 2; Vector ab = b.Minus(a); Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); Vector abn = (ab.Cross(gn)).WithMagnitude(1); abn = abn.Minus(gn.ScaledBy(gn.Dot(abn))); // So now abn is normal to the projection of ab into the screen, so the // line will always have constant thickness as the view is rotated. abn = abn.WithMagnitude(hw); ab = gn.Cross(abn); ab = ab. WithMagnitude(hw); // The body of a line is a quad glBegin(GL_QUADS); ssglVertex3v(a.Minus(abn)); ssglVertex3v(b.Minus(abn)); ssglVertex3v(b.Plus (abn)); ssglVertex3v(a.Plus (abn)); glEnd(); // And the line has two semi-circular end caps. FatLineEndcap(a, ab, abn); FatLineEndcap(b, ab.ScaledBy(-1), abn); }
static void LineDrawCallback(void *fndata, Vector a, Vector b) { glLineWidth(1); glBegin(GL_LINES); ssglVertex3v(a); ssglVertex3v(b); glEnd(); }
void Group::Draw(void) { // Everything here gets drawn whether or not the group is hidden; we // can control this stuff independently, with show/hide solids, edges, // mesh, etc. GenerateDisplayItems(); DrawDisplayItems(type); if(!SS.checkClosedContour) return; // And finally show the polygons too, and any errors if it's not possible // to assemble the lines into closed polygons. if(polyError.how == POLY_NOT_CLOSED) { // Report this error only in sketch-in-workplane groups; otherwise // it's just a nuisance. if(type == DRAWING_WORKPLANE) { glDisable(GL_DEPTH_TEST); ssglColorRGBa(Style::Color(Style::DRAW_ERROR), 0.2); ssglLineWidth (Style::Width(Style::DRAW_ERROR)); glBegin(GL_LINES); ssglVertex3v(polyError.notClosedAt.a); ssglVertex3v(polyError.notClosedAt.b); glEnd(); ssglColorRGB(Style::Color(Style::DRAW_ERROR)); ssglWriteText("not closed contour, or not all same style!", Style::DefaultTextHeight(), polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp, NULL, NULL); glEnable(GL_DEPTH_TEST); } } else if(polyError.how == POLY_NOT_COPLANAR || polyError.how == POLY_SELF_INTERSECTING || polyError.how == POLY_ZERO_LEN_EDGE) { // These errors occur at points, not lines if(type == DRAWING_WORKPLANE) { glDisable(GL_DEPTH_TEST); ssglColorRGB(Style::Color(Style::DRAW_ERROR)); const char *msg; if(polyError.how == POLY_NOT_COPLANAR) { msg = "points not all coplanar!"; } else if(polyError.how == POLY_SELF_INTERSECTING) { msg = "contour is self-intersecting!"; } else { msg = "zero-length edge!"; } ssglWriteText(msg, Style::DefaultTextHeight(), polyError.errorPointAt, SS.GW.projRight, SS.GW.projUp, NULL, NULL); glEnable(GL_DEPTH_TEST); } } else { // The contours will get filled in DrawFilledPaths. } }
void SBsp2::DebugDraw(Vector n, double d) { if(!this) return; if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops(); if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops(); glLineWidth(10); glBegin(GL_LINES); ssglVertex3v(edge.a); ssglVertex3v(edge.b); glEnd(); pos->DebugDraw(n, d); neg->DebugDraw(n, d); more->DebugDraw(n, d); glLineWidth(1); }
void SBsp3::DebugDraw(void) { if(!this) return; pos->DebugDraw(); Vector norm = tri.Normal(); glNormal3d(norm.x, norm.y, norm.z); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glBegin(GL_TRIANGLES); ssglVertex3v(tri.a); ssglVertex3v(tri.b); ssglVertex3v(tri.c); glEnd(); glDisable(GL_LIGHTING); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); ssglDepthRangeOffset(2); glBegin(GL_TRIANGLES); ssglVertex3v(tri.a); ssglVertex3v(tri.b); ssglVertex3v(tri.c); glEnd(); glDisable(GL_LIGHTING); glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); glPointSize(10); ssglDepthRangeOffset(2); glBegin(GL_TRIANGLES); ssglVertex3v(tri.a); ssglVertex3v(tri.b); ssglVertex3v(tri.c); glEnd(); ssglDepthRangeOffset(0); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); more->DebugDraw(); neg->DebugDraw(); edges->DebugDraw(n, d); }
static void FatLineEndcap(Vector p, Vector u, Vector v) { // A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10 static const double Circle[11][2] = { { 0.0000, 1.0000 }, { -0.3090, 0.9511 }, { -0.5878, 0.8090 }, { -0.8090, 0.5878 }, { -0.9511, 0.3090 }, { -1.0000, 0.0000 }, { -0.9511, -0.3090 }, { -0.8090, -0.5878 }, { -0.5878, -0.8090 }, { -0.3090, -0.9511 }, { 0.0000, -1.0000 }, }; glBegin(GL_TRIANGLE_FAN); for(int i = 0; i <= 10; i++) { double c = Circle[i][0], s = Circle[i][1]; ssglVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s))); } glEnd(); }
void Constraint::DrawOrGetDistance(Vector *labelPos) { if(!SS.GW.showConstraints) return; Group *g = SK.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; except for comments // with an assigned style. if(g->h.v != SS.GW.activeGroup.v && !(type == COMMENT && disp.style.v)) { return; } if(disp.style.v) { Style *s = Style::Get(disp.style); if(!s->visible) 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); switch(type) { case PT_PT_DISTANCE: { Vector ap = SK.GetEntity(ptA)->PointGetNum(); Vector bp = SK.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); DoLineWithArrows(ref, ap, bp, false); DoLabel(ref, labelPos, gr, gu); break; } case PROJ_PT_DISTANCE: { Vector ap = SK.GetEntity(ptA)->PointGetNum(), bp = SK.GetEntity(ptB)->PointGetNum(), dp = (bp.Minus(ap)), pp = SK.GetEntity(entityA)->VectorGetNum(); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); pp = pp.WithMagnitude(1); double d = dp.Dot(pp); Vector bpp = ap.Plus(pp.ScaledBy(d)); StippledLine(ap, bpp); StippledLine(bp, bpp); DoLineWithArrows(ref, ap, bpp, false); DoLabel(ref, labelPos, gr, gu); break; } case PT_FACE_DISTANCE: case PT_PLANE_DISTANCE: { Vector pt = SK.GetEntity(ptA)->PointGetNum(); Entity *enta = SK.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)); Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset); if(!pt.Equals(closest)) { DoLineWithArrows(ref, pt, closest, true); } DoLabel(ref, labelPos, gr, gu); break; } case PT_LINE_DISTANCE: { Vector pt = SK.GetEntity(ptA)->PointGetNum(); Entity *line = SK.GetEntity(entityA); Vector lA = SK.GetEntity(line->point[0])->PointGetNum(); Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); Vector dl = lB.Minus(lA); 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, dl); Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset); DoLabel(ref, labelPos, gr, gu); if(!pt.Equals(closest)) { DoLineWithArrows(ref, pt, closest, true); } 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 = SK.GetEntity(line->point[0])->PointGetNum(); Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); Vector c2 = (lA.ScaledBy(1-t)).Plus(lB.ScaledBy(t)); DoProjectedPoint(&c2); } break; } case DIAMETER: { Entity *circle = SK.GetEntity(entityA); Vector center = SK.GetEntity(circle->point[0])->PointGetNum(); Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum(); Vector n = q.RotationN().WithMagnitude(1); double r = circle->CircleGetRadiusNum(); Vector ref = center.Plus(disp.offset); // Force the label into the same plane as the circle. ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center))); Vector mark = ref.Minus(center); mark = mark.WithMagnitude(mark.Magnitude()-r); DoLineTrimmedAgainstBox(ref, ref, ref.Minus(mark)); Vector topLeft; DoLabel(ref, &topLeft, gr, gu); if(labelPos) *labelPos = topLeft; // Show this as diameter or radius? if(!other) { // Draw the diameter symbol Vector dc = topLeft; dc = dc.Plus(gu.WithMagnitude(5/SS.GW.scale)); dc = dc.Plus(gr.WithMagnitude(9/SS.GW.scale)); double dr = 5/SS.GW.scale; double theta, dtheta = (2*PI)/12; for(theta = 0; theta < 2*PI-0.01; theta += dtheta) { LineDrawOrGetDistance( dc.Plus(gu.WithMagnitude(cos(theta)*dr)).Plus( gr.WithMagnitude(sin(theta)*dr)), dc.Plus(gu.WithMagnitude(cos(theta+dtheta)*dr)).Plus( gr.WithMagnitude(sin(theta+dtheta)*dr))); } theta = 25*(PI/180); dr *= 1.7; dtheta = PI; LineDrawOrGetDistance( dc.Plus(gu.WithMagnitude(cos(theta)*dr)).Plus( gr.WithMagnitude(sin(theta)*dr)), dc.Plus(gu.WithMagnitude(cos(theta+dtheta)*dr)).Plus( gr.WithMagnitude(sin(theta+dtheta)*dr))); } break; } case POINTS_COINCIDENT: { if(!dogd.drawing) { for(int i = 0; i < 2; i++) { Vector p = SK.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; } if(dogd.drawing) { // Let's adjust the color of this constraint to have the same // rough luma as the point color, so that the constraint does not // stand out in an ugly way. RgbaColor cd = Style::Color(Style::DATUM), cc = Style::Color(Style::CONSTRAINT); // convert from 8-bit color to a vector Vector vd = Vector::From(cd.redF(), cd.greenF(), cd.blueF()), vc = Vector::From(cc.redF(), cc.greenF(), cc.blueF()); // and scale the constraint color to have the same magnitude as // the datum color, maybe a bit dimmer vc = vc.WithMagnitude(vd.Magnitude()*0.9); // and set the color to that. ssglColorRGB(RGBf(vc.x, vc.y, vc.z)); 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 = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); glBegin(GL_QUADS); ssglVertex3v(p.Plus (r).Plus (d)); ssglVertex3v(p.Plus (r).Minus(d)); ssglVertex3v(p.Minus(r).Minus(d)); ssglVertex3v(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 = SK.GetEntity(ptA)->PointGetNum(); Vector r, d; if(type == PT_ON_FACE) { Vector n = SK.GetEntity(entityA)->FaceGetNormalNum(); r = n.Normal(0); d = n.Normal(1); } else if(type == PT_IN_PLANE) { EntityBase *n = SK.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 WHERE_DRAGGED: { Vector p = SK.GetEntity(ptA)->PointGetNum(), u = p.Plus(gu.WithMagnitude(8/SS.GW.scale)).Plus( gr.WithMagnitude(8/SS.GW.scale)), uu = u.Minus(gu.WithMagnitude(5/SS.GW.scale)), ur = u.Minus(gr.WithMagnitude(5/SS.GW.scale)); // Draw four little crop marks, uniformly spaced (by ninety // degree rotations) around the point. int i; for(i = 0; i < 4; i++) { LineDrawOrGetDistance(u, uu); LineDrawOrGetDistance(u, ur); u = u.RotatedAbout(p, gn, PI/2); ur = ur.RotatedAbout(p, gn, PI/2); uu = uu.RotatedAbout(p, gn, PI/2); } break; } case SAME_ORIENTATION: { for(int i = 0; i < 2; i++) { Entity *e = SK.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 = SK.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 = SK.GetEntity(entityA); Entity *b = SK.GetEntity(entityB); Entity *c = SK.GetEntity(entityC); Entity *d = SK.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 = SK.GetEntity(entityA); Entity *b = SK.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 = Vector::From(0, 0, 0), v = Vector::From(0, 0, 0); Vector rn, ru; if(workplane.v == Entity::FREE_IN_3D.v) { rn = gn; ru = gu; } else { EntityBase *normal = SK.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 = SK.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); // a bit of bias to stop it from flickering between the // two possibilities if(fabs(u.Dot(ru)) < fabs(v.Dot(ru)) + LENGTH_EPS) { 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 CURVE_CURVE_TANGENT: case CUBIC_LINE_TANGENT: case ARC_LINE_TANGENT: { Vector textAt, u, v; if(type == ARC_LINE_TANGENT) { Entity *arc = SK.GetEntity(entityA); Entity *norm = SK.GetEntity(arc->normal); Vector c = SK.GetEntity(arc->point[0])->PointGetNum(); Vector p = SK.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 if(type == CUBIC_LINE_TANGENT) { Vector n; if(workplane.v == Entity::FREE_IN_3D.v) { u = gr; v = gu; n = gn; } else { EntityBase *wn = SK.GetEntity(workplane)->Normal(); u = wn->NormalU(); v = wn->NormalV(); n = wn->NormalN(); } Entity *cubic = SK.GetEntity(entityA); Vector p = other ? cubic->CubicGetFinishNum() : cubic->CubicGetStartNum(); Vector dir = SK.GetEntity(entityB)->VectorGetNum(); Vector out = n.Cross(dir); textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale)); } else { Vector n, dir; EntityBase *wn = SK.GetEntity(workplane)->Normal(); u = wn->NormalU(); v = wn->NormalV(); n = wn->NormalN(); EntityBase *eA = SK.GetEntity(entityA); // Big pain; we have to get a vector tangent to the curve // at the shared point, which could be from either a cubic // or an arc. if(other) { textAt = eA->EndpointFinish(); if(eA->type == Entity::CUBIC) { dir = eA->CubicGetFinishTangentNum(); } else { dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus( SK.GetEntity(eA->point[2])->PointGetNum()); dir = n.Cross(dir); } } else { textAt = eA->EndpointStart(); if(eA->type == Entity::CUBIC) { dir = eA->CubicGetStartTangentNum(); } else { dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus( SK.GetEntity(eA->point[1])->PointGetNum()); dir = n.Cross(dir); } } dir = n.Cross(dir); textAt = textAt.Plus(dir.WithMagnitude(14/SS.GW.scale)); } if(dogd.drawing) { ssglWriteTextRefCenter("T", DEFAULT_TEXT_HEIGHT, textAt, u, v, LineCallback, this); } 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 = SK.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++) { DoEqualRadiusTicks(i == 0 ? entityA : entityB); } break; } case EQUAL_LINE_ARC_LEN: { Entity *line = SK.GetEntity(entityA); DoEqualLenTicks( SK.GetEntity(line->point[0])->PointGetNum(), SK.GetEntity(line->point[1])->PointGetNum(), gn); DoEqualRadiusTicks(entityB); break; } case LENGTH_RATIO: case EQUAL_LENGTH_LINES: { Vector a, b = Vector::From(0, 0, 0); for(int i = 0; i < 2; i++) { Entity *e = SK.GetEntity(i == 0 ? entityA : entityB); a = SK.GetEntity(e->point[0])->PointGetNum(); b = SK.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 = SK.GetEntity(entityA); Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(), b = SK.GetEntity(forLen->point[1])->PointGetNum(); if(workplane.v != Entity::FREE_IN_3D.v) { DoProjectedPoint(&a); DoProjectedPoint(&b); } DoEqualLenTicks(a, b, gn); Entity *ln = SK.GetEntity(entityB); Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), lb = SK.GetEntity(ln->point[1])->PointGetNum(); Vector pt = SK.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 = SK.GetEntity(i == 0 ? entityA : entityB); Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), lb = SK.GetEntity(ln->point[1])->PointGetNum(); Entity *pte = SK.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; } { case SYMMETRIC: Vector n; n = SK.GetEntity(entityA)->Normal()->NormalN(); goto s; case SYMMETRIC_HORIZ: n = SK.GetEntity(workplane)->Normal()->NormalU(); goto s; case SYMMETRIC_VERT: n = SK.GetEntity(workplane)->Normal()->NormalV(); goto s; case SYMMETRIC_LINE: { Entity *ln = SK.GetEntity(entityA); Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), lb = SK.GetEntity(ln->point[1])->PointGetNum(); la = la.ProjectInto(workplane); lb = lb.ProjectInto(workplane); n = lb.Minus(la); Vector nw = SK.GetEntity(workplane)->Normal()->NormalN(); n = n.RotatedAbout(nw, PI/2); goto s; } s: Vector a = SK.GetEntity(ptA)->PointGetNum(); Vector b = SK.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 = SK.GetEntity(workplane)->Normal()->NormalU(); u = SK.GetEntity(workplane)->Normal()->NormalV(); n = r.Cross(u); } // For "at midpoint", this branch is always taken. Entity *e = SK.GetEntity(entityA); Vector a = SK.GetEntity(e->point[0])->PointGetNum(); Vector b = SK.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) { const char *s = (type == HORIZONTAL) ? "H" : ( (type == VERTICAL) ? "V" : ( (type == AT_MIDPOINT) ? "M" : NULL)); ssglWriteTextRefCenter(s, DEFAULT_TEXT_HEIGHT, m.Plus(offset), r, u, LineCallback, this); } 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 = SK.GetEntity(ptA)->PointGetNum(); Vector b = SK.GetEntity(ptB)->PointGetNum(); Entity *w = SK.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); ssglVertex3v((c.Plus(d)).Plus(dp)); ssglVertex3v((c.Minus(d)).Plus(dp)); ssglVertex3v((c.Minus(d)).Minus(dp)); ssglVertex3v((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: { if(dogd.drawing && disp.style.v) { ssglLineWidth(Style::Width(disp.style)); ssglColorRGB(Style::Color(disp.style)); } Vector u, v; if(workplane.v == Entity::FREE_IN_3D.v) { u = gr; v = gu; } else { EntityBase *norm = SK.GetEntity(workplane)->Normal(); u = norm->NormalU(); v = norm->NormalV(); } DoLabel(disp.offset, labelPos, u, v); break; } default: oops(); } }
void GraphicsWindow::Paint(void) { int i; havePainted = true; int w, h; GetGraphicsWindowSize(&w, &h); width = w; height = h; glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000); double mat[16]; // Last thing before display is to apply the perspective double clp = SS.CameraTangent()*scale; MakeMatrix(mat, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, clp, 1); glMultMatrixd(mat); // Before that, we apply the rotation Vector n = projUp.Cross(projRight); MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0, projUp.x, projUp.y, projUp.z, 0, n.x, n.y, n.z, 0, 0, 0, 0, 1); glMultMatrixd(mat); // And before that, the translation MakeMatrix(mat, 1, 0, 0, offset.x, 0, 1, 0, offset.y, 0, 0, 1, offset.z, 0, 0, 0, 1); glMultMatrixd(mat); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glShadeModel(GL_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); // don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards, // drawn with leaks in the mesh glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_DEPTH_TEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glEnable(GL_NORMALIZE); // At the same depth, we want later lines drawn over earlier. glDepthFunc(GL_LEQUAL); if(SS.AllGroupsOkay()) { glClearColor(SS.backgroundColor.redF(), SS.backgroundColor.greenF(), SS.backgroundColor.blueF(), 1.0f); } else { // Draw a different background whenever we're having solve problems. RgbColor rgb = Style::Color(Style::DRAW_ERROR); glClearColor(0.4f*rgb.redF(), 0.4f*rgb.greenF(), 0.4f*rgb.blueF(), 1.0f); // And show the text window, which has info to debug it ForceTextWindowShown(); } glClear(GL_COLOR_BUFFER_BIT); glClearDepth(1.0); glClear(GL_DEPTH_BUFFER_BIT); if(SS.bgImage.fromFile) { // If a background image is loaded, then we draw it now as a texture. // This handles the resizing for us nicely. glBindTexture(GL_TEXTURE_2D, TEXTURE_BACKGROUND_IMG); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SS.bgImage.rw, SS.bgImage.rh, 0, GL_RGB, GL_UNSIGNED_BYTE, SS.bgImage.fromFile); double tw = ((double)SS.bgImage.w) / SS.bgImage.rw, th = ((double)SS.bgImage.h) / SS.bgImage.rh; double mmw = SS.bgImage.w / SS.bgImage.scale, mmh = SS.bgImage.h / SS.bgImage.scale; Vector origin = SS.bgImage.origin; origin = origin.DotInToCsys(projRight, projUp, n); // Place the depth of our origin at the point that corresponds to // w = 1, so that it's unaffected by perspective. origin.z = (offset.ScaledBy(-1)).Dot(n); origin = origin.ScaleOutOfCsys(projRight, projUp, n); // Place the background at the very back of the Z order, though, by // mucking with the depth range. glDepthRange(1, 1); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2d(0, 0); ssglVertex3v(origin); glTexCoord2d(0, th); ssglVertex3v(origin.Plus(projUp.ScaledBy(mmh))); glTexCoord2d(tw, th); ssglVertex3v(origin.Plus(projRight.ScaledBy(mmw).Plus( projUp. ScaledBy(mmh)))); glTexCoord2d(tw, 0); ssglVertex3v(origin.Plus(projRight.ScaledBy(mmw))); glEnd(); glDisable(GL_TEXTURE_2D); } ssglDepthRangeOffset(0); // Nasty case when we're reloading the imported files; could be that // we get an error, so a dialog pops up, and a message loop starts, and // we have to get called to paint ourselves. If the sketch is screwed // up, then we could trigger an oops trying to draw. if(!SS.allConsistent) return; // Let's use two lights, at the user-specified locations GLfloat f; glEnable(GL_LIGHT0); f = (GLfloat)SS.lightIntensity[0]; GLfloat li0[] = { f, f, f, 1.0f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, li0); glLightfv(GL_LIGHT0, GL_SPECULAR, li0); glEnable(GL_LIGHT1); f = (GLfloat)SS.lightIntensity[1]; GLfloat li1[] = { f, f, f, 1.0f }; glLightfv(GL_LIGHT1, GL_DIFFUSE, li1); glLightfv(GL_LIGHT1, GL_SPECULAR, li1); Vector ld; ld = VectorFromProjs(SS.lightDir[0]); GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 }; glLightfv(GL_LIGHT0, GL_POSITION, ld0); ld = VectorFromProjs(SS.lightDir[1]); GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 }; glLightfv(GL_LIGHT1, GL_POSITION, ld1); if(SS.drawBackFaces) { // For debugging, draw the backs of the triangles in red, so that we // notice when a shell is open glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1); } else { glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0); } GLfloat ambient[4] = { (float)SS.ambientIntensity, (float)SS.ambientIntensity, (float)SS.ambientIntensity, 1 }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); ssglUnlockColor(); if(showSnapGrid && LockedInWorkplane()) { hEntity he = ActiveWorkplane(); EntityBase *wrkpl = SK.GetEntity(he), *norm = wrkpl->Normal(); Vector wu, wv, wn, wp; wp = SK.GetEntity(wrkpl->point[0])->PointGetNum(); wu = norm->NormalU(); wv = norm->NormalV(); wn = norm->NormalN(); double g = SS.gridSpacing; double umin = VERY_POSITIVE, umax = VERY_NEGATIVE, vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE; int a; for(a = 0; a < 4; a++) { // Ideally, we would just do +/- half the width and height; but // allow some extra slop for rounding. Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g), vert = projUp. ScaledBy((0.6*height)/scale + 2*g); if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1); if(a == 1 || a == 3) vert = vert. ScaledBy(-1); Vector tp = horiz.Plus(vert).Minus(offset); // Project the point into our grid plane, normal to the screen // (not to the grid plane). If the plane is on edge then this is // impossible so don't try to draw the grid. bool parallel; Vector tpp = Vector::AtIntersectionOfPlaneAndLine( wn, wn.Dot(wp), tp, tp.Plus(n), ¶llel); if(parallel) goto nogrid; tpp = tpp.Minus(wp); double uu = tpp.Dot(wu), vv = tpp.Dot(wv); umin = min(uu, umin); umax = max(uu, umax); vmin = min(vv, vmin); vmax = max(vv, vmax); } int i, j, i0, i1, j0, j1; i0 = (int)(umin / g); i1 = (int)(umax / g); j0 = (int)(vmin / g); j1 = (int)(vmax / g); if(i0 > i1 || i1 - i0 > 400) goto nogrid; if(j0 > j1 || j1 - j0 > 400) goto nogrid; glLineWidth(1); ssglColorRGBa(Style::Color(Style::DATUM), 0.3); glBegin(GL_LINES); for(i = i0 + 1; i < i1; i++) { ssglVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g))); ssglVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g))); } for(j = j0 + 1; j < j1; j++) { ssglVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g))); ssglVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g))); } glEnd(); // Clear the depth buffer, so that the grid is at the very back of // the Z order. glClear(GL_DEPTH_BUFFER_BIT); nogrid:; } // Draw the active group; this does stuff like the mesh and edges. (SK.GetGroup(activeGroup))->Draw(); // Now draw the entities if(showHdnLines) glDisable(GL_DEPTH_TEST); Entity::DrawAll(); // Draw filled paths in all groups, when those filled paths were requested // specially by assigning a style with a fill color, or when the filled // paths are just being filled by default. This should go last, to make // the transparency work. Group *g; for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) { if(!(g->IsVisible())) continue; g->DrawFilledPaths(); } glDisable(GL_DEPTH_TEST); // Draw the constraints for(i = 0; i < SK.constraint.n; i++) { SK.constraint.elem[i].Draw(); } // Draw the traced path, if one exists glLineWidth(Style::Width(Style::ANALYZE)); ssglColorRGB(Style::Color(Style::ANALYZE)); SContour *sc = &(SS.traced.path); glBegin(GL_LINE_STRIP); for(i = 0; i < sc->l.n; i++) { ssglVertex3v(sc->l.elem[i].p); } glEnd(); // And the naked edges, if the user did Analyze -> Show Naked Edges. glLineWidth(Style::Width(Style::DRAW_ERROR)); ssglColorRGB(Style::Color(Style::DRAW_ERROR)); ssglDrawEdges(&(SS.nakedEdges), true); // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); ssglLockColorTo(Style::Color(Style::HOVERED)); hover.Draw(); // And finally draw the selection, same mechanism. ssglLockColorTo(Style::Color(Style::SELECTED)); for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) { s->Draw(); } ssglUnlockColor(); // If a marquee selection is in progress, then draw the selection // rectangle, as an outline and a transparent fill. if(pending.operation == DRAGGING_MARQUEE) { Point2d begin = ProjectPoint(orig.marqueePoint); double xmin = min(orig.mouse.x, begin.x), xmax = max(orig.mouse.x, begin.x), ymin = min(orig.mouse.y, begin.y), ymax = max(orig.mouse.y, begin.y); Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)), tr = UnProjectPoint(Point2d::From(xmax, ymin)), br = UnProjectPoint(Point2d::From(xmax, ymax)), bl = UnProjectPoint(Point2d::From(xmin, ymax)); glLineWidth((GLfloat)1.3); ssglColorRGB(Style::Color(Style::HOVERED)); glBegin(GL_LINE_LOOP); ssglVertex3v(tl); ssglVertex3v(tr); ssglVertex3v(br); ssglVertex3v(bl); glEnd(); ssglColorRGBa(Style::Color(Style::HOVERED), 0.10); glBegin(GL_QUADS); ssglVertex3v(tl); ssglVertex3v(tr); ssglVertex3v(br); ssglVertex3v(bl); glEnd(); } // An extra line, used to indicate the origin when rotating within the // plane of the monitor. if(SS.extraLine.draw) { glLineWidth(1); ssglLockColorTo(Style::Color(Style::DATUM)); glBegin(GL_LINES); ssglVertex3v(SS.extraLine.ptA); ssglVertex3v(SS.extraLine.ptB); glEnd(); } // A note to indicate the origin in the just-exported file. if(SS.justExportedInfo.draw) { ssglColorRGB(Style::Color(Style::DATUM)); Vector p = SS.justExportedInfo.pt, u = SS.justExportedInfo.u, v = SS.justExportedInfo.v; glLineWidth(1.5); glBegin(GL_LINES); ssglVertex3v(p.Plus(u.WithMagnitude(-15/scale))); ssglVertex3v(p.Plus(u.WithMagnitude(30/scale))); ssglVertex3v(p.Plus(v.WithMagnitude(-15/scale))); ssglVertex3v(p.Plus(v.WithMagnitude(30/scale))); glEnd(); ssglWriteText("(x, y) = (0, 0) for file just exported", DEFAULT_TEXT_HEIGHT, p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)), u, v, NULL, NULL); ssglWriteText("press Esc to clear this message", DEFAULT_TEXT_HEIGHT, p.Plus(u.ScaledBy(40/scale)).Plus( v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)), u, v, NULL, NULL); } // And finally the toolbar. if(SS.showToolbar) { ToolbarDraw(); } }
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); ssglColorRGB(Style::Color(Style::DATUM)); ssglDepthRangeOffset(6); glBegin(GL_QUADS); for(i = 0; i < SK.entity.n; i++) { Entity *e = &(SK.entity.elem[i]); if(!e->IsPoint()) continue; if(!(SK.GetGroup(e->group)->IsVisible())) continue; if(e->forceHidden) continue; Vector v = e->PointGetNum(); // If we're analyzing the sketch to show the degrees of freedom, // then we draw big colored squares over the points that are // free to move. bool free = false; if(e->type == POINT_IN_3D) { Param *px = SK.GetParam(e->param[0]), *py = SK.GetParam(e->param[1]), *pz = SK.GetParam(e->param[2]); free = (px->free) || (py->free) || (pz->free); } else if(e->type == POINT_IN_2D) { Param *pu = SK.GetParam(e->param[0]), *pv = SK.GetParam(e->param[1]); free = (pu->free) || (pv->free); } if(free) { Vector re = r.ScaledBy(2.5), de = d.ScaledBy(2.5); ssglColorRGB(Style::Color(Style::ANALYZE)); ssglVertex3v(v.Plus (re).Plus (de)); ssglVertex3v(v.Plus (re).Minus(de)); ssglVertex3v(v.Minus(re).Minus(de)); ssglVertex3v(v.Minus(re).Plus (de)); ssglColorRGB(Style::Color(Style::DATUM)); } ssglVertex3v(v.Plus (r).Plus (d)); ssglVertex3v(v.Plus (r).Minus(d)); ssglVertex3v(v.Minus(r).Minus(d)); ssglVertex3v(v.Minus(r).Plus (d)); } glEnd(); ssglDepthRangeOffset(0); } for(i = 0; i < SK.entity.n; i++) { Entity *e = &(SK.entity.elem[i]); if(e->IsPoint()) { continue; // already handled } e->Draw(); } }
void Entity::DrawOrGetDistance(void) { if(!IsVisible()) return; Group *g = SK.GetGroup(group); 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); ssglColorRGB(Style::Color(Style::DATUM)); ssglDepthRangeOffset(6); glBegin(GL_QUADS); ssglVertex3v(v.Plus (r).Plus (d)); ssglVertex3v(v.Plus (r).Minus(d)); ssglVertex3v(v.Minus(r).Minus(d)); ssglVertex3v(v.Minus(r).Plus (d)); glEnd(); ssglDepthRangeOffset(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++) { if(i == 0 && !SS.GW.showNormals) { // When the normals are hidden, we will continue to show // the coordinate axes at the bottom left corner, but // not at the origin. continue; } hRequest hr = h.request(); // Always draw the x, y, and z axes in red, green, and blue; // brighter for the ones at the bottom left of the screen, // dimmer for the ones at the model origin. int f = (i == 0 ? 100 : 255); if(hr.v == Request::HREQUEST_REFERENCE_XY.v) { ssglColorRGB(RGBi(0, 0, f)); } else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) { ssglColorRGB(RGBi(f, 0, 0)); } else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) { ssglColorRGB(RGBi(0, f, 0)); } else { ssglColorRGB(Style::Color(Style::NORMALS)); if(i > 0) break; } Quaternion q = NormalGetNum(); Vector tail; if(i == 0) { tail = SK.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); ssglDepthRangeLockToFront(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))); } ssglDepthRangeLockToFront(false); break; } case DISTANCE: case DISTANCE_N_COPY: // These are used only as data structures, nothing to display. break; case WORKPLANE: { Vector p; p = SK.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); ssglColorRGB(Style::Color(Style::NORMALS)); 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); char *str = DescriptionString()+5; double th = DEFAULT_TEXT_HEIGHT; if(dogd.drawing) { ssglWriteText(str, th, mm2, u, v, NULL, NULL); } else { Vector pos = mm2.Plus(u.ScaledBy(ssglStrWidth(str, th)/2)).Plus( v.ScaledBy(ssglStrHeight(th)/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: case CIRCLE: case ARC_OF_CIRCLE: case CUBIC: case CUBIC_PERIODIC: case TTF_TEXT: // Nothing but the curve(s). 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(); } // And draw the curves; generate the rational polynomial curves for // everything, then piecewise linearize them, and display those. SEdgeList sel; ZERO(&sel); GenerateEdges(&sel, true); int i; for(i = 0; i < sel.l.n; i++) { SEdge *se = &(sel.l.elem[i]); LineDrawOrGetDistance(se->a, se->b, true); } sel.Clear(); }