diff --git a/constraint.cpp b/constraint.cpp index 22252fa..66b97e6 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -31,6 +31,7 @@ char *Constraint::DescriptionString(void) { case SAME_ORIENTATION: s = "same-orientation"; break; case ANGLE: s = "angle"; break; case PARALLEL: s = "parallel"; break; + case PERPENDICULAR: s = "perpendicular"; break; case EQUAL_RADIUS: s = "eq-radius"; break; case COMMENT: s = "comment"; break; default: s = "???"; break; @@ -425,6 +426,18 @@ void Constraint::MenuConstrain(int id) { AddConstraint(&c); break; + case GraphicsWindow::MNU_PERPENDICULAR: + if(gs.vectors == 2 && gs.n == 2) { + c.type = PERPENDICULAR; + c.entityA = gs.vector[0]; + c.entityB = gs.vector[1]; + } else { + Error("Bad selection for perpendicular constraint."); + return; + } + AddConstraint(&c); + break; + case GraphicsWindow::MNU_COMMENT: c.type = COMMENT; c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT"); @@ -942,6 +955,7 @@ void Constraint::GenerateReal(IdList *l) { break; } + case PERPENDICULAR: case ANGLE: { Entity *a = SS.GetEntity(entityA); Entity *b = SS.GetEntity(entityB); @@ -965,8 +979,16 @@ void Constraint::GenerateReal(IdList *l) { Expr *dot = (ua->Times(ub))->Plus(va->Times(vb)); c = dot->Div(maga->Times(magb)); } - Expr *rads = exA->Times(Expr::From(PI/180)); - AddEq(l, c->Minus(rads->Cos()), 0); + if(type == ANGLE) { + // The direction cosine is equal to the cosine of the + // specified angle + Expr *rads = exA->Times(Expr::From(PI/180)); + AddEq(l, c->Minus(rads->Cos()), 0); + } else { + // The dot product (and therefore the direction cosine) + // is equal to zero, perpendicular. + AddEq(l, c, 0); + } break; } diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 3d40e19..1803f5c 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -373,6 +373,43 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case PERPENDICULAR: { + Vector u, v; + Vector rn, ru; + if(workplane.v == Entity::FREE_IN_3D.v) { + rn = gn; + ru = gu; + } else { + Entity *normal = SS.GetEntity(workplane)->Normal(); + rn = normal->NormalN(); + ru = normal->NormalV(); // ru meaning r_up, not u/v + } + + for(int i = 0; i < 2; i++) { + Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); + + if(i == 0) { + // Calculate orientation of perpendicular sign only + // once, so that it's the same both times it's drawn + u = e->VectorGetNum(); + u = u.WithMagnitude(16/SS.GW.scale); + v = (rn.Cross(u)).WithMagnitude(16/SS.GW.scale); + if(fabs(u.Dot(ru)) < fabs(v.Dot(ru))) { + SWAP(Vector, u, v); + } + if(u.Dot(ru) < 0) u = u.ScaledBy(-1); + } + + Vector p = e->VectorGetRefPoint(); + Vector s = p.Plus(u).Plus(v); + LineDrawOrGetDistance(s, s.Plus(v)); + + Vector m = s.Plus(v.ScaledBy(0.5)); + LineDrawOrGetDistance(m, m.Plus(u)); + } + break; + } + case PARALLEL: { for(int i = 0; i < 2; i++) { Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); diff --git a/graphicswin.cpp b/graphicswin.cpp index 289acde..08bb54d 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -86,6 +86,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "At &Midpoint\tShift+M", MNU_AT_MIDPOINT, 'M'|S, mCon }, { 1, "S&ymmetric\tShift+Y", MNU_SYMMETRIC, 'Y'|S, mCon }, { 1, "Para&llel\tShift+L", MNU_PARALLEL, 'L'|S, mCon }, +{ 1, "&Perpendicular\tShift+P", MNU_PERPENDICULAR, 'P'|S, mCon }, { 1, "Same Orient&ation\tShift+A", MNU_ORIENTED_SAME, 'A'|S, mCon }, { 1, NULL, 0, NULL }, { 1, "Comment\tShift+C", MNU_COMMENT, 'C'|S, mCon }, diff --git a/sketch.h b/sketch.h index 22f453d..d550b8a 100644 --- a/sketch.h +++ b/sketch.h @@ -456,6 +456,7 @@ public: static const int SAME_ORIENTATION = 110; static const int ANGLE = 120; static const int PARALLEL = 121; + static const int PERPENDICULAR = 122; static const int EQUAL_RADIUS = 130; static const int COMMENT = 1000; diff --git a/ui.h b/ui.h index 68588f8..0ee602e 100644 --- a/ui.h +++ b/ui.h @@ -196,6 +196,7 @@ public: MNU_HORIZONTAL, MNU_VERTICAL, MNU_PARALLEL, + MNU_PERPENDICULAR, MNU_ORIENTED_SAME, MNU_COMMENT, } MenuId; diff --git a/wishlist.txt b/wishlist.txt index 1bcac2b..21508c2 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -8,7 +8,6 @@ auto-generate circles and faces when lathing copy the section geometry to other end when sweeping cylindrical faces draw explicit edges -perpendicular constraint long term