Skip to content

SLVS_C_ANGLE with signed angles #1670

@BjornMoren

Description

@BjornMoren

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions