void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { double th; if(type == COMMENT) { th = Style::TextHeight(disp.style); } else { th = DEFAULT_TEXT_HEIGHT; } char *s = Label(); double swidth = ssglStrWidth(s, th), sheight = ssglStrHeight(th); // By default, the reference is from the center; but the style could // specify otherwise if one is present, and it could also specify a // rotation. if(type == COMMENT && disp.style.v) { Style *st = Style::Get(disp.style); // rotation first double rads = st->textAngle*PI/180; double c = cos(rads), s = sin(rads); Vector pr = gr, pu = gu; gr = pr.ScaledBy( c).Plus(pu.ScaledBy(s)); gu = pr.ScaledBy(-s).Plus(pu.ScaledBy(c)); // then origin int o = st->textOrigin; if(o & Style::ORIGIN_LEFT) ref = ref.Plus(gr.WithMagnitude(swidth/2)); if(o & Style::ORIGIN_RIGHT) ref = ref.Minus(gr.WithMagnitude(swidth/2)); if(o & Style::ORIGIN_BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2)); if(o & Style::ORIGIN_TOP) ref = ref.Minus(gu.WithMagnitude(sheight/2)); } 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) { ssglWriteTextRefCenter(s, th, ref, gr, gu, LineCallback, this); } 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 - (th / 2)); dogd.refp = ref; } }
void ssglWriteTextRefCenter(const char *str, double h, Vector t, Vector u, Vector v, ssglLineFn *fn, void *fndata) { u = u.WithMagnitude(1); v = v.WithMagnitude(1); double scale = FONT_SCALE(h)/SS.GW.scale; double fh = ssglStrHeight(h); double fw = ssglStrWidth(str, h); t = t.Plus(u.ScaledBy(-fw/2)); t = t.Plus(v.ScaledBy(-fh/2)); // Undo the (+5, +5) offset that ssglWriteText applies. t = t.Plus(u.ScaledBy(-5*scale)); t = t.Plus(v.ScaledBy(-5*scale)); ssglWriteText(str, h, t, u, v, fn, fndata); }
void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db, Vector offset, Vector *ref) { Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale); Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale); if(workplane.v != Entity::FREE_IN_3D.v) { a0 = a0.ProjectInto(workplane); b0 = b0.ProjectInto(workplane); da = da.ProjectVectorInto(workplane); db = db.ProjectVectorInto(workplane); } bool skew; Vector pi = Vector::AtIntersectionOfLines(a0, a0.Plus(da), b0, b0.Plus(db), &skew); if(!skew) { *ref = pi.Plus(offset); // We draw in a coordinate system centered at the intersection point. // One basis vector is da, and the other is normal to da and in // the plane that contains our lines (so normal to its normal). Vector dna = (da.Cross(db)).Cross(da); 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; } // The elliptical approximation isn't exactly right, but the correct // calculation (against the bounding box of the text) would be rather // complex and this looks pretty good. double tl = atan2(rm.Dot(gu), rm.Dot(gr)); double adj = EllipticalInterpolation( ssglStrWidth(Label(), DEFAULT_TEXT_HEIGHT)/2, ssglStrHeight(DEFAULT_TEXT_HEIGHT)/2, tl); *ref = (*ref).Plus(rm.WithMagnitude(adj + 3/SS.GW.scale)); } else { // The lines are skew; no wonderful way to illustrate that. *ref = a0.Plus(b0); *ref = (*ref).ScaledBy(0.5).Plus(disp.offset); gu = gu.WithMagnitude(1); Vector trans = (*ref).Plus(gu.ScaledBy(-1.5*ssglStrHeight(DEFAULT_TEXT_HEIGHT))); ssglWriteTextRefCenter("angle between skew lines", DEFAULT_TEXT_HEIGHT, trans, gr, gu, LineCallback, this); } }
//----------------------------------------------------------------------------- // There is a rectangular box, aligned to our display axes (projRight, projUp) // centered at ref. This is where a dimension label will be drawn. We want to // draw a line from A to B. If that line would intersect the label box, then // trim the line to leave a gap for it, and return zero. If not, then extend // the line to almost meet the box, and return either positive or negative, // depending whether that extension was from A or from B. //----------------------------------------------------------------------------- int Constraint::DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b) { Vector gu = SS.GW.projUp.WithMagnitude(1), gr = SS.GW.projRight.WithMagnitude(1); double pixels = 1.0 / SS.GW.scale; char *s = Label(); double swidth = ssglStrWidth(s, DEFAULT_TEXT_HEIGHT) + 4*pixels, sheight = ssglStrHeight(DEFAULT_TEXT_HEIGHT) + 8*pixels; struct { Vector n; double d; } planes[4]; // reference pos is the center of box occupied by text; build a rectangle // around that, aligned to axes gr and gu, from four planes will all four // normals pointing inward planes[0].n = gu.ScaledBy(-1); planes[0].d = -(gu.Dot(ref) + sheight/2); planes[1].n = gu; planes[1].d = gu.Dot(ref) - sheight/2; planes[2].n = gr; planes[2].d = gr.Dot(ref) - swidth/2; planes[3].n = gr.ScaledBy(-1); planes[3].d = -(gr.Dot(ref) + swidth/2); double tmin = VERY_POSITIVE, tmax = VERY_NEGATIVE; Vector dl = b.Minus(a); for(int i = 0; i < 4; i++) { bool parallel; Vector p = Vector::AtIntersectionOfPlaneAndLine( planes[i].n, planes[i].d, a, b, ¶llel); if(parallel) continue; int j; for(j = 0; j < 4; j++) { double d = (planes[j].n).Dot(p) - planes[j].d; if(d < -LENGTH_EPS) break; } if(j < 4) continue; double t = (p.Minus(a)).DivPivoting(dl); tmin = min(t, tmin); tmax = max(t, tmax); } int within = 0; if(tmin > -0.01 && tmin < 1.01 && tmax > -0.01 && tmax < 1.01) { // Both in range; so there's pieces of the line on both sides of the // label box. LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin))); LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b); } else if(tmin > -0.01 && tmin < 1.01) { // Only one intersection in range; so the box is right on top of the // endpoint LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin))); } else if(tmax > -0.01 && tmax < 1.01) { // Likewise. LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b); } else { // The line does not intersect the label; so the line should get // extended to just barely meet the label. if(tmin < 0.01 && tmax < 0.01) { LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b); within = 1; } else if(tmin > 0.99 && tmax > 0.99) { LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin))); within = -1; } else { // This will happen if the entire line lies within the box. LineDrawOrGetDistance(a, b); } } // 0 means the label lies within the line, negative means it's outside // and closer to b, positive means outside and closer to a. return within; }
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(); }