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]
solver
Jonathan Westhues 2008-05-07 00:19:37 -08:00
parent c05659753a
commit 328a946cc4
10 changed files with 150 additions and 30 deletions

View File

@ -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<Equation,hEquation> *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);

View File

@ -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);

View File

@ -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();

View File

@ -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) },

View File

@ -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.

View File

@ -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));

View File

@ -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,hEntity> *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,hEntity> *entity,
points = 1;
params = 1;
hasNormal = true;
hasDistance = true;
break;
case Request::CUBIC:
@ -373,7 +380,7 @@ void Request::Generate(IdList<Entity,hEntity> *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,hEntity> *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++) {

View File

@ -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);

View File

@ -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);

1
ui.h
View File

@ -222,6 +222,7 @@ public:
int entities;
int workplanes;
int lineSegments;
int circlesOrArcs;
int n;
} gs;
void GroupSelection(void);