From 328a946cc44041b07e1530e4e299f9a55a464a45 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Wed, 7 May 2008 00:19:37 -0800 Subject: [PATCH] Add a diameter constraint, and add a `distance' entity that I can remap when I copy circle entities, in order to make the radius numerical somehow (analogy with the POINT_ and NORMAL_XFRMD) thing. [git-p4: depot-paths = "//depot/solvespace/": change = 1710] --- constraint.cpp | 22 +++++++++++++--- drawconstraint.cpp | 62 +++++++++++++++++++++++++++++++++++++--------- entity.cpp | 29 +++++++++++++++++++++- file.cpp | 1 + glhelper.cpp | 13 ++++++---- graphicswin.cpp | 8 +++--- sketch.cpp | 22 ++++++++++++++-- sketch.h | 20 ++++++++++++--- solvespace.h | 2 ++ ui.h | 1 + 10 files changed, 150 insertions(+), 30 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index d923092..be60735 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -55,15 +55,22 @@ void Constraint::MenuConstrain(int id) { Entity *e = SS.GetEntity(gs.entity[0]); c.ptA = e->point[0]; c.ptB = e->point[1]; + } else if(gs.circlesOrArcs == 1 && gs.n == 1) { + c.type = DIAMETER; + c.entityA = gs.entity[0]; } else { Error("Bad selection for distance / diameter constraint."); return; } - Vector n = SS.GW.projRight.Cross(SS.GW.projUp); - Vector a = SS.GetEntity(c.ptA)->PointGetNum(); - Vector b = SS.GetEntity(c.ptB)->PointGetNum(); + if(c.type == PT_PT_DISTANCE) { + Vector n = SS.GW.projRight.Cross(SS.GW.projUp); + Vector a = SS.GetEntity(c.ptA)->PointGetNum(); + Vector b = SS.GetEntity(c.ptB)->PointGetNum(); + c.disp.offset = n.Cross(a.Minus(b)).WithMagnitude(50); + } else { + c.disp.offset = Vector::MakeFrom(0, 0, 0); + } - c.disp.offset = n.Cross(a.Minus(b)).WithMagnitude(50); c.exprA = Expr::FromString("0")->DeepCopyKeep(); c.ModifyToSatisfy(); AddConstraint(&c); @@ -330,6 +337,13 @@ void Constraint::Generate(IdList *l) { break; } + case DIAMETER: { + Entity *circle = SS.GetEntity(entityA); + Expr *r = (SS.GetEntity(circle->distance))->DistanceGetExpr(); + AddEq(l, (r->Times(Expr::FromConstant(2)))->Minus(exprA), 0); + break; + } + case POINTS_COINCIDENT: { Entity *a = SS.GetEntity(ptA); Entity *b = SS.GetEntity(ptB); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index cb0bc1f..a105d8d 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -3,6 +3,7 @@ bool Constraint::HasLabel(void) { switch(type) { case PT_PT_DISTANCE: + case DIAMETER: return true; default: @@ -25,6 +26,35 @@ void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { } } +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; +} + +void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { + char *s = exprA->Print(); + 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(glxStrWidth(s)/2)).Minus( + gu.WithMagnitude(glxStrHeight()/2)); + } + + if(dogd.drawing) { + glPushMatrix(); + glxTranslatev(ref); + glxOntoWorkplane(gr, gu); + glxWriteTextRefCenter(s); + glPopMatrix(); + } else { + Point2d o = SS.GW.ProjectPoint(ref); + dogd.dmin = min(dogd.dmin, o.DistanceTo(dogd.mp) - 10); + } +} + void Constraint::DrawOrGetDistance(Vector *labelPos) { if(!SS.GW.showConstraints) return; Group *g = SS.GetGroup(group); @@ -47,7 +77,6 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector bp = SS.GetEntity(ptB)->PointGetNum(); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); - if(labelPos) *labelPos = ref; Vector ab = ap.Minus(bp); Vector ar = ap.Minus(ref); @@ -59,17 +88,26 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { LineDrawOrGetDistance(ap, ap.Plus(out)); LineDrawOrGetDistance(bp, bp.Plus(out)); - if(dogd.drawing) { - glPushMatrix(); - glxTranslatev(ref); - glxOntoWorkplane(gr, gu); - glxWriteText(exprA->Print()); - glPopMatrix(); - } else { - Point2d o = SS.GW.ProjectPoint(ref); - dogd.dmin = min(dogd.dmin, o.DistanceTo(dogd.mp) - 10); - } + DoLabel(ref, labelPos, gr, gu); + break; + } + case DIAMETER: { + Entity *circle = SS.GetEntity(entityA); + Vector center = SS.GetEntity(circle->point[0])->PointGetNum(); + double r = SS.GetEntity(circle->distance)->DistanceGetNum(); + Vector ref = center.Plus(disp.offset); + + double theta = atan2(disp.offset.Dot(gu), disp.offset.Dot(gr)); + double adj = EllipticalInterpolation( + glxStrWidth(exprA->Print())/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; } @@ -106,7 +144,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { case PT_ON_LINE: case PT_IN_PLANE: { - double s = 7; + double s = 7/SS.GW.scale; Vector p = SS.GetEntity(ptA)->PointGetNum(); Vector r = gr.WithMagnitude(s); Vector d = gu.WithMagnitude(s); diff --git a/entity.cpp b/entity.cpp index dfb5adc..042a53c 100644 --- a/entity.cpp +++ b/entity.cpp @@ -31,6 +31,28 @@ void Entity::WorkplaneGetPlaneExprs(ExprVector *n, Expr **dn) { } } +double Entity::DistanceGetNum(void) { + if(type == DISTANCE) { + return SS.GetParam(param[0])->val; + } else if(type == DISTANCE_XFRMD) { + return numDistance; + } else oops(); +} +Expr *Entity::DistanceGetExpr(void) { + if(type == DISTANCE) { + return Expr::FromParam(param[0]); + } else if(type == DISTANCE_XFRMD) { + return Expr::FromConstant(numDistance); + } else oops(); +} +void Entity::DistanceForceTo(double v) { + if(type == DISTANCE) { + (SS.GetParam(param[0]))->val = v; + } else if(type == DISTANCE_XFRMD) { + // do nothing, it's locked + } else oops(); +} + Entity *Entity::Normal(void) { return SS.GetEntity(normal); } @@ -404,6 +426,11 @@ void Entity::DrawOrGetDistance(int order) { break; } + case DISTANCE: + case DISTANCE_XFRMD: + // These are used only as data structures, nothing to display. + break; + case WORKPLANE: { if(order >= 0 && order != 0) break; if(!SS.GW.showWorkplanes) break; @@ -477,7 +504,7 @@ void Entity::DrawOrGetDistance(int order) { if(order >= 0 && order != 1) break; Quaternion q = SS.GetEntity(normal)->NormalGetNum(); - double r = SS.GetParam(param[0])->val; + double r = SS.GetEntity(distance)->DistanceGetNum(); Vector center = SS.GetEntity(point[0])->PointGetNum(); Vector u = q.RotationU(), v = q.RotationV(); diff --git a/file.cpp b/file.cpp index e3a3884..25c38c8 100644 --- a/file.cpp +++ b/file.cpp @@ -77,6 +77,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'e', "Entity.point[2].v", 'x', &(SS.sv.e.point[2].v) }, { 'e', "Entity.point[3].v", 'x', &(SS.sv.e.point[3].v) }, { 'e', "Entity.normal.v", 'x', &(SS.sv.e.normal.v) }, + { 'e', "Entity.distance.v", 'x', &(SS.sv.e.distance.v) }, { 'e', "Entity.workplane.v", 'x', &(SS.sv.e.workplane.v) }, { 'e', "Entity.numPoint.x", 'f', &(SS.sv.e.numPoint.x) }, { 'e', "Entity.numPoint.y", 'f', &(SS.sv.e.numPoint.y) }, diff --git a/glhelper.cpp b/glhelper.cpp index a3a3564..5796750 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -6,7 +6,7 @@ static bool ColorLocked; #define FONT_SCALE (0.5) -static int StrWidth(char *str) { +double glxStrWidth(char *str) { int w = 0; for(; *str; str++) { int c = *str; @@ -15,14 +15,17 @@ static int StrWidth(char *str) { w += Font[c].width; } - return w; + return w*FONT_SCALE/SS.GW.scale; +} +double glxStrHeight(void) { + // The characters have height ~21, as they appear in the table. + return 21.0*FONT_SCALE/SS.GW.scale; } void glxWriteTextRefCenter(char *str) { double scale = FONT_SCALE/SS.GW.scale; - // The characters have height ~21, as they appear in the table. - double fh = (21.0)*scale; - double fw = StrWidth(str)*scale; + double fh = glxStrHeight(); + double fw = glxStrWidth(str); glPushMatrix(); glTranslated(-fw/2, -fh/2, 0); // Undo the (+5, +5) offset that glxWriteText applies. diff --git a/graphicswin.cpp b/graphicswin.cpp index 939e347..7ab934a 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -513,7 +513,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, Entity *circle = SS.GetEntity(pending.circle); Vector center = SS.GetEntity(circle->point[0])->PointGetNum(); Point2d c2 = ProjectPoint(center); - SS.GetParam(circle->param[0])->val = c2.DistanceTo(mp)*scale; + double r = c2.DistanceTo(mp)*scale; + SS.GetEntity(circle->distance)->DistanceForceTo(r); break; } @@ -634,6 +635,7 @@ void GraphicsWindow::GroupSelection(void) { switch(e->type) { case Entity::WORKPLANE: (gs.workplanes)++; break; case Entity::LINE_SEGMENT: (gs.lineSegments)++; break; + case Entity::CIRCLE: (gs.circlesOrArcs)++; break; } } } @@ -726,7 +728,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case MNU_CIRCLE: hr = AddRequest(Request::CIRCLE); SS.GetEntity(hr.entity(1))->PointForceTo(v); - SS.GetEntity(hr.entity(16))->NormalForceTo( + SS.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::MakeFrom(SS.GW.projRight, SS.GW.projUp)); MAYBE_PLACE(hr.entity(1)); @@ -757,7 +759,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case MNU_WORKPLANE: hr = AddRequest(Request::WORKPLANE); SS.GetEntity(hr.entity(1))->PointForceTo(v); - SS.GetEntity(hr.entity(16))->NormalForceTo( + SS.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::MakeFrom(SS.GW.projRight, SS.GW.projUp)); MAYBE_PLACE(hr.entity(1)); diff --git a/sketch.cpp b/sketch.cpp index 9fd7083..fd84fdc 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -126,7 +126,7 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, case Entity::CIRCLE: en.point[0] = Remap(ep->point[0], a); en.normal = Remap(ep->normal, a); - en.param[0] = ep->param[0]; // XXX make numerical somehow later + en.distance = Remap(ep->distance, a); break; case Entity::POINT_IN_3D: @@ -159,6 +159,11 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, en.point[0] = Remap(ep->point[0], a); break; + case Entity::DISTANCE: + en.type = Entity::DISTANCE_XFRMD; + en.numDistance = ep->DistanceGetNum(); + break; + default: oops(); } @@ -302,6 +307,7 @@ void Request::Generate(IdList *entity, int params = 0; int et = 0; bool hasNormal = false; + bool hasDistance = false; int i; Entity e; @@ -328,6 +334,7 @@ void Request::Generate(IdList *entity, points = 1; params = 1; hasNormal = true; + hasDistance = true; break; case Request::CUBIC: @@ -373,7 +380,7 @@ void Request::Generate(IdList *entity, Entity n; memset(&n, 0, sizeof(n)); n.workplane = workplane; - n.h = h.entity(16); + n.h = h.entity(32); n.group = group; if(workplane.v == Entity::FREE_IN_3D.v) { n.type = Entity::NORMAL_IN_3D; @@ -393,6 +400,17 @@ void Request::Generate(IdList *entity, entity->Add(&n); e.normal = n.h; } + if(hasDistance) { + Entity d; + memset(&d, 0, sizeof(d)); + d.workplane = workplane; + d.h = h.entity(64); + d.group = group; + d.type = Entity::DISTANCE; + d.param[0] = AddParam(param, h.param(64)); + entity->Add(&d); + e.distance = d.h; + } // And generate any params not associated with the point that // we happen to need. for(i = 0; i < params; i++) { diff --git a/sketch.h b/sketch.h index 5622965..d799b27 100644 --- a/sketch.h +++ b/sketch.h @@ -168,6 +168,9 @@ public: static const int NORMAL_IN_PLANE = 3002; static const int NORMAL_XFRMD = 3010; + static const int DISTANCE = 4000; + static const int DISTANCE_XFRMD = 4001; + static const int WORKPLANE = 10000; static const int LINE_SEGMENT = 11000; static const int CUBIC = 12000; @@ -177,13 +180,17 @@ public: // When it comes time to draw an entity, we look here to get the // defining variables. - hParam param[4]; hEntity point[4]; hEntity normal; + hEntity distance; + // The only types that have their own params are points, normals, + // and directions. + hParam param[4]; - // Derived points are a symbolic offset from a constant base. + // Transformed points/normals/distances have their numerical value. Vector numPoint; Quaternion numNormal; + double numDistance; hGroup group; hEntity workplane; // or Entity::FREE_IN_3D @@ -197,6 +204,11 @@ public: bool HasDirection(void); ExprVector GetDirection(void); + // For distances + double DistanceGetNum(void); + Expr *DistanceGetExpr(void); + void DistanceForceTo(double v); + bool IsWorkplane(void); // The plane is points P such that P dot (xn, yn, zn) - d = 0 void WorkplaneGetPlaneExprs(ExprVector *n, Expr **d); @@ -301,9 +313,9 @@ public: static const int EQUAL_LENGTH_LINES = 50; static const int SYMMETRIC = 60; static const int AT_MIDPOINT = 70; - static const int HORIZONTAL = 80; static const int VERTICAL = 81; + static const int DIAMETER = 90; int tag; hConstraint h; @@ -339,6 +351,8 @@ public: } dogd; // state for drawing or getting distance (for hit testing) void LineDrawOrGetDistance(Vector a, Vector b); void DrawOrGetDistance(Vector *labelPos); + double EllipticalInterpolation(double rx, double ry, double theta); + void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu); double GetDistance(Point2d mp); Vector GetLabelPos(void); diff --git a/solvespace.h b/solvespace.h index 93eb2b8..a10963e 100644 --- a/solvespace.h +++ b/solvespace.h @@ -71,6 +71,8 @@ void glxFillPolygon(SPolygon *p); void glxMarkPolygonNormal(SPolygon *p); void glxWriteText(char *str); void glxWriteTextRefCenter(char *str); +double glxStrWidth(char *str); +double glxStrHeight(void); void glxTranslatev(Vector u); void glxOntoWorkplane(Vector u, Vector v); void glxLockColorTo(double r, double g, double b); diff --git a/ui.h b/ui.h index 63ceacb..26be832 100644 --- a/ui.h +++ b/ui.h @@ -222,6 +222,7 @@ public: int entities; int workplanes; int lineSegments; + int circlesOrArcs; int n; } gs; void GroupSelection(void);