-
Notifications
You must be signed in to change notification settings - Fork 544
Description
This might be the wrong place to post this, but there is no discussion section.
For visual based modeling SLVS_C_ANGLE makes a lot of sense, where the angle between lines entityA and entityB is equal to valA, and it doesn't matter in what direction, CW or CCW. So it has two solutions.
For a code based CAD it makes more sense if you can specify what direction. I took SLVS_C_ANGLE and created a SLVS_C_SIGNED_ANGLE constraint. It respects the sign of vaIA, so there is only one solution. For example valA=10 produces a different solution from valA=-10. I thought I'd share it here in case someone else wants to do the same. I've tested it and seems to work.
In constrainteq.cpp:
case Type::SIGNED_ANGLE: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
ExprVector ae = a->VectorGetExprs();
ExprVector be = b->VectorGetExprs();
if(other) ae = ae.ScaledBy(Expr::From(-1));
// Get cosine and sine of current oriented angle (in workplane)
Expr *cos_expr = DirectionCosine(workplane, ae, be);
Expr *sin_expr = DirectionSine(workplane, ae, be);
// Target angle in radians (signed)
Expr *target_rads = exA->Times(Expr::From(PI/180.0));
Expr *target_cos = target_rads->Cos();
Expr *target_sin = target_rads->Sin();
// Residuals: enforce both cos and sin match -> pins direction
Expr *res_cos = cos_expr->Minus(target_cos);
Expr *res_sin = sin_expr->Minus(target_sin);
// Gain adjustment near 0/180
double cos_eval = fabs(cos_expr->Eval());
double gain = (cos_eval > 0.99) ? 0.01 / (1.00001 - cos_eval) : 1.0;
Expr *mult = Expr::From(gain);
AddEq(l, res_cos->Times(mult), 0);
AddEq(l, res_sin, 1); // sin is already signed, no need for extra gain here
return;
}
Helper function:
Expr *ConstraintBase::DirectionSine(hEntity wrkpl,
ExprVector ae, ExprVector be)
{
if (wrkpl == EntityBase::FREE_IN_3D) {
return Expr::From(0.0);
} else {
// In workplane: project into u-v plane, compute signed 2D cross product
EntityBase *w = SK.GetEntity(wrkpl);
ExprVector u = w->Normal()->NormalExprsU();
ExprVector v = w->Normal()->NormalExprsV();
Expr *ua = u.Dot(ae);
Expr *va = v.Dot(ae);
Expr *ub = u.Dot(be);
Expr *vb = v.Dot(be);
Expr *cross2d = (va->Times(ub))->Minus(ua->Times(vb));
Expr *maga = (ua->Square()->Plus(va->Square()))->Sqrt();
Expr *magb = (ub->Square()->Plus(vb->Square()))->Sqrt();
Expr *denom = maga->Times(magb);
return cross2d->Div(denom);
}
}
Then you also need to define SIGNED_ANGLE and SLVS_C_SIGNED_ANGLE in the appropriate places.