diff --git a/constraint.cpp b/constraint.cpp index 39d5ce9d..78f4d6ee 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -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 *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); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index fef86217..ef04ef88 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -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: diff --git a/dsc.h b/dsc.h index efce50ac..bb10b49d 100644 --- a/dsc.h +++ b/dsc.h @@ -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); diff --git a/sketch.h b/sketch.h index 90402d8b..9c078ea3 100644 --- a/sketch.h +++ b/sketch.h @@ -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); diff --git a/util.cpp b/util.cpp index ccd06262..cc52c758 100644 --- a/util.cpp +++ b/util.cpp @@ -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); }