From 364938f33299da28ee792f6c9f7fbd47c1d37f43 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 12 May 2008 02:01:44 -0800 Subject: [PATCH] Add an arc entity. That's not allowed to exist in free space (since we need something to force the points into plane, and the workplane supplies that), but otherwise straightforward. And add diameter and equal radius constraints for the arc. [git-p4: depot-paths = "//depot/solvespace/": change = 1718] --- constraint.cpp | 14 ++++++++- drawconstraint.cpp | 28 ++++++++++++++++- dsc.h | 1 + entity.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++ graphicswin.cpp | 40 +++++++++++++++++++++++- sketch.cpp | 13 ++++++++ sketch.h | 6 ++++ system.cpp | 2 +- ui.h | 10 +++--- util.cpp | 7 +++++ 10 files changed, 190 insertions(+), 8 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 5f791b4..7d10794 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -110,6 +110,10 @@ void Constraint::MenuConstrain(int id) { c.type = EQUAL_LENGTH_LINES; c.entityA = gs.entity[0]; c.entityB = gs.entity[1]; + } else if(gs.circlesOrArcs == 2 && gs.n == 2) { + c.type = EQUAL_RADIUS; + c.entityA = gs.entity[0]; + c.entityB = gs.entity[1]; } else { Error("Bad selection for equal length / radius constraint."); return; @@ -406,11 +410,19 @@ void Constraint::Generate(IdList *l) { case DIAMETER: { Entity *circle = SS.GetEntity(entityA); - Expr *r = (SS.GetEntity(circle->distance))->DistanceGetExpr(); + Expr *r = circle->CircleGetRadiusExpr(); AddEq(l, (r->Times(Expr::FromConstant(2)))->Minus(exprA), 0); break; } + case EQUAL_RADIUS: { + Entity *c1 = SS.GetEntity(entityA); + Entity *c2 = SS.GetEntity(entityB); + AddEq(l, (c1->CircleGetRadiusExpr())->Minus( + c2->CircleGetRadiusExpr()), 0); + break; + } + case POINTS_COINCIDENT: { Entity *a = SS.GetEntity(ptA); Entity *b = SS.GetEntity(ptB); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index ccba8af..396049b 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -171,7 +171,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { case DIAMETER: { Entity *circle = SS.GetEntity(entityA); Vector center = SS.GetEntity(circle->point[0])->PointGetNum(); - double r = SS.GetEntity(circle->distance)->DistanceGetNum(); + double r = circle->CircleGetRadiusNum(); Vector ref = center.Plus(disp.offset); double theta = atan2(disp.offset.Dot(gu), disp.offset.Dot(gr)); @@ -261,6 +261,32 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case EQUAL_RADIUS: { + for(int i = 0; i < 2; i++) { + Entity *circ = SS.GetEntity(i == 0 ? entityA : entityB); + Vector center = SS.GetEntity(circ->point[0])->PointGetNum(); + double r = circ->CircleGetRadiusNum(); + Quaternion q = circ->Normal()->NormalGetNum(); + Vector u = q.RotationU(), v = q.RotationV(); + + double theta; + if(circ->type == Entity::CIRCLE) { + theta = PI/2; + } else if(circ->type == Entity::ARC_OF_CIRCLE) { + double thetaa, thetab, dtheta; + circ->ArcGetAngles(&thetaa, &thetab, &dtheta); + theta = thetaa + dtheta/2; + } else oops(); + + Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta))); + d = d.ScaledBy(r); + Vector p = center.Plus(d); + Vector tick = d.WithMagnitude(10/SS.GW.scale); + LineDrawOrGetDistance(p.Plus(tick), p.Minus(tick)); + } + break; + } + case LENGTH_RATIO: case EQUAL_LENGTH_LINES: { Vector a, b; diff --git a/dsc.h b/dsc.h index eabb57b..df30bbe 100644 --- a/dsc.h +++ b/dsc.h @@ -53,6 +53,7 @@ public: Vector ProjectInto(hEntity wrkpl); double DivPivoting(Vector delta); Vector ClosestOrtho(void); + Point2d Project2d(Vector u, Vector v); }; class Point2d { diff --git a/entity.cpp b/entity.cpp index 4306341..4ea3cf9 100644 --- a/entity.cpp +++ b/entity.cpp @@ -76,6 +76,45 @@ bool Entity::IsCircle(void) { return (type == CIRCLE); } +Expr *Entity::CircleGetRadiusExpr(void) { + if(type == CIRCLE) { + return SS.GetEntity(distance)->DistanceGetExpr(); + } else if(type == ARC_OF_CIRCLE) { + return Constraint::Distance(workplane, point[0], point[1]); + } else oops(); +} + +double Entity::CircleGetRadiusNum(void) { + if(type == CIRCLE) { + return SS.GetEntity(distance)->DistanceGetNum(); + } else if(type == ARC_OF_CIRCLE) { + Vector c = SS.GetEntity(point[0])->PointGetNum(); + Vector pa = SS.GetEntity(point[1])->PointGetNum(); + return (pa.Minus(c)).Magnitude(); + } else oops(); +} + +void Entity::ArcGetAngles(double *thetaa, double *thetab, double *dtheta) { + if(type != ARC_OF_CIRCLE) oops(); + + Quaternion q = Normal()->NormalGetNum(); + Vector u = q.RotationU(), v = q.RotationV(); + + Vector c = SS.GetEntity(point[0])->PointGetNum(); + Vector pa = SS.GetEntity(point[1])->PointGetNum(); + Vector pb = SS.GetEntity(point[2])->PointGetNum(); + + Point2d c2 = c.Project2d(u, v); + Point2d pa2 = (pa.Project2d(u, v)).Minus(c2); + Point2d pb2 = (pb.Project2d(u, v)).Minus(c2); + + *thetaa = atan2(pa2.y, pa2.x); + *thetab = atan2(pb2.y, pb2.x); + *dtheta = *thetab - *thetaa; + while(*dtheta < 0) *dtheta += 2*PI; + while(*dtheta > (2*PI)) *dtheta -= 2*PI; +} + bool Entity::IsWorkplane(void) { return (type == WORKPLANE); } @@ -685,6 +724,33 @@ void Entity::DrawOrGetDistance(int order) { break; } + case ARC_OF_CIRCLE: { + if(order >= 0 && order != 1) break; + Vector c = SS.GetEntity(point[0])->PointGetNum(); + Vector pa = SS.GetEntity(point[1])->PointGetNum(); + Vector pb = SS.GetEntity(point[2])->PointGetNum(); + Quaternion q = SS.GetEntity(normal)->NormalGetNum(); + Vector u = q.RotationU(), v = q.RotationV(); + + double ra = (pa.Minus(c)).Magnitude(); + double rb = (pb.Minus(c)).Magnitude(); + + double thetaa, thetab, dtheta; + ArcGetAngles(&thetaa, &thetab, &dtheta); + + int i, n = (int)((40*dtheta)/(2*PI)); + Vector prev = pa; + for(i = 1; i <= n; i++) { + double theta = thetaa + (dtheta*i)/n; + double r = ra + ((rb - ra)*i)/n; + Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta))); + Vector p = c.Plus(d.ScaledBy(r)); + LineDrawOrGetDistanceOrEdge(prev, p); + prev = p; + } + break; + } + case CIRCLE: { if(order >= 0 && order != 1) break; @@ -725,6 +791,17 @@ void Entity::GenerateEquations(IdList *l) { AddEq(l, (q.Magnitude())->Minus(Expr::FromConstant(1)), 0); break; } + case ARC_OF_CIRCLE: { + // If this is a copied entity, with its point already fixed + // with respect to each other, then we don't want to generate + // the distance constraint! + if(SS.GetEntity(point[0])->type == POINT_IN_2D) { + Expr *ra = Constraint::Distance(workplane, point[0], point[1]); + Expr *rb = Constraint::Distance(workplane, point[0], point[2]); + AddEq(l, ra->Minus(rb), 0); + } + break; + } default:; // Most entities do not generate equations. } diff --git a/graphicswin.cpp b/graphicswin.cpp index 4739f7a..6f6d59e 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -60,7 +60,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq }, { 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', mReq }, { 1, "&Circle\tC", MNU_CIRCLE, 'C', mReq }, -{ 1, "&Arc of a Circle\tA", 0, 'A', mReq }, +{ 1, "&Arc of a Circle\tA", MNU_ARC, 'A', mReq }, { 1, "&Cubic Segment\t3", MNU_CUBIC, '3', mReq }, { 1, NULL, 0, NULL }, { 1, "Sym&bolic Variable\tB", 0, 'B', mReq }, @@ -350,6 +350,7 @@ void GraphicsWindow::MenuRequest(int id) { 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 rectangular"; goto c; c: @@ -540,6 +541,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, orig.mouse = mp; } else { UpdateDraggedPoint(pending.point, x, y); + HitTestMakeSelection(mp); } break; } @@ -556,6 +558,18 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, SS.GetEntity(hr.entity(3))->PointForceTo(p2); break; } + case DRAGGING_NEW_ARC_POINT: { + UpdateDraggedPoint(pending.point, x, y); + HitTestMakeSelection(mp); + + hRequest hr = pending.point.request(); + Vector ona = SS.GetEntity(hr.entity(2))->PointGetNum(); + Vector onb = SS.GetEntity(hr.entity(3))->PointGetNum(); + Vector center = (ona.Plus(onb)).ScaledBy(0.5); + + SS.GetEntity(hr.entity(1))->PointForceTo(center); + break; + } case DRAGGING_NEW_RADIUS: case DRAGGING_RADIUS: { Entity *circle = SS.GetEntity(pending.circle); @@ -699,6 +713,8 @@ void GraphicsWindow::GroupSelection(void) { switch(e->type) { case Entity::WORKPLANE: (gs.workplanes)++; break; case Entity::LINE_SEGMENT: (gs.lineSegments)++; break; + + case Entity::ARC_OF_CIRCLE: case Entity::CIRCLE: (gs.circlesOrArcs)++; break; } } @@ -814,6 +830,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { Entity::NO_ENTITY, Entity::NO_ENTITY, lns[i].entity(0)); } + ConstrainPointByHovered(lns[2].entity(1)); pending.operation = DRAGGING_NEW_POINT; pending.point = lns[1].entity(2); @@ -836,6 +853,26 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { SS.GetParam(hr.param(0))->val = 0; break; + case MNU_ARC: + if(SS.GW.activeWorkplane.v == Entity::FREE_IN_3D.v) { + Error("Can't draw arc in 3d; select a workplane first."); + ClearPending(); + break; + } + hr = AddRequest(Request::ARC_OF_CIRCLE); + SS.GetEntity(hr.entity(1))->PointForceTo(v); + SS.GetEntity(hr.entity(2))->PointForceTo(v); + SS.GetEntity(hr.entity(3))->PointForceTo(v); + ConstrainPointByHovered(hr.entity(2)); + + ClearSelection(); hover.Clear(); + + ClearPending(); + pending.operation = DRAGGING_NEW_ARC_POINT; + pending.point = hr.entity(3); + pending.description = "click to place point"; + break; + case MNU_CUBIC: hr = AddRequest(Request::CUBIC); SS.GetEntity(hr.entity(1))->PointForceTo(v); @@ -868,6 +905,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { ClearPending(); break; + case DRAGGING_NEW_ARC_POINT: case DRAGGING_NEW_CUBIC_POINT: ConstrainPointByHovered(pending.point); ClearPending(); diff --git a/sketch.cpp b/sketch.cpp index 3003deb..b3aaf7a 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -273,6 +273,13 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, en.distance = Remap(ep->distance, a); break; + case Entity::ARC_OF_CIRCLE: + en.point[0] = Remap(ep->point[0], a); + en.point[1] = Remap(ep->point[1], a); + en.point[2] = Remap(ep->point[2], a); + en.normal = Remap(ep->normal, a); + break; + case Entity::POINT_N_COPY: case Entity::POINT_N_TRANS: case Entity::POINT_N_ROT_TRANS: @@ -511,6 +518,12 @@ void Request::Generate(IdList *entity, hasDistance = true; break; + case Request::ARC_OF_CIRCLE: + et = Entity::ARC_OF_CIRCLE; + points = 3; + hasNormal = true; + break; + case Request::CUBIC: et = Entity::CUBIC; points = 4; diff --git a/sketch.h b/sketch.h index f9c7a58..618c3b0 100644 --- a/sketch.h +++ b/sketch.h @@ -153,6 +153,7 @@ public: static const int LINE_SEGMENT = 200; static const int CUBIC = 300; static const int CIRCLE = 400; + static const int ARC_OF_CIRCLE = 500; int type; @@ -200,6 +201,7 @@ public: static const int LINE_SEGMENT = 11000; static const int CUBIC = 12000; static const int CIRCLE = 13000; + static const int ARC_OF_CIRCLE = 14000; int type; @@ -227,6 +229,9 @@ public: int timesApplied; bool IsCircle(void); + Expr *CircleGetRadiusExpr(void); + double CircleGetRadiusNum(void); + void ArcGetAngles(double *thetaa, double *thetab, double *dtheta); bool HasVector(void); ExprVector VectorGetExprs(void); @@ -333,6 +338,7 @@ public: static const int PT_ON_CIRCLE = 100; static const int SAME_ORIENTATION = 110; static const int PARALLEL = 120; + static const int EQUAL_RADIUS = 130; int tag; hConstraint h; diff --git a/system.cpp b/system.cpp index 419a6ee..9c5d0ef 100644 --- a/system.cpp +++ b/system.cpp @@ -344,7 +344,7 @@ bool System::NewtonSolve(int tag) { } bool System::Solve(void) { - int i, j; + int i, j = 0; /* dbp("%d equations", eq.n); diff --git a/ui.h b/ui.h index 1181f54..f4920d1 100644 --- a/ui.h +++ b/ui.h @@ -105,6 +105,7 @@ public: MNU_WORKPLANE, MNU_LINE_SEGMENT, MNU_CIRCLE, + MNU_ARC, MNU_RECTANGLE, MNU_CUBIC, MNU_CONSTRUCTION, @@ -184,10 +185,11 @@ public: static const int DRAGGING_NEW_POINT = 0x0f000001; static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003; - static const int DRAGGING_CONSTRAINT = 0x0f000004; - static const int DRAGGING_RADIUS = 0x0f000005; - static const int DRAGGING_NORMAL = 0x0f000006; - static const int DRAGGING_NEW_RADIUS = 0x0f000007; + static const int DRAGGING_NEW_ARC_POINT = 0x0f000004; + static const int DRAGGING_CONSTRAINT = 0x0f000005; + static const int DRAGGING_RADIUS = 0x0f000006; + static const int DRAGGING_NORMAL = 0x0f000007; + static const int DRAGGING_NEW_RADIUS = 0x0f000008; struct { int operation; diff --git a/util.cpp b/util.cpp index 99992d7..7b532f6 100644 --- a/util.cpp +++ b/util.cpp @@ -298,6 +298,13 @@ Vector Vector::ProjectInto(hEntity wrkpl) { return p0.Plus((u.ScaledBy(up)).Plus(v.ScaledBy(vp))); } +Point2d Vector::Project2d(Vector u, Vector v) { + Point2d p; + p.x = this->Dot(u); + p.y = this->Dot(v); + return p; +} + double Vector::DivPivoting(Vector delta) { double m = max(fabs(delta.x), max(fabs(delta.y), fabs(delta.z)));