AIS_Manipulator Transform control

Hello all, i am running

m_manipulator->Transform(thePoint.x(), thePoint.y(), m_view)

to move and rotate an AIS_InteractiveObject.

I would like that when I hold the ctrl key that rotation or movements go in steps like +-15 degrees for rotation or  or 5mm steps for translation.

I was expecting to find some Signals for the manipulator but there are non (at least I can't find them). There are also no signals for AIS_InteractiveObject

How can I solve this problem?

Thank you!

gkv311 n's picture

It should be possible implementing such logic at application side, but I guess it might require some efforts.

Would be nice if AIS_Manipulator will support something like this on its own...

Dmitrii Pasukhin's picture

Hello, you mean integration ability to specify determined steps for interactive actions?

Best regards, Dmitrii.

 

Daniel Duesentrieb's picture

>>Would be nice if AIS_Manipulator will support something like this on its own...
100% agreed

>> Hello, you mean integration ability to specify determined steps for interactive actions?
yes.

like

public:
setTranslationSteps(double steps)
setRotationSteps(double steps)

slots:
onTranslationStep(double step)
onRotationStep(double step)

Guillaume CHAFFAROD's picture

I also need a method to perform this.

Anyone has found a solution to implement it ?

Daniel Duesentrieb's picture

I bring this up and hope there will be progress on this?

Guillaume CHAFFAROD's picture

I implemented this to achieve the goal, it's an extension of the base AIS_Manipulator.
Hope you'll enjoy it.

Custom_AIS_Manipulator.h :

#pragma once

#include "AIS_Manipulator.hxx"
#include "gp_Ax2.hxx"
#include "V3d_View.hxx"
#include "gp.hxx"

class Custom_AIS_Manipulator : public AIS_Manipulator
{
    DEFINE_STANDARD_RTTI_INLINE(APGM_AIS_Manipulator, AIS_Manipulator)

public:
    Custom_AIS_Manipulator();
    Custom_AIS_Manipulator(const gp_Ax2& thePosition);

    Standard_Real TranslationSteps();
    void SetTranslationSteps(Standard_Real value);

    Standard_Real RotationSteps();
    void SetRotationSteps(Standard_Real value);

    Standard_Real ScalingSteps();
    void SetScalingSteps(Standard_Real value);
    
    Standard_Boolean CustomProcessDragging(const Handle(AIS_InteractiveContext)&,
        const Handle(V3d_View)& theView,
        const Handle(SelectMgr_EntityOwner)&,
        const Graphic3d_Vec2i& theDragFrom,
        const Graphic3d_Vec2i& theDragTo,
        const AIS_DragAction theAction);

    gp_Trsf CustomTransform(const Standard_Integer thePX, const Standard_Integer thePY,
        const Handle(V3d_View)& theView);

    Standard_Boolean CustomObjectTransformation(const Standard_Integer theMaxX, const Standard_Integer theMaxY,
        const Handle(V3d_View)& theView, gp_Trsf& theTrsf);

private:
    Standard_Real myTranslationSteps = 0;
    Standard_Real myRotationSteps = 0;
    Standard_Real myScalingSteps = 0;
};

Custom_AIS_Manipulator.cpp

#include "Custom_AIS_Manipulator.h"
#include <Extrema_ExtElC.hxx>
#include <IntAna_IntConicQuad.hxx>
#include <gce_MakeDir.hxx>


//! Return Ax1 for specified direction of Ax2.
static gp_Ax1 getAx1FromAx2Dir(const gp_Ax2& theAx2,
    int theIndex)
{
    switch (theIndex)
    {
    case 0: return gp_Ax1(theAx2.Location(), theAx2.XDirection());
    case 1: return gp_Ax1(theAx2.Location(), theAx2.YDirection());
    case 2: return theAx2.Axis();
    }
    throw Standard_ProgramError("AIS_Manipulator - Invalid axis index");
}

Custom_AIS_Manipulator::Custom_AIS_Manipulator() : AIS_Manipulator() { }

Custom_AIS_Manipulator::Custom_AIS_Manipulator(const gp_Ax2& thePosition) : AIS_Manipulator(thePosition) { }

Standard_Real Custom_AIS_Manipulator::TranslationSteps()
{
	return this->myTranslationSteps;
}

void Custom_AIS_Manipulator::SetTranslationSteps(Standard_Real value)
{
    if (value < 0)
        value = 0;

	this->myTranslationSteps = value;
}

Standard_Real Custom_AIS_Manipulator::RotationSteps()
{
	return this->myRotationSteps;
}

void Custom_AIS_Manipulator::SetRotationSteps(Standard_Real value)
{
    if (value < 0)
        value = 0;
    else if (value > M_PI)
        value = M_PI;

	this->myRotationSteps = value;
}

Standard_Real Custom_AIS_Manipulator::ScalingSteps()
{
    return this->myScalingSteps;
}

void Custom_AIS_Manipulator::SetScalingSteps(Standard_Real value)
{
    if (value < 0)
        value = 0;

    this->myScalingSteps = value;
}

Standard_Boolean Custom_AIS_Manipulator::CustomProcessDragging(const Handle(AIS_InteractiveContext)&,
    const Handle(V3d_View)& theView,
    const Handle(SelectMgr_EntityOwner)&,
    const Graphic3d_Vec2i& theDragFrom,
    const Graphic3d_Vec2i& theDragTo,
    const AIS_DragAction theAction)
{
    switch (theAction)
    {
    case AIS_DragAction_Start:
    {
        if (HasActiveMode())
        {
            StartTransform(theDragFrom.x(), theDragFrom.y(), theView);
            return Standard_True;
        }
        break;
    }
    case AIS_DragAction_Confirmed:
    {
        return Standard_True;
    }
    case AIS_DragAction_Update:
    {
        // ADDED TO ENSURE TRANSFORMATIONS STEPS
        CustomTransform(theDragTo.x(), theDragTo.y(), theView);
        
        // REMOVED TO ENSURE TRANSFORMATIONS STEPS 
        //Transform(theDragTo.x(), theDragTo.y(), theView);
        
        return Standard_True;
    }
    case AIS_DragAction_Abort:
    {
        StopTransform(false);
        return Standard_True;
    }
    case AIS_DragAction_Stop:
        break;
    }
    return Standard_False;
}

gp_Trsf Custom_AIS_Manipulator::CustomTransform(const Standard_Integer thePX, const Standard_Integer thePY,
	const Handle(V3d_View)& theView)
{
    gp_Trsf aTrsf;
    if (CustomObjectTransformation(thePX, thePY, theView, aTrsf))
    {
        Transform(aTrsf);
    }

    return aTrsf;
}

Standard_Boolean Custom_AIS_Manipulator::CustomObjectTransformation(const Standard_Integer theMaxX, const Standard_Integer theMaxY,
    const Handle(V3d_View)& theView, gp_Trsf& theTrsf)
{
    // Initialize start reference data
    if (!myHasStartedTransformation)
    {
        myStartTrsfs.Clear();
        Handle(AIS_ManipulatorObjectSequence) anObjects = Objects();
        for (AIS_ManipulatorObjectSequence::Iterator anObjIter(*anObjects); anObjIter.More(); anObjIter.Next())
        {
            myStartTrsfs.Append(anObjIter.Value()->LocalTransformation());
        }
        myStartPosition = myPosition;
    }

    // Get 3d point with projection vector
    Graphic3d_Vec3d anInputPoint, aProj;
    theView->ConvertWithProj(theMaxX, theMaxY, anInputPoint.x(), anInputPoint.y(), anInputPoint.z(), aProj.x(), aProj.y(), aProj.z());
    const gp_Lin anInputLine(gp_Pnt(anInputPoint.x(), anInputPoint.y(), anInputPoint.z()), gp_Dir(aProj.x(), aProj.y(), aProj.z()));
    switch (myCurrentMode)
    {
        case AIS_MM_Translation:
        case AIS_MM_Scaling:
        {
            const gp_Lin aLine(myStartPosition.Location(), myAxes[myCurrentIndex].Position().Direction());
            Extrema_ExtElC anExtrema(anInputLine, aLine, Precision::Angular());
            if (!anExtrema.IsDone()
                || anExtrema.IsParallel()
                || anExtrema.NbExt() != 1)
            {
                // translation cannot be done co-directed with camera
                return Standard_False;
            }

            Extrema_POnCurv anExPnts[2];
            anExtrema.Points(1, anExPnts[0], anExPnts[1]);
            const gp_Pnt aNewPosition = anExPnts[1].Value();
            if (!myHasStartedTransformation)
            {
                myStartPick = aNewPosition;
                myHasStartedTransformation = Standard_True;
                return Standard_True;
            }
            else if (aNewPosition.Distance(myStartPick) < Precision::Confusion())
            {
                return Standard_False;
            }

            gp_Trsf aNewTrsf;
            if (myCurrentMode == AIS_MM_Translation)
            {
                // CHANGED TO ENSURE TRANSLATION STEPS
                gp_Vec translationVector(myStartPick, aNewPosition);

                if (this->myTranslationSteps > 0)
                {
                    Standard_Real distance = aNewPosition.Distance(myStartPick);

                    Standard_Real modDiv = fmod(distance, this->myTranslationSteps);

                    translationVector.Multiply((distance - modDiv) / distance);

                    if (translationVector.Magnitude() < Precision::Confusion())
                        return Standard_False;
                }

                aNewTrsf.SetTranslation(translationVector);
                // END

                // REMOVED TO ENSURE TRANSLATION STEPS 
                //aNewTrsf.SetTranslation(gp_Vec(myStartPick, aNewPosition));
                // END

                theTrsf *= aNewTrsf;
            }
            else if (myCurrentMode == AIS_MM_Scaling)
            {
                if (aNewPosition.Distance(myStartPosition.Location()) < Precision::Confusion())
                {
                    return Standard_False;
                }

                Standard_Real aCoeff = myStartPosition.Location().Distance(aNewPosition)
                    / myStartPosition.Location().Distance(myStartPick);

                // CHANGED TO ENSURE SCALING STEPS
                if (this->myScalingSteps > 0)
                {
                    Standard_Real modDiv = fmod(aCoeff, this->myScalingSteps);

                    aCoeff -= modDiv;
                }
                // END

                aNewTrsf.SetScale(myPosition.Location(), aCoeff);
                theTrsf = aNewTrsf;
            }
            return Standard_True;
        }
        case AIS_MM_Rotation:
        {
            const gp_Pnt aPosLoc = myStartPosition.Location();
            const gp_Ax1 aCurrAxis = getAx1FromAx2Dir(myStartPosition, myCurrentIndex);
            IntAna_IntConicQuad aIntersector(anInputLine, gp_Pln(aPosLoc, aCurrAxis.Direction()), Precision::Angular(), Precision::Intersection());
            if (!aIntersector.IsDone()
                || aIntersector.IsParallel()
                || aIntersector.NbPoints() < 1)
            {
                return Standard_False;
            }

            const gp_Pnt aNewPosition = aIntersector.Point(1);
            if (!myHasStartedTransformation)
            {
                myStartPick = aNewPosition;
                myHasStartedTransformation = Standard_True;
                gp_Dir aStartAxis = gce_MakeDir(aPosLoc, myStartPick);
                myPrevState = aStartAxis.AngleWithRef(gce_MakeDir(aPosLoc, aNewPosition), aCurrAxis.Direction());
                return Standard_True;
            }

            if (aNewPosition.Distance(myStartPick) < Precision::Confusion())
            {
                return Standard_False;
            }

            gp_Dir aStartAxis = aPosLoc.IsEqual(myStartPick, Precision::Confusion())
                ? getAx1FromAx2Dir(myStartPosition, (myCurrentIndex + 1) % 3).Direction()
                : gce_MakeDir(aPosLoc, myStartPick);

            gp_Dir aCurrentAxis = gce_MakeDir(aPosLoc, aNewPosition);
            Standard_Real anAngle = aStartAxis.AngleWithRef(aCurrentAxis, aCurrAxis.Direction());

            // Change value of an angle if it should have different sign.
            if (anAngle * myPrevState < 0 && Abs(anAngle) < M_PI_2)
            {
                Standard_Real aSign = myPrevState > 0 ? -1.0 : 1.0;
                anAngle = aSign * (M_PI * 2 - anAngle);
            }

            if (Abs(anAngle) < Precision::Confusion())
            {
                return Standard_False;
            }

            // ADDED TO ENSURE ROTATION STEPS
            if (this->myRotationSteps > 0)
            {
                Standard_Real modDiv = fmod(anAngle, this->myRotationSteps);
                anAngle -= modDiv;
            }
            // END

            gp_Trsf aNewTrsf;
            aNewTrsf.SetRotation(aCurrAxis, anAngle);
            theTrsf *= aNewTrsf;
            myPrevState = anAngle;
            return Standard_True;
        }
        case AIS_MM_TranslationPlane:
        {
            const gp_Pnt aPosLoc = myStartPosition.Location();
            const gp_Ax1 aCurrAxis = getAx1FromAx2Dir(myStartPosition, myCurrentIndex);
            IntAna_IntConicQuad aIntersector(anInputLine, gp_Pln(aPosLoc, aCurrAxis.Direction()), Precision::Angular(), Precision::Intersection());
            if (!aIntersector.IsDone() || aIntersector.NbPoints() < 1)
            {
                return Standard_False;
            }

            const gp_Pnt aNewPosition = aIntersector.Point(1);
            if (!myHasStartedTransformation)
            {
                myStartPick = aNewPosition;
                myHasStartedTransformation = Standard_True;
                return Standard_True;
            }

            if (aNewPosition.Distance(myStartPick) < Precision::Confusion())
            {
                return Standard_False;
            }

            // ADDED TO ENSURE TRANSLATION STEPS
            gp_Vec translationVector(myStartPick, aNewPosition);

            if (this->myTranslationSteps > 0)
            {
                Standard_Real distance = aNewPosition.Distance(myStartPick);

                Standard_Real modDiv = fmod(distance, this->myTranslationSteps);

                translationVector.Multiply((distance - modDiv) / distance);

                if (translationVector.Magnitude() < Precision::Confusion())
                    return Standard_False;
            }

            gp_Trsf aNewTrsf;
            aNewTrsf.SetTranslation(translationVector);
            // END
            
            // REMOVED TO ENSURE TRANSLATION STEPS 
            //gp_Trsf aNewTrsf;
            //aNewTrsf.SetTranslation(gp_Vec(myStartPick, aNewPosition));
            // END
            theTrsf *= aNewTrsf;
            return Standard_True;
        }
        case AIS_MM_None:
        {
            return Standard_False;
        }
    }
    return Standard_False;
}

Usage :

myManipulator->SetTranslationSteps(0.5);
myManipulator->SetRotationSteps(M_PI / 8);
myManipulator->SetScalingSteps(0.2);
gp_Trsf transformation = myManipulator->CustomTransform(mouseX, mouseY, myView);