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]
This commit is contained in:
Jonathan Westhues 2008-05-12 02:01:44 -08:00
parent f4d2651031
commit 364938f332
10 changed files with 190 additions and 8 deletions

View File

@ -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<Equation,hEquation> *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);

View File

@ -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;

1
dsc.h
View File

@ -53,6 +53,7 @@ public:
Vector ProjectInto(hEntity wrkpl);
double DivPivoting(Vector delta);
Vector ClosestOrtho(void);
Point2d Project2d(Vector u, Vector v);
};
class Point2d {

View File

@ -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<Equation,hEquation> *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.
}

View File

@ -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();

View File

@ -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,hEntity> *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;

View File

@ -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;

View File

@ -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);

10
ui.h
View File

@ -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;

View File

@ -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)));