Add a constraint to make line length equal arc length. That's quite
tricky; can't just use the dot product, since that blows up when you cross pi radians. A gear shift approach, use either sin or cos, same kind of thing as the 3d-parallel constraint. And report a NaN constraint as unconverged, of course. [git-p4: depot-paths = "//depot/solvespace/": change = 1890]
This commit is contained in:
parent
f9eedb4db7
commit
984956cbc7
|
@ -36,6 +36,7 @@ char *Constraint::DescriptionString(void) {
|
||||||
case PERPENDICULAR: s = "perpendicular"; break;
|
case PERPENDICULAR: s = "perpendicular"; break;
|
||||||
case EQUAL_RADIUS: s = "eq-radius"; break;
|
case EQUAL_RADIUS: s = "eq-radius"; break;
|
||||||
case EQUAL_ANGLE: s = "eq-angle"; break;
|
case EQUAL_ANGLE: s = "eq-angle"; break;
|
||||||
|
case EQUAL_LINE_ARC_LEN:s = "eq-line-len-arc-len"; break;
|
||||||
case COMMENT: s = "comment"; break;
|
case COMMENT: s = "comment"; break;
|
||||||
default: s = "???"; break;
|
default: s = "???"; break;
|
||||||
}
|
}
|
||||||
|
@ -239,6 +240,15 @@ void Constraint::MenuConstrain(int id) {
|
||||||
c.type = EQUAL_RADIUS;
|
c.type = EQUAL_RADIUS;
|
||||||
c.entityA = gs.entity[0];
|
c.entityA = gs.entity[0];
|
||||||
c.entityB = gs.entity[1];
|
c.entityB = gs.entity[1];
|
||||||
|
} else if(gs.arcs == 1 && gs.lineSegments == 1 && gs.n == 2) {
|
||||||
|
c.type = EQUAL_LINE_ARC_LEN;
|
||||||
|
if(SS.GetEntity(gs.entity[0])->type == Entity::ARC_OF_CIRCLE) {
|
||||||
|
c.entityA = gs.entity[1];
|
||||||
|
c.entityB = gs.entity[0];
|
||||||
|
} else {
|
||||||
|
c.entityA = gs.entity[0];
|
||||||
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Error("Bad selection for equal length / radius constraint. "
|
Error("Bad selection for equal length / radius constraint. "
|
||||||
"This constraint can apply to:\r\n\r\n"
|
"This constraint can apply to:\r\n\r\n"
|
||||||
|
@ -253,7 +263,9 @@ void Constraint::MenuConstrain(int id) {
|
||||||
"(equal angle between A,B and C,D)\r\n"
|
"(equal angle between A,B and C,D)\r\n"
|
||||||
" * three line segments or normals "
|
" * three line segments or normals "
|
||||||
"(equal angle between A,B and B,C)\r\n"
|
"(equal angle between A,B and B,C)\r\n"
|
||||||
" * two circles or arcs (equal radius)\r\n");
|
" * two circles or arcs (equal radius)\r\n"
|
||||||
|
" * a line segment and an arc "
|
||||||
|
"(line segment length equals arc length)\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(c.type == EQUAL_ANGLE) {
|
if(c.type == EQUAL_ANGLE) {
|
||||||
|
|
|
@ -249,6 +249,49 @@ void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EQUAL_LINE_ARC_LEN: {
|
||||||
|
Entity *line = SS.GetEntity(entityA),
|
||||||
|
*arc = SS.GetEntity(entityB);
|
||||||
|
|
||||||
|
// Get the line length
|
||||||
|
ExprVector l0 = SS.GetEntity(line->point[0])->PointGetExprs(),
|
||||||
|
l1 = SS.GetEntity(line->point[1])->PointGetExprs();
|
||||||
|
Expr *ll = (l1.Minus(l0)).Magnitude();
|
||||||
|
|
||||||
|
// And get the arc radius, and the cosine of its angle
|
||||||
|
Entity *ao = SS.GetEntity(arc->point[0]),
|
||||||
|
*as = SS.GetEntity(arc->point[1]),
|
||||||
|
*af = SS.GetEntity(arc->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos = (as->PointGetExprs()).Minus(ao->PointGetExprs()),
|
||||||
|
aof = (af->PointGetExprs()).Minus(ao->PointGetExprs());
|
||||||
|
Expr *r = aof.Magnitude();
|
||||||
|
|
||||||
|
ExprVector n = arc->Normal()->NormalExprsN();
|
||||||
|
ExprVector u = aos.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v = n.Cross(u);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta = aof.Dot(u)->Div(r);
|
||||||
|
Expr *sintheta = aof.Dot(v)->Div(r);
|
||||||
|
|
||||||
|
double thetas, thetaf, dtheta;
|
||||||
|
arc->ArcGetAngles(&thetas, &thetaf, &dtheta);
|
||||||
|
Expr *theta;
|
||||||
|
if(dtheta < 3*PI/4) {
|
||||||
|
theta = costheta->ACos();
|
||||||
|
} else if(dtheta < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta = Expr::From(PI)->Minus(sintheta->ASin());
|
||||||
|
} else {
|
||||||
|
theta = (Expr::From(2*PI))->Minus(costheta->ACos());
|
||||||
|
}
|
||||||
|
|
||||||
|
// And write the equation; r*theta = L
|
||||||
|
AddEq(l, (r->Times(theta))->Minus(ll), 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case POINTS_COINCIDENT: {
|
case POINTS_COINCIDENT: {
|
||||||
Entity *a = SS.GetEntity(ptA);
|
Entity *a = SS.GetEntity(ptA);
|
||||||
Entity *b = SS.GetEntity(ptB);
|
Entity *b = SS.GetEntity(ptB);
|
||||||
|
|
|
@ -104,6 +104,30 @@ void Constraint::DoEqualLenTicks(Vector a, Vector b, Vector gn) {
|
||||||
LineDrawOrGetDistance(m.Minus(n), m.Plus(n));
|
LineDrawOrGetDistance(m.Minus(n), m.Plus(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Constraint::DoEqualRadiusTicks(hEntity he) {
|
||||||
|
Entity *circ = SS.GetEntity(he);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db,
|
void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db,
|
||||||
Vector offset, Vector *ref)
|
Vector offset, Vector *ref)
|
||||||
{
|
{
|
||||||
|
@ -505,30 +529,22 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
|
||||||
|
|
||||||
case EQUAL_RADIUS: {
|
case EQUAL_RADIUS: {
|
||||||
for(int i = 0; i < 2; i++) {
|
for(int i = 0; i < 2; i++) {
|
||||||
Entity *circ = SS.GetEntity(i == 0 ? entityA : entityB);
|
DoEqualRadiusTicks(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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EQUAL_LINE_ARC_LEN: {
|
||||||
|
Entity *line = SS.GetEntity(entityA);
|
||||||
|
DoEqualLenTicks(
|
||||||
|
SS.GetEntity(line->point[0])->PointGetNum(),
|
||||||
|
SS.GetEntity(line->point[1])->PointGetNum(),
|
||||||
|
gn);
|
||||||
|
|
||||||
|
DoEqualRadiusTicks(entityB);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case LENGTH_RATIO:
|
case LENGTH_RATIO:
|
||||||
case EQUAL_LENGTH_LINES: {
|
case EQUAL_LENGTH_LINES: {
|
||||||
Vector a, b;
|
Vector a, b;
|
||||||
|
|
15
expr.cpp
15
expr.cpp
|
@ -243,6 +243,8 @@ int Expr::Children(void) {
|
||||||
case SQUARE:
|
case SQUARE:
|
||||||
case SIN:
|
case SIN:
|
||||||
case COS:
|
case COS:
|
||||||
|
case ASIN:
|
||||||
|
case ACOS:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
|
@ -312,6 +314,8 @@ double Expr::Eval(void) {
|
||||||
case SQUARE: { double r = a->Eval(); return r*r; }
|
case SQUARE: { double r = a->Eval(); return r*r; }
|
||||||
case SIN: return sin(a->Eval());
|
case SIN: return sin(a->Eval());
|
||||||
case COS: return cos(a->Eval());
|
case COS: return cos(a->Eval());
|
||||||
|
case ACOS: return acos(a->Eval());
|
||||||
|
case ASIN: return asin(a->Eval());
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
|
@ -349,6 +353,13 @@ Expr *Expr::PartialWrt(hParam p) {
|
||||||
case SIN: return (a->Cos())->Times(a->PartialWrt(p));
|
case SIN: return (a->Cos())->Times(a->PartialWrt(p));
|
||||||
case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
|
case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
|
||||||
|
|
||||||
|
case ASIN:
|
||||||
|
return (From(1)->Div((From(1)->Minus(a->Square()))->Sqrt()))
|
||||||
|
->Times(a->PartialWrt(p));
|
||||||
|
case ACOS:
|
||||||
|
return (From(-1)->Div((From(1)->Minus(a->Square()))->Sqrt()))
|
||||||
|
->Times(a->PartialWrt(p));
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -421,6 +432,8 @@ Expr *Expr::FoldConstants(void) {
|
||||||
case NEGATE:
|
case NEGATE:
|
||||||
case SIN:
|
case SIN:
|
||||||
case COS:
|
case COS:
|
||||||
|
case ASIN:
|
||||||
|
case ACOS:
|
||||||
if(n->a->op == CONSTANT) {
|
if(n->a->op == CONSTANT) {
|
||||||
double nv = n->Eval();
|
double nv = n->Eval();
|
||||||
n->op = CONSTANT;
|
n->op = CONSTANT;
|
||||||
|
@ -526,6 +539,8 @@ p:
|
||||||
case SQUARE: App("(square "); a->PrintW(); App(")"); break;
|
case SQUARE: App("(square "); a->PrintW(); App(")"); break;
|
||||||
case SIN: App("(sin "); a->PrintW(); App(")"); break;
|
case SIN: App("(sin "); a->PrintW(); App(")"); break;
|
||||||
case COS: App("(cos "); a->PrintW(); App(")"); break;
|
case COS: App("(cos "); a->PrintW(); App(")"); break;
|
||||||
|
case ASIN: App("(asin "); a->PrintW(); App(")"); break;
|
||||||
|
case ACOS: App("(acos "); a->PrintW(); App(")"); break;
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
|
|
4
expr.h
4
expr.h
|
@ -29,6 +29,8 @@ public:
|
||||||
static const int SQUARE = 106;
|
static const int SQUARE = 106;
|
||||||
static const int SIN = 107;
|
static const int SIN = 107;
|
||||||
static const int COS = 108;
|
static const int COS = 108;
|
||||||
|
static const int ASIN = 109;
|
||||||
|
static const int ACOS = 110;
|
||||||
|
|
||||||
// Special helpers for when we're parsing an expression from text.
|
// Special helpers for when we're parsing an expression from text.
|
||||||
// Initially, literals (like a constant number) appear in the same
|
// Initially, literals (like a constant number) appear in the same
|
||||||
|
@ -70,6 +72,8 @@ public:
|
||||||
inline Expr *Square(void) { return AnyOp(SQUARE, NULL); }
|
inline Expr *Square(void) { return AnyOp(SQUARE, NULL); }
|
||||||
inline Expr *Sin (void) { return AnyOp(SIN, NULL); }
|
inline Expr *Sin (void) { return AnyOp(SIN, NULL); }
|
||||||
inline Expr *Cos (void) { return AnyOp(COS, NULL); }
|
inline Expr *Cos (void) { return AnyOp(COS, NULL); }
|
||||||
|
inline Expr *ASin (void) { return AnyOp(ASIN, NULL); }
|
||||||
|
inline Expr *ACos (void) { return AnyOp(ACOS, NULL); }
|
||||||
|
|
||||||
Expr *PartialWrt(hParam p);
|
Expr *PartialWrt(hParam p);
|
||||||
double Eval(void);
|
double Eval(void);
|
||||||
|
|
2
sketch.h
2
sketch.h
|
@ -458,6 +458,7 @@ public:
|
||||||
static const int EQ_LEN_PT_LINE_D = 52;
|
static const int EQ_LEN_PT_LINE_D = 52;
|
||||||
static const int EQ_PT_LN_DISTANCES = 53;
|
static const int EQ_PT_LN_DISTANCES = 53;
|
||||||
static const int EQUAL_ANGLE = 54;
|
static const int EQUAL_ANGLE = 54;
|
||||||
|
static const int EQUAL_LINE_ARC_LEN = 55;
|
||||||
static const int SYMMETRIC = 60;
|
static const int SYMMETRIC = 60;
|
||||||
static const int SYMMETRIC_HORIZ = 61;
|
static const int SYMMETRIC_HORIZ = 61;
|
||||||
static const int SYMMETRIC_VERT = 62;
|
static const int SYMMETRIC_VERT = 62;
|
||||||
|
@ -527,6 +528,7 @@ public:
|
||||||
void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu);
|
void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu);
|
||||||
void DoProjectedPoint(Vector *p);
|
void DoProjectedPoint(Vector *p);
|
||||||
void DoEqualLenTicks(Vector a, Vector b, Vector gn);
|
void DoEqualLenTicks(Vector a, Vector b, Vector gn);
|
||||||
|
void DoEqualRadiusTicks(hEntity he);
|
||||||
|
|
||||||
double GetDistance(Point2d mp);
|
double GetDistance(Point2d mp);
|
||||||
Vector GetLabelPos(void);
|
Vector GetLabelPos(void);
|
||||||
|
|
|
@ -516,7 +516,7 @@ didnt_converge:
|
||||||
(g->solved.remove).Clear();
|
(g->solved.remove).Clear();
|
||||||
SS.constraint.ClearTags();
|
SS.constraint.ClearTags();
|
||||||
for(i = 0; i < eq.n; i++) {
|
for(i = 0; i < eq.n; i++) {
|
||||||
if(fabs(mat.B.num[i]) > CONVERGE_TOLERANCE) {
|
if(fabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) {
|
||||||
// This constraint is unsatisfied.
|
// This constraint is unsatisfied.
|
||||||
if(!mat.eq[i].isFromConstraint()) continue;
|
if(!mat.eq[i].isFromConstraint()) continue;
|
||||||
|
|
||||||
|
|
|
@ -343,6 +343,9 @@ void TextWindow::DescribeSelection(void) {
|
||||||
double r = e->CircleGetRadiusNum();
|
double r = e->CircleGetRadiusNum();
|
||||||
Printf(true, " diameter = %Fi%s", SS.MmToString(r*2));
|
Printf(true, " diameter = %Fi%s", SS.MmToString(r*2));
|
||||||
Printf(false, " radius = %Fi%s", SS.MmToString(r));
|
Printf(false, " radius = %Fi%s", SS.MmToString(r));
|
||||||
|
double thetas, thetaf, dtheta;
|
||||||
|
e->ArcGetAngles(&thetas, &thetaf, &dtheta);
|
||||||
|
Printf(false, " arc len = %Fi%s", SS.MmToString(dtheta*r));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Entity::CIRCLE: {
|
case Entity::CIRCLE: {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user