Addition of ArcLength Ratio and ArcLength Difference constraints to Constraints list

pull/1063/head
Eric Chan 2021-06-25 13:25:53 -07:00 committed by phkahler
parent 4308dc136b
commit 37de364257
9 changed files with 323 additions and 10 deletions

View File

@ -113,6 +113,10 @@ typedef struct {
#define SLVS_C_WHERE_DRAGGED 100031
#define SLVS_C_CURVE_CURVE_TANGENT 100032
#define SLVS_C_LENGTH_DIFFERENCE 100033
#define SLVS_C_ARC_ARC_LEN_RATIO 100034
#define SLVS_C_ARC_LINE_LEN_RATIO 100035
#define SLVS_C_ARC_ARC_DIFFERENCE 100036
#define SLVS_C_ARC_LINE_DIFFERENCE 100037
typedef struct {
Slvs_hConstraint h;

View File

@ -22,7 +22,11 @@ std::string Constraint::DescriptionString() const {
case Type::EQ_LEN_PT_LINE_D: s = C_("constr-name", "eq-length-and-pt-ln-dist"); break;
case Type::EQ_PT_LN_DISTANCES: s = C_("constr-name", "eq-pt-line-distances"); break;
case Type::LENGTH_RATIO: s = C_("constr-name", "length-ratio"); break;
case Type::ARC_ARC_LEN_RATIO: s = C_("constr-name", "arc-arc-length-ratio"); break;
case Type::ARC_LINE_LEN_RATIO: s = C_("constr-name", "arc-line-length-ratio"); break;
case Type::LENGTH_DIFFERENCE: s = C_("constr-name", "length-difference"); break;
case Type::ARC_ARC_DIFFERENCE: s = C_("constr-name", "arc-arc-len-difference"); break;
case Type::ARC_LINE_DIFFERENCE: s = C_("constr-name", "arc-line-len-difference"); break;
case Type::SYMMETRIC: s = C_("constr-name", "symmetric"); break;
case Type::SYMMETRIC_HORIZ: s = C_("constr-name", "symmetric-h"); break;
case Type::SYMMETRIC_VERT: s = C_("constr-name", "symmetric-v"); break;
@ -384,10 +388,27 @@ void Constraint::MenuConstrain(Command id) {
c.type = Type::LENGTH_RATIO;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
}
else if(gs.arcs == 2 && gs.n == 2) {
c.type = Type::ARC_ARC_LEN_RATIO;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
}
else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
c.type = Type::ARC_LINE_LEN_RATIO;
if(SK.GetEntity(gs.entity[0])->type == Entity::Type::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 {
Error(_("Bad selection for length ratio constraint. This "
"constraint can apply to:\n\n"
" * two line segments\n"));
" * two line segments\n"
" * two arcs\n"
" * one arc and one line segment\n"));
return;
}
@ -401,10 +422,27 @@ void Constraint::MenuConstrain(Command id) {
c.type = Type::LENGTH_DIFFERENCE;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
}
else if(gs.arcs == 2 && gs.n == 2) {
c.type = Type::ARC_ARC_DIFFERENCE;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
}
else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
c.type = Type::ARC_LINE_DIFFERENCE;
if(SK.GetEntity(gs.entity[0])->type == Entity::Type::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 {
Error(_("Bad selection for length difference constraint. This "
"constraint can apply to:\n\n"
" * two line segments\n"));
" * two line segments\n"
" * two arcs\n"
" * one arc and one line segment\n"));
return;
}

View File

@ -18,7 +18,11 @@ bool ConstraintBase::HasLabel() const {
case Type::PROJ_PT_DISTANCE:
case Type::DIAMETER:
case Type::LENGTH_RATIO:
case Type::ARC_ARC_LEN_RATIO:
case Type::ARC_LINE_LEN_RATIO:
case Type::LENGTH_DIFFERENCE:
case Type::ARC_ARC_DIFFERENCE:
case Type::ARC_LINE_DIFFERENCE:
case Type::ANGLE:
case Type::COMMENT:
return true;
@ -39,7 +43,11 @@ bool ConstraintBase::IsProjectible() const {
case Type::EQ_PT_LN_DISTANCES:
case Type::EQUAL_ANGLE:
case Type::LENGTH_RATIO:
case Type::ARC_ARC_LEN_RATIO:
case Type::ARC_LINE_LEN_RATIO:
case Type::LENGTH_DIFFERENCE:
case Type::ARC_ARC_DIFFERENCE:
case Type::ARC_LINE_DIFFERENCE:
case Type::SYMMETRIC:
case Type::SYMMETRIC_HORIZ:
case Type::SYMMETRIC_VERT:
@ -335,6 +343,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
return;
}
case Type::ARC_ARC_LEN_RATIO: {
EntityBase *arc1 = SK.GetEntity(entityA),
*arc2 = SK.GetEntity(entityB);
// And get the arc1 radius, and the cosine of its angle
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
*as1 = SK.GetEntity(arc1->point[1]),
*af1 = SK.GetEntity(arc1->point[2]);
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
Expr *r1 = aof1.Magnitude();
ExprVector n1 = arc1->Normal()->NormalExprsN();
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
ExprVector v1 = n1.Cross(u1);
// so in our new csys, we start at (1, 0, 0)
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
double thetas1, thetaf1, dtheta1;
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
Expr *theta1;
if(dtheta1 < 3*PI/4) {
theta1 = costheta1->ACos();
} else if(dtheta1 < 5*PI/4) {
// As the angle crosses pi, cos theta1 is not invertible;
// so use the sine to stop blowing up
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
} else {
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
}
// And get the arc2 radius, and the cosine of its angle
EntityBase *ao2 = SK.GetEntity(arc2->point[0]),
*as2 = SK.GetEntity(arc2->point[1]),
*af2 = SK.GetEntity(arc2->point[2]);
ExprVector aos2 = (as2->PointGetExprs()).Minus(ao2->PointGetExprs()),
aof2 = (af2->PointGetExprs()).Minus(ao2->PointGetExprs());
Expr *r2 = aof2.Magnitude();
ExprVector n2 = arc2->Normal()->NormalExprsN();
ExprVector u2 = aos2.WithMagnitude(Expr::From(1.0));
ExprVector v2 = n2.Cross(u2);
// so in our new csys, we start at (1, 0, 0)
Expr *costheta2 = aof2.Dot(u2)->Div(r2);
Expr *sintheta2 = aof2.Dot(v2)->Div(r2);
double thetas2, thetaf2, dtheta2;
arc2->ArcGetAngles(&thetas2, &thetaf2, &dtheta2);
Expr *theta2;
if(dtheta2 < 3*PI/4) {
theta2 = costheta2->ACos();
} else if(dtheta2 < 5*PI/4) {
// As the angle crosses pi, cos theta2 is not invertible;
// so use the sine to stop blowing up
theta2 = Expr::From(PI)->Minus(sintheta2->ASin());
} else {
theta2 = (Expr::From(2*PI))->Minus(costheta2->ACos());
}
// And write the equation; (r1*theta1) / ( r2*theta2) = some ratio
AddEq(l, (r1->Times(theta1))->Div(r2->Times(theta2))->Minus(exA), 0);
return;
}
case Type::ARC_LINE_LEN_RATIO: {
EntityBase *line = SK.GetEntity(entityA),
*arc1 = SK.GetEntity(entityB);
Expr *ll = Distance(workplane, line->point[0], line->point[1]);
// And get the arc1 radius, and the cosine of its angle
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
*as1 = SK.GetEntity(arc1->point[1]),
*af1 = SK.GetEntity(arc1->point[2]);
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
Expr *r1 = aof1.Magnitude();
ExprVector n1 = arc1->Normal()->NormalExprsN();
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
ExprVector v1 = n1.Cross(u1);
// so in our new csys, we start at (1, 0, 0)
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
double thetas1, thetaf1, dtheta1;
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
Expr *theta1;
if(dtheta1 < 3*PI/4) {
theta1 = costheta1->ACos();
} else if(dtheta1 < 5*PI/4) {
// As the angle crosses pi, cos theta1 is not invertible;
// so use the sine to stop blowing up
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
} else {
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
}
// And write the equation; (r1*theta1) / ( length) = some ratio
AddEq(l, (r1->Times(theta1))->Div(ll)->Minus(exA), 0);
return;
}
case Type::LENGTH_DIFFERENCE: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
@ -344,6 +456,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
return;
}
case Type::ARC_ARC_DIFFERENCE: {
EntityBase *arc1 = SK.GetEntity(entityA),
*arc2 = SK.GetEntity(entityB);
// And get the arc1 radius, and the cosine of its angle
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
*as1 = SK.GetEntity(arc1->point[1]),
*af1 = SK.GetEntity(arc1->point[2]);
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
Expr *r1 = aof1.Magnitude();
ExprVector n1 = arc1->Normal()->NormalExprsN();
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
ExprVector v1 = n1.Cross(u1);
// so in our new csys, we start at (1, 0, 0)
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
double thetas1, thetaf1, dtheta1;
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
Expr *theta1;
if(dtheta1 < 3*PI/4) {
theta1 = costheta1->ACos();
} else if(dtheta1 < 5*PI/4) {
// As the angle crosses pi, cos theta1 is not invertible;
// so use the sine to stop blowing up
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
} else {
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
}
// And get the arc2 radius, and the cosine of its angle
EntityBase *ao2 = SK.GetEntity(arc2->point[0]),
*as2 = SK.GetEntity(arc2->point[1]),
*af2 = SK.GetEntity(arc2->point[2]);
ExprVector aos2 = (as2->PointGetExprs()).Minus(ao2->PointGetExprs()),
aof2 = (af2->PointGetExprs()).Minus(ao2->PointGetExprs());
Expr *r2 = aof2.Magnitude();
ExprVector n2 = arc2->Normal()->NormalExprsN();
ExprVector u2 = aos2.WithMagnitude(Expr::From(1.0));
ExprVector v2 = n2.Cross(u2);
// so in our new csys, we start at (1, 0, 0)
Expr *costheta2 = aof2.Dot(u2)->Div(r2);
Expr *sintheta2 = aof2.Dot(v2)->Div(r2);
double thetas2, thetaf2, dtheta2;
arc2->ArcGetAngles(&thetas2, &thetaf2, &dtheta2);
Expr *theta2;
if(dtheta2 < 3*PI/4) {
theta2 = costheta2->ACos();
} else if(dtheta2 < 5*PI/4) {
// As the angle crosses pi, cos theta2 is not invertible;
// so use the sine to stop blowing up
theta2 = Expr::From(PI)->Minus(sintheta2->ASin());
} else {
theta2 = (Expr::From(2*PI))->Minus(costheta2->ACos());
}
// And write the equation; (r1*theta1) - ( r2*theta2) = some difference
AddEq(l, (r1->Times(theta1))->Minus(r2->Times(theta2))->Minus(exA), 0);
return;
}
case Type::ARC_LINE_DIFFERENCE: {
EntityBase *line = SK.GetEntity(entityA),
*arc1 = SK.GetEntity(entityB);
Expr *ll = Distance(workplane, line->point[0], line->point[1]);
// And get the arc1 radius, and the cosine of its angle
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
*as1 = SK.GetEntity(arc1->point[1]),
*af1 = SK.GetEntity(arc1->point[2]);
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
Expr *r1 = aof1.Magnitude();
ExprVector n1 = arc1->Normal()->NormalExprsN();
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
ExprVector v1 = n1.Cross(u1);
// so in our new csys, we start at (1, 0, 0)
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
double thetas1, thetaf1, dtheta1;
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
Expr *theta1;
if(dtheta1 < 3*PI/4) {
theta1 = costheta1->ACos();
} else if(dtheta1 < 5*PI/4) {
// As the angle crosses pi, cos theta1 is not invertible;
// so use the sine to stop blowing up
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
} else {
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
}
// And write the equation; (r1*theta1) - ( length) = some difference
AddEq(l, (r1->Times(theta1))->Minus(ll)->Minus(exA), 0);
return;
}
case Type::DIAMETER: {
EntityBase *circle = SK.GetEntity(entityA);
Expr *r = circle->CircleGetRadiusExpr();

View File

@ -12,7 +12,7 @@ std::string Constraint::Label() const {
std::string result;
if(type == Type::ANGLE) {
result = SS.DegreeToString(valA) + "°";
} else if(type == Type::LENGTH_RATIO) {
} else if(type == Type::LENGTH_RATIO || type == Type::ARC_ARC_LEN_RATIO || type == Type::ARC_LINE_LEN_RATIO) {
result = ssprintf("%.3f:1", valA);
} else if(type == Type::COMMENT) {
result = comment;
@ -1000,6 +1000,42 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
}
return;
}
case Type::ARC_ARC_LEN_RATIO:
case Type::ARC_ARC_DIFFERENCE: {
Entity *circle = SK.GetEntity(entityA);
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
Vector n = q.RotationN().WithMagnitude(1);
double r = circle->CircleGetRadiusNum();
Vector ref2;
DoEqualRadiusTicks(canvas, hcs, entityA, &ref2);
DoEqualRadiusTicks(canvas, hcs, entityB, &ref2);
Vector ref = center.Plus(disp.offset);
// Force the label into the same plane as the circle.
ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center)));
if(refs) refs->push_back(ref);
Vector topLeft;
DoLabel(canvas, hcs, ref, &topLeft, gr, gu);
if(labelPos) *labelPos = topLeft;
return;
}
case Type::ARC_LINE_LEN_RATIO:
case Type::ARC_LINE_DIFFERENCE: {
Vector a, b = Vector::From(0, 0, 0);
Vector ref;
Entity *e = SK.GetEntity(entityA);
a = SK.GetEntity(e->point[0])->PointGetNum();
b = SK.GetEntity(e->point[1])->PointGetNum();
DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
if(refs) refs->push_back(ref);
DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
if(refs) refs->push_back(ref);
ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset);
DoLabel(canvas, hcs, ref, labelPos, gr, gu);
return;
}
case Type::EQ_LEN_PT_LINE_D: {
Entity *forLen = SK.GetEntity(entityA);
@ -1243,7 +1279,11 @@ bool Constraint::HasLabel() const {
case Type::PT_FACE_DISTANCE:
case Type::PROJ_PT_DISTANCE:
case Type::LENGTH_RATIO:
case Type::ARC_ARC_LEN_RATIO:
case Type::ARC_LINE_LEN_RATIO:
case Type::LENGTH_DIFFERENCE:
case Type::ARC_ARC_DIFFERENCE:
case Type::ARC_LINE_DIFFERENCE:
case Type::DIAMETER:
case Type::ANGLE:
return true;

View File

@ -153,8 +153,8 @@ const MenuEntry Menu[] = {
{ 1, NULL, Command::NONE, 0, KN, NULL },
{ 1, N_("&On Point / Curve / Plane"), Command::ON_ENTITY, 'o', KN, mCon },
{ 1, N_("E&qual Length / Radius / Angle"), Command::EQUAL, 'q', KN, mCon },
{ 1, N_("Length Ra&tio"), Command::RATIO, 'z', KN, mCon },
{ 1, N_("Length Diff&erence"), Command::DIFFERENCE, 'j', KN, mCon },
{ 1, N_("Length / Arc Ra&tio"), Command::RATIO, 'z', KN, mCon },
{ 1, N_("Length / Arc Diff&erence"), Command::DIFFERENCE, 'j', KN, mCon },
{ 1, N_("At &Midpoint"), Command::AT_MIDPOINT, 'm', KN, mCon },
{ 1, N_("S&ymmetric"), Command::SYMMETRIC, 'y', KN, mCon },
{ 1, N_("Para&llel / Tangent"), Command::PARALLEL, 'l', KN, mCon },

View File

@ -131,11 +131,15 @@ case SLVS_C_PT_ON_LINE: t = Constraint::Type::PT_ON_LINE; break;
case SLVS_C_PT_ON_FACE: t = Constraint::Type::PT_ON_FACE; break;
case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::Type::EQUAL_LENGTH_LINES; break;
case SLVS_C_LENGTH_RATIO: t = Constraint::Type::LENGTH_RATIO; break;
case SLVS_C_ARC_ARC_LEN_RATIO: t = Constraint::Type::ARC_ARC_LEN_RATIO; break;
case SLVS_C_ARC_LINE_LEN_RATIO: t = Constraint::Type::ARC_LINE_LEN_RATIO; break;
case SLVS_C_EQ_LEN_PT_LINE_D: t = Constraint::Type::EQ_LEN_PT_LINE_D; break;
case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::Type::EQ_PT_LN_DISTANCES; break;
case SLVS_C_EQUAL_ANGLE: t = Constraint::Type::EQUAL_ANGLE; break;
case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::Type::EQUAL_LINE_ARC_LEN; break;
case SLVS_C_LENGTH_DIFFERENCE: t = Constraint::Type::LENGTH_DIFFERENCE; break;
case SLVS_C_ARC_ARC_DIFFERENCE: t = Constraint::Type::ARC_ARC_DIFFERENCE; break;
case SLVS_C_ARC_LINE_DIFFERENCE:t = Constraint::Type::ARC_LINE_DIFFERENCE; break;
case SLVS_C_SYMMETRIC: t = Constraint::Type::SYMMETRIC; break;
case SLVS_C_SYMMETRIC_HORIZ: t = Constraint::Type::SYMMETRIC_HORIZ; break;
case SLVS_C_SYMMETRIC_VERT: t = Constraint::Type::SYMMETRIC_VERT; break;

View File

@ -1373,7 +1373,7 @@ void GraphicsWindow::EditConstraint(hConstraint constraint) {
value /= 2;
// Try showing value with default number of digits after decimal first.
if(c->type == Constraint::Type::LENGTH_RATIO) {
if(c->type == Constraint::Type::LENGTH_RATIO || c->type == Constraint::Type::ARC_ARC_LEN_RATIO || c->type == Constraint::Type::ARC_LINE_LEN_RATIO) {
editValue = ssprintf("%.3f", value);
} else if(c->type == Constraint::Type::ANGLE) {
editValue = SS.DegreeToString(value);
@ -1434,7 +1434,9 @@ void GraphicsWindow::EditControlDone(const std::string &s) {
case Constraint::Type::PT_LINE_DISTANCE:
case Constraint::Type::PT_FACE_DISTANCE:
case Constraint::Type::PT_PLANE_DISTANCE:
case Constraint::Type::LENGTH_DIFFERENCE: {
case Constraint::Type::LENGTH_DIFFERENCE:
case Constraint::Type::ARC_ARC_DIFFERENCE:
case Constraint::Type::ARC_LINE_DIFFERENCE: {
// The sign is not displayed to the user, but this is a signed
// distance internally. To flip the sign, the user enters a
// negative distance.
@ -1448,6 +1450,8 @@ void GraphicsWindow::EditControlDone(const std::string &s) {
}
case Constraint::Type::ANGLE:
case Constraint::Type::LENGTH_RATIO:
case Constraint::Type::ARC_ARC_LEN_RATIO:
case Constraint::Type::ARC_LINE_LEN_RATIO:
// These don't get the units conversion for distance, and
// they're always positive
c->valA = fabs(e->Eval());

View File

@ -676,7 +676,10 @@ public:
CURVE_CURVE_TANGENT = 125,
EQUAL_RADIUS = 130,
WHERE_DRAGGED = 200,
ARC_ARC_LEN_RATIO = 210,
ARC_LINE_LEN_RATIO = 211,
ARC_ARC_DIFFERENCE = 212,
ARC_LINE_DIFFERENCE = 213,
COMMENT = 1000
};

View File

@ -764,7 +764,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
SS.TW.stepDim.isDistance =
(c->type != Constraint::Type::ANGLE) &&
(c->type != Constraint::Type::LENGTH_RATIO) &&
(c->type != Constraint::Type::LENGTH_DIFFERENCE);
(c->type != Constraint::Type::ARC_ARC_LEN_RATIO) &&
(c->type != Constraint::Type::ARC_LINE_LEN_RATIO) &&
(c->type != Constraint::Type::LENGTH_DIFFERENCE) &&
(c->type != Constraint::Type::ARC_ARC_DIFFERENCE) &&
(c->type != Constraint::Type::ARC_LINE_DIFFERENCE) ;
SS.TW.shown.constraint = c->h;
SS.TW.shown.screen = TextWindow::Screen::STEP_DIMENSION;