Add two more exotic constraints: equal point-line distances, and

point-line distance equal to line segment length. These are
available in both normal and projected versions, with fancy display
for all of these.

[git-p4: depot-paths = "//depot/solvespace/": change = 1793]
solver
Jonathan Westhues 2008-06-14 03:16:14 -08:00
parent d391c43bff
commit 4c6d350cee
5 changed files with 121 additions and 18 deletions

View File

@ -16,6 +16,8 @@ char *Constraint::DescriptionString(void) {
case PT_ON_LINE: s = "pt-on-line"; break;
case PT_ON_FACE: s = "pt-on-face"; break;
case EQUAL_LENGTH_LINES:s = "eq-length"; break;
case EQ_LEN_PT_LINE_D: s = "eq-length-and-pt-ln-dist"; break;
case EQ_PT_LN_DISTANCES:s = "eq-pt-line-distances"; break;
case LENGTH_RATIO: s = "length-ratio"; break;
case SYMMETRIC: s = "symmetric"; break;
case SYMMETRIC_HORIZ: s = "symmetric-h"; break;
@ -154,6 +156,25 @@ void Constraint::MenuConstrain(int id) {
c.type = EQUAL_LENGTH_LINES;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
} else if(gs.lineSegments == 2 && gs.points == 2 && gs.n == 4) {
c.type = EQ_PT_LN_DISTANCES;
c.entityA = gs.entity[0];
c.ptA = gs.point[0];
c.entityB = gs.entity[1];
c.ptB = gs.point[1];
} else if(gs.lineSegments == 1 && gs.points == 2 && gs.n == 3) {
// The same line segment for the distances, but different
// points.
c.type = EQ_PT_LN_DISTANCES;
c.entityA = gs.entity[0];
c.ptA = gs.point[0];
c.entityB = gs.entity[0];
c.ptB = gs.point[1];
} else if(gs.lineSegments == 2 && gs.points == 1 && gs.n == 3) {
c.type = EQ_LEN_PT_LINE_D;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
c.ptA = gs.point[0];
} else if(gs.circlesOrArcs == 2 && gs.n == 2) {
c.type = EQUAL_RADIUS;
c.entityA = gs.entity[0];
@ -552,6 +573,22 @@ void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
break;
}
// These work on distance squared, since the pt-line distances are
// signed, and we want the absolute value.
case EQ_LEN_PT_LINE_D: {
Entity *forLen = SS.GetEntity(entityA);
Expr *d1 = Distance(workplane, forLen->point[0], forLen->point[1]);
Expr *d2 = PointLineDistance(workplane, ptA, entityB);
AddEq(l, (d1->Square())->Minus(d2->Square()), 0);
break;
}
case EQ_PT_LN_DISTANCES: {
Expr *d1 = PointLineDistance(workplane, ptA, entityA);
Expr *d2 = PointLineDistance(workplane, ptB, entityB);
AddEq(l, (d1->Square())->Minus(d2->Square()), 0);
break;
}
case LENGTH_RATIO: {
Entity *a = SS.GetEntity(entityA);
Entity *b = SS.GetEntity(entityB);

View File

@ -95,6 +95,14 @@ void Constraint::DoProjectedPoint(Vector *r) {
*r = p;
}
void Constraint::DoEqualLenTicks(Vector a, Vector b, Vector gn) {
Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3));
Vector ab = a.Minus(b);
Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale);
LineDrawOrGetDistance(m.Minus(n), m.Plus(n));
}
void Constraint::DrawOrGetDistance(Vector *labelPos) {
if(!SS.GW.showConstraints) return;
Group *g = SS.GetGroup(group);
@ -172,19 +180,9 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
DoProjectedPoint(&pt);
}
Vector lAB = (lA.Minus(lB)).WithMagnitude(1);
Vector closest;
// lA, lB, and pt define a plane; the min distance is in
// that plane, so calculate its normal
Vector pn = (pt.Minus(lA)).Cross(lAB);
// The minimum distance line is in that plane, perpendicular
// to the line
Vector n = pn.Cross(lAB);
// Find the closest point on the line
Vector closest = pt.ClosestPointOnLine(lA, (lA.Minus(lB)));
// Calculate the actual distance
double d = (lAB.Cross(lA.Minus(pt))).Magnitude();
closest = pt.Plus(n.WithMagnitude(d));
LineDrawOrGetDistance(pt, closest);
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset);
DoLabel(ref, labelPos, gr, gu);
@ -192,7 +190,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
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.
lAB = (lA.Minus(lB));
Vector lAB = (lA.Minus(lB));
double t = (lA.Minus(closest)).DivPivoting(lAB);
Vector lA = SS.GetEntity(line->point[0])->PointGetNum();
@ -422,11 +420,13 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);
a = SS.GetEntity(e->point[0])->PointGetNum();
b = SS.GetEntity(e->point[1])->PointGetNum();
Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3));
Vector ab = a.Minus(b);
Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale);
LineDrawOrGetDistance(m.Minus(n), m.Plus(n));
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);
@ -435,6 +435,54 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
break;
}
case EQ_LEN_PT_LINE_D: {
Entity *forLen = SS.GetEntity(entityA);
Vector a = SS.GetEntity(forLen->point[0])->PointGetNum(),
b = SS.GetEntity(forLen->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
DoProjectedPoint(&a);
DoProjectedPoint(&b);
}
DoEqualLenTicks(a, b, gn);
Entity *ln = SS.GetEntity(entityB);
Vector la = SS.GetEntity(ln->point[0])->PointGetNum(),
lb = SS.GetEntity(ln->point[1])->PointGetNum();
Vector pt = SS.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 = SS.GetEntity(i == 0 ? entityA : entityB);
Vector la = SS.GetEntity(ln->point[0])->PointGetNum(),
lb = SS.GetEntity(ln->point[1])->PointGetNum();
Entity *pte = SS.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;
}
{
Vector n;
case SYMMETRIC:

1
dsc.h
View File

@ -56,6 +56,7 @@ public:
Vector RotatedAbout(Vector orig, Vector axis, double theta);
Vector RotatedAbout(Vector axis, double theta);
double DistanceToLine(Vector p0, Vector dp);
Vector ClosestPointOnLine(Vector p0, Vector dp);
double Magnitude(void);
Vector WithMagnitude(double s);
Vector ScaledBy(double s);

View File

@ -412,6 +412,8 @@ public:
static const int PT_ON_FACE = 43;
static const int EQUAL_LENGTH_LINES = 50;
static const int LENGTH_RATIO = 51;
static const int EQ_LEN_PT_LINE_D = 52;
static const int EQ_PT_LN_DISTANCES = 53;
static const int SYMMETRIC = 60;
static const int SYMMETRIC_HORIZ = 61;
static const int SYMMETRIC_VERT = 62;
@ -471,6 +473,7 @@ public:
char *Label(void);
void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu);
void DoProjectedPoint(Vector *p);
void DoEqualLenTicks(Vector a, Vector b, Vector gn);
double GetDistance(Point2d mp);
Vector GetLabelPos(void);

View File

@ -315,6 +315,20 @@ double Vector::DistanceToLine(Vector p0, Vector dp) {
return ((this->Minus(p0)).Cross(dp)).Magnitude() / m;
}
Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) {
dp = dp.WithMagnitude(1);
// this, p0, and (p0+dp) define a plane; the min distance is in
// that plane, so calculate its normal
Vector pn = (this->Minus(p0)).Cross(dp);
// The minimum distance line is in that plane, perpendicular
// to the line
Vector n = pn.Cross(dp);
// Calculate the actual distance
double d = (dp.Cross(p0.Minus(*this))).Magnitude();
return this->Plus(n.WithMagnitude(d));
}
double Vector::Magnitude(void) {
return sqrt(x*x + y*y + z*z);
}