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...
Archived discussion
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!
Discussion
Posts are displayed in their archived order.
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...
Hello, you mean integration ability to specify determined steps for interactive actions?
Best regards, Commenter-2.
>>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)
I also need a method to perform this.
Anyone has found a solution to implement it ?
I bring this up and hope there will be progress on this?
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);