diff --git a/constraint.cpp b/constraint.cpp index 6fcb2d1..ace3c10 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -55,7 +55,9 @@ void Constraint::AddConstraint(Constraint *c, bool rememberForUndo) { SS.later.generateAll = true; } -void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA) +void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, + hEntity entityA, hEntity entityB, + bool other) { Constraint c; memset(&c, 0, sizeof(c)); @@ -65,11 +67,16 @@ void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA) c.ptA = ptA; c.ptB = ptB; c.entityA = entityA; + c.entityB = entityB; + c.other = other; AddConstraint(&c, false); } - +void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA){ + Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, false); +} void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { - Constrain(POINTS_COINCIDENT, ptA, ptB, Entity::NO_ENTITY); + Constrain(POINTS_COINCIDENT, ptA, ptB, + Entity::NO_ENTITY, Entity::NO_ENTITY, false); } void Constraint::MenuConstrain(int id) { diff --git a/graphicswin.cpp b/graphicswin.cpp index 3a0feb2..70d5a8d 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -514,12 +514,152 @@ void GraphicsWindow::MenuRequest(int id) { SS.GW.EnsureValidActives(); SS.later.showTW = true; break; - + + case MNU_ARC: { + SS.GW.GroupSelection(); + if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) { + if(!SS.GW.LockedInWorkplane()) { + Error("Must be sketching in workplane to create tangent " + "arc."); + break; + } + + // Find two line segments that join at the specified point, + // and blend them with a tangent arc. First, find the + // requests that generate the line segments. + Vector pshared = SS.GetEntity(SS.GW.gs.point[0])->PointGetNum(); + SS.GW.ClearSelection(); + + int i, c = 0; + Entity *line[2]; + Request *lineReq[2]; + bool point1[2]; + for(i = 0; i < SS.request.n; i++) { + Request *r = &(SS.request.elem[i]); + if(r->group.v != SS.GW.activeGroup.v) continue; + if(r->type != Request::LINE_SEGMENT) continue; + if(r->construction) continue; + + Entity *e = SS.GetEntity(r->h.entity(0)); + Vector p0 = SS.GetEntity(e->point[0])->PointGetNum(), + p1 = SS.GetEntity(e->point[1])->PointGetNum(); + + if(p0.Equals(pshared) || p1.Equals(pshared)) { + if(c < 2) { + line[c] = e; + lineReq[c] = r; + point1[c] = (p1.Equals(pshared)); + } + c++; + } + } + if(c != 2) { + Error("To create a tangent arc, select a point where " + "two non-construction line segments join."); + break; + } + + SS.UndoRemember(); + + Entity *wrkpl = SS.GetEntity(SS.GW.ActiveWorkplane()); + Vector wn = wrkpl->Normal()->NormalN(); + + hEntity hshared = (line[0])->point[point1[0] ? 1 : 0], + hother0 = (line[0])->point[point1[0] ? 0 : 1], + hother1 = (line[1])->point[point1[1] ? 0 : 1]; + + Vector pother0 = SS.GetEntity(hother0)->PointGetNum(), + pother1 = SS.GetEntity(hother1)->PointGetNum(); + + Vector v0shared = pshared.Minus(pother0), + v1shared = pshared.Minus(pother1); + + hEntity srcline0 = (line[0])->h, + srcline1 = (line[1])->h; + + (lineReq[0])->construction = true; + (lineReq[1])->construction = true; + + // And thereafter we mustn't touch the entity or req ptrs, + // because the new requests/entities we add might force a + // realloc. + memset(line, 0, sizeof(line)); + memset(lineReq, 0, sizeof(lineReq)); + + // The sign of vv determines whether shortest distance is + // clockwise or anti-clockwise. + Vector v = (wn.Cross(v0shared)).WithMagnitude(1); + double vv = v1shared.Dot(v); + + double dot = (v0shared.WithMagnitude(1)).Dot( + v1shared.WithMagnitude(1)); + double theta = acos(dot); + double r = 200/SS.GW.scale; + // Set the radius so that no more than one third of the + // line segment disappears. + r = min(r, v0shared.Magnitude()*tan(theta/2)/3); + r = min(r, v1shared.Magnitude()*tan(theta/2)/3); + double el = r/tan(theta/2); + + hRequest rln0 = SS.GW.AddRequest(Request::LINE_SEGMENT, false), + rln1 = SS.GW.AddRequest(Request::LINE_SEGMENT, false); + hRequest rarc = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false); + + Entity *ln0 = SS.GetEntity(rln0.entity(0)), + *ln1 = SS.GetEntity(rln1.entity(0)); + Entity *arc = SS.GetEntity(rarc.entity(0)); + + SS.GetEntity(ln0->point[0])->PointForceTo(pother0); + Constraint::ConstrainCoincident(ln0->point[0], hother0); + SS.GetEntity(ln1->point[0])->PointForceTo(pother1); + Constraint::ConstrainCoincident(ln1->point[0], hother1); + + Vector arc0 = pshared.Minus(v0shared.WithMagnitude(el)); + Vector arc1 = pshared.Minus(v1shared.WithMagnitude(el)); + + SS.GetEntity(ln0->point[1])->PointForceTo(arc0); + SS.GetEntity(ln1->point[1])->PointForceTo(arc1); + + Constraint::Constrain(Constraint::PT_ON_LINE, + ln0->point[1], Entity::NO_ENTITY, srcline0); + Constraint::Constrain(Constraint::PT_ON_LINE, + ln1->point[1], Entity::NO_ENTITY, srcline1); + + Vector center = arc0; + int a, b; + if(vv < 0) { + a = 1; b = 2; + center = center.Minus(v0shared.Cross(wn).WithMagnitude(r)); + } else { + a = 2; b = 1; + center = center.Plus(v0shared.Cross(wn).WithMagnitude(r)); + } + + SS.GetEntity(arc->point[0])->PointForceTo(center); + SS.GetEntity(arc->point[a])->PointForceTo(arc0); + SS.GetEntity(arc->point[b])->PointForceTo(arc1); + + Constraint::ConstrainCoincident(arc->point[a], ln0->point[1]); + Constraint::ConstrainCoincident(arc->point[b], ln1->point[1]); + + Constraint::Constrain(Constraint::ARC_LINE_TANGENT, + Entity::NO_ENTITY, Entity::NO_ENTITY, + arc->h, ln0->h, (a==2)); + Constraint::Constrain(Constraint::ARC_LINE_TANGENT, + Entity::NO_ENTITY, Entity::NO_ENTITY, + arc->h, ln1->h, (b==2)); + + SS.later.generateAll = true; + } else { + s = "click point on arc (draws anti-clockwise)"; + goto c; + } + break; + } case MNU_DATUM_POINT: s = "click to place datum point"; goto c; case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c; case MNU_CUBIC: s = "click first point of cubic segment"; goto c; case MNU_CIRCLE: s = "click center of circle"; goto c; - case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c; case MNU_WORKPLANE: s = "click origin of workplane"; goto c; case MNU_RECTANGLE: s = "click one corner of rectangle"; goto c; case MNU_TTF_TEXT: s = "click top left of text"; goto c; diff --git a/sketch.h b/sketch.h index 7da81d5..b049d1c 100644 --- a/sketch.h +++ b/sketch.h @@ -535,6 +535,9 @@ public: static void ConstrainCoincident(hEntity ptA, hEntity ptB); static void Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA); + static void Constrain(int type, hEntity ptA, hEntity ptB, + hEntity entityA, hEntity entityB, + bool other); }; class hEquation { diff --git a/wishlist.txt b/wishlist.txt index 2047b0b..48d9cf9 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,6 +1,5 @@ get rid of the oops() calls in the mesh codes -some kind of rounding / chamfer auto-generate circles and faces when lathing copy the section geometry to other end when sweeping cylindrical faces