From e11da119f0fd50a3cbd07241878a3b1ddfd5d573 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Wed, 7 May 2008 23:30:30 -0800 Subject: [PATCH] Add point-line distance, point-plane distance, and point-on-circle constraints. And generate the constraint equations for entities (e.g., that our unit quaternions have magnitude one). Numerical troubles there, but it sort of works. Also some stuff to draw projection lines with projected constraints, and to auto-insert more constraints as you draw. [git-p4: depot-paths = "//depot/solvespace/": change = 1711] --- constraint.cpp | 48 +++++++++++++++++++------ drawconstraint.cpp | 63 ++++++++++++++++++++++++++++++++ dsc.h | 3 ++ entity.cpp | 24 +++++++++++++ expr.cpp | 7 ++++ expr.h | 2 ++ file.cpp | 1 + graphicswin.cpp | 58 ++++++++++++++++++++++-------- sketch.cpp | 1 + sketch.h | 89 ++++++++++++++++++++++++++-------------------- solvespace.cpp | 7 ++++ system.cpp | 16 ++++++++- ui.h | 1 + util.cpp | 14 ++++++++ 14 files changed, 269 insertions(+), 65 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index be60735..4907cee 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -13,26 +13,21 @@ hConstraint Constraint::AddConstraint(Constraint *c) { return c->h; } -void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { +void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA) +{ Constraint c; memset(&c, 0, sizeof(c)); c.group = SS.GW.activeGroup; c.workplane = SS.GW.activeWorkplane; - c.type = POINTS_COINCIDENT; + c.type = type; c.ptA = ptA; c.ptB = ptB; + c.entityA = entityA; AddConstraint(&c); } -void Constraint::ConstrainHorizVert(bool horiz, hEntity ls) { - Constraint c; - memset(&c, 0, sizeof(c)); - c.group = SS.GW.activeGroup; - c.workplane = SS.GW.activeWorkplane; - if(c.workplane.v == Entity::FREE_IN_3D.v) oops(); - c.type = (horiz ? HORIZONTAL : VERTICAL); - c.entityA = ls; - AddConstraint(&c); +void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { + Constrain(POINTS_COINCIDENT, ptA, ptB, Entity::NO_ENTITY); } void Constraint::MenuConstrain(int id) { @@ -55,6 +50,14 @@ 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.workplanes == 1 && gs.points == 1 && gs.n == 2) { + c.type = PT_PLANE_DISTANCE; + c.ptA = gs.point[0]; + c.entityA = gs.entity[0]; + } else if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) { + c.type = PT_LINE_DISTANCE; + c.ptA = gs.point[0]; + c.entityA = gs.entity[0]; } else if(gs.circlesOrArcs == 1 && gs.n == 1) { c.type = DIAMETER; c.entityA = gs.entity[0]; @@ -90,6 +93,10 @@ void Constraint::MenuConstrain(int id) { c.type = PT_ON_LINE; c.ptA = gs.point[0]; c.entityA = gs.entity[0]; + } else if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.n == 2) { + c.type = PT_ON_CIRCLE; + c.ptA = gs.point[0]; + c.entityA = gs.entity[0]; } else { Error("Bad selection for on point / curve / plane constraint."); return; @@ -329,6 +336,17 @@ void Constraint::Generate(IdList *l) { AddEq(l, Distance(workplane, ptA, ptB)->Minus(exprA), 0); break; + case PT_LINE_DISTANCE: + AddEq(l, + PointLineDistance(workplane, ptA, entityA)->Minus(exprA), 0); + break; + + case PT_PLANE_DISTANCE: { + ExprVector pt = SS.GetEntity(ptA)->PointGetExprs(); + AddEq(l, (PointPlaneDistance(pt, entityA))->Minus(exprA), 0); + break; + } + case EQUAL_LENGTH_LINES: { Entity *a = SS.GetEntity(entityA); Entity *b = SS.GetEntity(entityB); @@ -390,6 +408,14 @@ void Constraint::Generate(IdList *l) { } break; + case PT_ON_CIRCLE: { + Entity *circle = SS.GetEntity(entityA); + hEntity center = circle->point[0]; + Expr *radius = SS.GetEntity(circle->distance)->DistanceGetExpr(); + AddEq(l, Distance(workplane, ptA, center)->Minus(radius), 0); + break; + } + case AT_MIDPOINT: if(workplane.v == Entity::FREE_IN_3D.v) { Entity *ln = SS.GetEntity(entityA); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index a105d8d..0b9686a 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -2,6 +2,8 @@ bool Constraint::HasLabel(void) { switch(type) { + case PT_LINE_DISTANCE: + case PT_PLANE_DISTANCE: case PT_PT_DISTANCE: case DIAMETER: return true; @@ -55,6 +57,15 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { } } +void Constraint::DoProjectedPoint(Vector *r) { + Vector p = r->ProjectInto(workplane); + glLineStipple(4, 0x5555); + glEnable(GL_LINE_STIPPLE); + LineDrawOrGetDistance(p, *r); + glDisable(GL_LINE_STIPPLE); + *r = p; +} + void Constraint::DrawOrGetDistance(Vector *labelPos) { if(!SS.GW.showConstraints) return; Group *g = SS.GetGroup(group); @@ -76,6 +87,11 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector ap = SS.GetEntity(ptA)->PointGetNum(); Vector bp = SS.GetEntity(ptB)->PointGetNum(); + if(workplane.v != Entity::FREE_IN_3D.v) { + DoProjectedPoint(&ap); + DoProjectedPoint(&bp); + } + Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); Vector ab = ap.Minus(bp); @@ -92,6 +108,52 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case PT_PLANE_DISTANCE: { + Vector pt = SS.GetEntity(ptA)->PointGetNum(); + Entity *plane = SS.GetEntity(entityA); + Vector n = plane->Normal()->NormalN(); + Vector p = plane->WorkplaneGetOffset(); + double d = (p.Minus(pt)).Dot(n); + + Vector 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); + break; + } + + case PT_LINE_DISTANCE: { + Vector pt = SS.GetEntity(ptA)->PointGetNum(); + Entity *line = SS.GetEntity(entityA); + Vector lA = SS.GetEntity(line->point[0])->PointGetNum(); + Vector lB = SS.GetEntity(line->point[1])->PointGetNum(); + + if(workplane.v != Entity::FREE_IN_3D.v) { + lA = lA.ProjectInto(workplane); + lB = lB.ProjectInto(workplane); + 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); + + // 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); + break; + } + case DIAMETER: { Entity *circle = SS.GetEntity(entityA); Vector center = SS.GetEntity(circle->point[0])->PointGetNum(); @@ -142,6 +204,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case PT_ON_CIRCLE: case PT_ON_LINE: case PT_IN_PLANE: { double s = 7/SS.GW.scale; diff --git a/dsc.h b/dsc.h index 2c2c6fd..1da3023 100644 --- a/dsc.h +++ b/dsc.h @@ -6,6 +6,8 @@ typedef unsigned long DWORD; typedef unsigned char BYTE; class Vector; +class Point2d; +class hEntity; class Quaternion { public: @@ -45,6 +47,7 @@ public: double Magnitude(void); Vector WithMagnitude(double s); Vector ScaledBy(double s); + Vector ProjectInto(hEntity wrkpl); }; class Point2d { diff --git a/entity.cpp b/entity.cpp index 042a53c..bcb36b4 100644 --- a/entity.cpp +++ b/entity.cpp @@ -5,6 +5,10 @@ char *Entity::DescriptionString(void) { return r->DescriptionString(); } +bool Entity::IsCircle(void) { + return (type == CIRCLE); +} + bool Entity::IsWorkplane(void) { return (type == WORKPLANE); } @@ -526,3 +530,23 @@ void Entity::DrawOrGetDistance(int order) { } } +void Entity::AddEq(IdList *l, Expr *expr, int index) { + Equation eq; + eq.e = expr; + eq.h = h.equation(index); + l->Add(&eq); +} + +void Entity::GenerateEquations(IdList *l) { + switch(type) { + case NORMAL_IN_3D: { + ExprQuaternion q = NormalGetExprs(); + AddEq(l, (q.Magnitude())->Minus(Expr::FromConstant(1)), 0); + break; + } + default:; + // Most entities do not generate equations. + } +} + + diff --git a/expr.cpp b/expr.cpp index e80f6c0..fd9b284 100644 --- a/expr.cpp +++ b/expr.cpp @@ -117,6 +117,13 @@ ExprVector ExprQuaternion::RotationN(void) { return n; } +Expr *ExprQuaternion::Magnitude(void) { + return ((w ->Square())->Plus( + (vx->Square())->Plus( + (vy->Square())->Plus( + (vz->Square())))))->Sqrt(); +} + Expr *Expr::FromParam(hParam p) { Expr *r = AllocExpr(); r->op = PARAM; diff --git a/expr.h b/expr.h index 059c817..b84e260 100644 --- a/expr.h +++ b/expr.h @@ -141,6 +141,8 @@ public: ExprVector RotationU(void); ExprVector RotationV(void); ExprVector RotationN(void); + + Expr *Magnitude(void); }; #endif diff --git a/file.cpp b/file.cpp index 25c38c8..c8a6e73 100644 --- a/file.cpp +++ b/file.cpp @@ -86,6 +86,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'e', "Entity.numNormal.vx", 'f', &(SS.sv.e.numNormal.vx) }, { 'e', "Entity.numNormal.vy", 'f', &(SS.sv.e.numNormal.vy) }, { 'e', "Entity.numNormal.vz", 'f', &(SS.sv.e.numNormal.vz) }, + { 'e', "Entity.numDistance", 'f', &(SS.sv.e.numDistance) }, { 'c', "Constraint.h.v", 'x', &(SS.sv.c.h.v) }, { 'c', "Constraint.type", 'd', &(SS.sv.c.type) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index 7ab934a..cdb66ba 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -658,11 +658,43 @@ hRequest GraphicsWindow::AddRequest(int type) { r.workplane = activeWorkplane; r.type = type; SS.request.AddAndAssignId(&r); - SS.GenerateAll(solving == SOLVE_ALWAYS); + + // We must regenerate the parameters, so that the code that tries to + // place this request's entities where the mouse is can do so. But + // we mustn't try to solve until reasonable values have been supplied + // for these new parameters, or else we'll get a numerical blowup. + SS.GenerateAll(false); return r.h; } +bool GraphicsWindow::ConstrainPointByHovered(hEntity pt) { + if(!hover.entity.v) return false; + + Entity *e = SS.GetEntity(hover.entity); + if(e->IsPoint()) { + Constraint::ConstrainCoincident(e->h, pt); + return true; + } + if(e->IsWorkplane()) { + Constraint::Constrain(Constraint::PT_IN_PLANE, + pt, Entity::NO_ENTITY, e->h); + return true; + } + if(e->IsCircle()) { + Constraint::Constrain(Constraint::PT_ON_CIRCLE, + pt, Entity::NO_ENTITY, e->h); + return true; + } + if(e->type == Entity::LINE_SEGMENT) { + Constraint::Constrain(Constraint::PT_ON_LINE, + pt, Entity::NO_ENTITY, e->h); + return true; + } + + return false; +} + void GraphicsWindow::MouseLeftDown(double mx, double my) { if(GraphicsEditControlIsVisible()) return; @@ -676,10 +708,6 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { v = v.Plus(projRight.ScaledBy(mx/scale)); v = v.Plus(projUp.ScaledBy(my/scale)); -#define MAYBE_PLACE(p) \ - if(hover.entity.v && SS.GetEntity((p))->IsPoint()) { \ - Constraint::ConstrainCoincident(hover.entity, (p)); \ - } hRequest hr; switch(pending.operation) { case MNU_DATUM_POINT: @@ -694,7 +722,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { case MNU_LINE_SEGMENT: hr = AddRequest(Request::LINE_SEGMENT); SS.GetEntity(hr.entity(1))->PointForceTo(v); - MAYBE_PLACE(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1)); ClearSelection(); hover.Clear(); @@ -717,7 +745,10 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(lns[i].entity(2))->PointForceTo(v); } for(i = 0; i < 4; i++) { - Constraint::ConstrainHorizVert((i % 2)==0, lns[i].entity(0)); + Constraint::Constrain( + (i % 2) ? Constraint::HORIZONTAL : Constraint::VERTICAL, + Entity::NO_ENTITY, Entity::NO_ENTITY, + lns[i].entity(0)); } pending.operation = DRAGGING_NEW_POINT; @@ -730,7 +761,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(hr.entity(1))->PointForceTo(v); SS.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::MakeFrom(SS.GW.projRight, SS.GW.projUp)); - MAYBE_PLACE(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1)); ClearSelection(); hover.Clear(); @@ -747,7 +778,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(hr.entity(2))->PointForceTo(v); SS.GetEntity(hr.entity(3))->PointForceTo(v); SS.GetEntity(hr.entity(4))->PointForceTo(v); - MAYBE_PLACE(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1)); ClearSelection(); hover.Clear(); @@ -761,7 +792,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetEntity(hr.entity(1))->PointForceTo(v); SS.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::MakeFrom(SS.GW.projRight, SS.GW.projUp)); - MAYBE_PLACE(hr.entity(1)); + ConstrainPointByHovered(hr.entity(1)); ClearSelection(); hover.Clear(); ClearPending(); @@ -774,15 +805,12 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { break; case DRAGGING_NEW_CUBIC_POINT: - if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { - Constraint::ConstrainCoincident(pending.point, hover.entity); - } + ConstrainPointByHovered(pending.point); ClearPending(); break; case DRAGGING_NEW_LINE_POINT: { - if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { - Constraint::ConstrainCoincident(pending.point, hover.entity); + if(ConstrainPointByHovered(pending.point)) { ClearPending(); break; } diff --git a/sketch.cpp b/sketch.cpp index fd84fdc..d2ddbcb 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -1,6 +1,7 @@ #include "solvespace.h" const hEntity Entity::FREE_IN_3D = { 0 }; +const hEntity Entity::NO_ENTITY = { 0 }; const hGroup Group::HGROUP_REFERENCES = { 1 }; const hRequest Request::HREQUEST_REFERENCE_XY = { 1 }; diff --git a/sketch.h b/sketch.h index d799b27..5acc9d8 100644 --- a/sketch.h +++ b/sketch.h @@ -41,6 +41,7 @@ public: inline bool isFromRequest(void); inline hRequest request(void); + inline hEquation equation(int i); }; class hParam { public: @@ -153,6 +154,7 @@ public: hEntity h; static const hEntity FREE_IN_3D; + static const hEntity NO_ENTITY; static const int POINT_IN_3D = 2000; static const int POINT_IN_2D = 2001; @@ -201,6 +203,8 @@ public: // times to apply the transformation. int timesApplied; + bool IsCircle(void); + bool HasDirection(void); ExprVector GetDirection(void); @@ -253,6 +257,9 @@ public: double GetDistance(Point2d mp); void GenerateEdges(SEdgeList *el); + void AddEq(IdList *l, Expr *expr, int index); + void GenerateEquations(IdList *l); + char *DescriptionString(void); }; @@ -270,31 +277,6 @@ public: }; -inline hEntity hGroup::entity(int i) - { hEntity r; r.v = 0x80000000 | (v << 16) | i; return r; } -inline hParam hGroup::param(int i) - { hParam r; r.v = 0x80000000 | (v << 16) | i; return r; } - -inline bool hRequest::IsFromReferences(void) { - if(v == Request::HREQUEST_REFERENCE_XY.v) return true; - if(v == Request::HREQUEST_REFERENCE_YZ.v) return true; - if(v == Request::HREQUEST_REFERENCE_ZX.v) return true; - return false; -} -inline hEntity hRequest::entity(int i) - { hEntity r; r.v = (v << 16) | i; return r; } -inline hParam hRequest::param(int i) - { hParam r; r.v = (v << 16) | i; return r; } - -inline bool hEntity::isFromRequest(void) - { if(v & 0x80000000) return false; else return true; } -inline hRequest hEntity::request(void) - { hRequest r; r.v = (v >> 16); return r; } - -inline hRequest hParam::request(void) - { hRequest r; r.v = (v >> 16); return r; } - - class hConstraint { public: DWORD v; @@ -304,18 +286,20 @@ public: class Constraint { public: - static const int USER_EQUATION = 10; - static const int POINTS_COINCIDENT = 20; - static const int PT_PT_DISTANCE = 30; - static const int PT_LINE_DISTANCE = 31; - static const int PT_IN_PLANE = 40; - static const int PT_ON_LINE = 41; - 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; + static const int USER_EQUATION = 10; + static const int POINTS_COINCIDENT = 20; + static const int PT_PT_DISTANCE = 30; + static const int PT_LINE_DISTANCE = 31; + static const int PT_PLANE_DISTANCE = 32; + static const int PT_IN_PLANE = 40; + static const int PT_ON_LINE = 41; + 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; + static const int PT_ON_CIRCLE = 100; int tag; hConstraint h; @@ -353,6 +337,7 @@ public: void DrawOrGetDistance(Vector *labelPos); double EllipticalInterpolation(double rx, double ry, double theta); void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu); + void DoProjectedPoint(Vector *p); double GetDistance(Point2d mp); Vector GetLabelPos(void); @@ -371,7 +356,7 @@ public: static ExprVector PointInThreeSpace(hEntity workplane, Expr *u, Expr *v); static void ConstrainCoincident(hEntity ptA, hEntity ptB); - static void ConstrainHorizVert(bool horiz, hEntity lineSegment); + static void Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA); }; class hEquation { @@ -387,6 +372,34 @@ public: Expr *e; }; + +inline hEntity hGroup::entity(int i) + { hEntity r; r.v = 0x80000000 | (v << 16) | i; return r; } +inline hParam hGroup::param(int i) + { hParam r; r.v = 0x80000000 | (v << 16) | i; return r; } + +inline bool hRequest::IsFromReferences(void) { + if(v == Request::HREQUEST_REFERENCE_XY.v) return true; + if(v == Request::HREQUEST_REFERENCE_YZ.v) return true; + if(v == Request::HREQUEST_REFERENCE_ZX.v) return true; + return false; +} +inline hEntity hRequest::entity(int i) + { hEntity r; r.v = (v << 16) | i; return r; } +inline hParam hRequest::param(int i) + { hParam r; r.v = (v << 16) | i; return r; } + +inline bool hEntity::isFromRequest(void) + { if(v & 0x80000000) return false; else return true; } +inline hRequest hEntity::request(void) + { hRequest r; r.v = (v >> 16); return r; } +inline hEquation hEntity::equation(int i) + { if(i != 0) oops(); hEquation r; r.v = v | 0x80000000; return r; } + +inline hRequest hParam::request(void) + { hRequest r; r.v = (v >> 16); return r; } + + inline hEquation hConstraint::equation(int i) { hEquation r; r.v = (v << 16) | i; return r; } diff --git a/solvespace.cpp b/solvespace.cpp index 926b9aa..fc51190 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -127,6 +127,13 @@ bool SolveSpace::SolveGroup(hGroup hg) { c->Generate(&(sys.eq)); } + // And the equations from entities + for(i = 0; i < entity.n; i++) { + Entity *e = &(entity.elem[i]); + if(e->group.v != hg.v) continue; + + e->GenerateEquations(&(sys.eq)); + } bool r = sys.Solve(); FreeAllTemporary(); diff --git a/system.cpp b/system.cpp index 317be18..e6fa9ea 100644 --- a/system.cpp +++ b/system.cpp @@ -70,6 +70,17 @@ bool System::IsDragged(hParam p) { } } } + if(SS.GW.pending.circle.v) { + Entity *circ = SS.entity.FindByIdNoOops(SS.GW.pending.circle); + if(circ) { + Entity *dist = SS.GetEntity(circ->distance); + switch(dist->type) { + case Entity::DISTANCE: + if(p.v == (dist->param[0].v)) return true; + break; + } + } + } return false; } @@ -358,7 +369,10 @@ bool System::Solve(void) { for(i = 0; i < eq.n; i++) { dbp(" %.3f = %s = 0", eq.elem[i].e->Eval(), eq.elem[i].e->Print()); } - dbp("%d parameters", param.n); */ + dbp("%d parameters", param.n); + for(i = 0; i < param.n; i++) { + dbp(" param %08x at %.3f", param.elem[i].h.v, param.elem[i].val); + } */ param.ClearTags(); eq.ClearTags(); diff --git a/ui.h b/ui.h index 26be832..b0ce66c 100644 --- a/ui.h +++ b/ui.h @@ -196,6 +196,7 @@ public: // The constraint that is being edited with the on-screen textbox. hConstraint constraintBeingEdited; + bool ConstrainPointByHovered(hEntity pt); hRequest AddRequest(int type); // The current selection. diff --git a/util.cpp b/util.cpp index bfe2987..abb6fd9 100644 --- a/util.cpp +++ b/util.cpp @@ -262,6 +262,19 @@ Vector Vector::WithMagnitude(double v) { } } +Vector Vector::ProjectInto(hEntity wrkpl) { + Entity *w = SS.GetEntity(wrkpl); + Vector u = w->Normal()->NormalU(); + Vector v = w->Normal()->NormalV(); + Vector p0 = w->WorkplaneGetOffset(); + + Vector f = this->Minus(p0); + double up = f.Dot(u); + double vp = f.Dot(v); + + return p0.Plus((u.ScaledBy(up)).Plus(v.ScaledBy(vp))); +} + Point2d Point2d::Plus(Point2d b) { Point2d r; r.x = x + b.x; @@ -322,3 +335,4 @@ double Point2d::DistanceToLine(Point2d p0, Point2d dp, bool segment) { } } +