MouseOperations
From SOFAWiki
Contents |
How SOFA manages the user interactions
The user interactions are handled by the GUI: as the events generated by the mouse are captured, and interpreted by the library used for the GUI. A class, the PickHandler is created to receive the events from the mouse, and translate them into specific Operations, that will be performed by the Sofa Components related, using an Interaction Performer.
Several Factories have been set up to allow flexibility in the creation, and modification of the different Operations and Performers desired. An objective of this design was to create a separation on what the user wants to do, and how this will be realized within the SOFA Framework.
This way, we created for the Qt GUI of Sofa, a Mouse Manager, able to know the Operations available, and let the User configure the behavior of its mouse, and the operations graphically. It is possible for instance to change the stiffness of the spring used to attach a body to the mouse. This way, we can imagine advanced User interactions, by designing more sophisticated Widgets than the current ones.
To know the different steps to create a complete new Mouse Interaction, just read this page
Components involved
PickHandler
The goal of the PickHandler is to translate mouse events given by the GUI you are using, into generic orders. For instance, for the Qt interface, we made the link between a QMouseEvent and the PickHandler inside the SofaViewer. An other usage can be found with the GUI using GLUT.
This PickHandler is an helper class to have information about the particles picked by the mouse. It creates basic sofa components inside the scene graph able to cast rays, and detect what is the nearest particle to the ray. It creates:
- Node Mouse: contains the components to interact with the mouse
- MechanicalObject: container of the position of the Mouse, and templated with Vec3d (only template compatible with collision models)
- RayModel: collision model compatible with the collision pipeline used with Sofa.
- ComponentMouseInteraction : this is in fact not a Sofa Component, but a tool to detect the template of the Mechanical State picked, and create a set of Sofa Components. It adds a node RayPick as child of the Mouse Node, and inside creates a compatible Mechanical State. To fill the MechanicalState with the position of the mouse, we create an IdentityMapping, updated at each time step. A MouseInteractor is added, with the same template as the Mechanical State picked, allowing to perform the MouseOperations (add a spring, remove a primitive...), using InteractionPerformer.
As it handles all the picking operation (cast a ray using the default collision pipeline if one is found, or using brute force if not), it always keeps in memory the last BodyPicked. It grants a direct access to the last MouseInteractor used (meaning by that, the one compatible with the last body picked).
Mouse Operation
To allow this flexibility, we set up an OperationFactory to handle the interactions with the Mouse. You have to register the different Operations you want to provide, so that the factory be able to generate the Operation on will.
The interface of the class Operation provides 3 basic methods:
virtual void start()=0; virtual void execution()=0; virtual void end()=0; virtual void wait()=0;
- start() is called when the button is pressed for the first time
- execution() is called when the button is still pressed and the mouse is moving
- end() is called when the button is released
- wait() is called as long as no other button are pressed
An Operation keeps as internal variable the PickHandler used: it gives the information about the last Body picked, and the MouseInteractor in use. Generally, when the start() method is called, an operation creates an InteractionPerformer, using the InteractionPerformerFactory, and link it to the MouseInteractor class (see [1]).
MouseInteractor
MouseInteractor is the Sofa Component appearing in the scene graph and triggering the different InteractionPerformer that it handles. The MouseInteractor class inherits from BehaviorModel. At each time step, the method updatePosition is called. Then, all the Performers are triggered, and execute their specific operation. Events transmission is done to all the Performers.
//Interactions handling void addInteractionPerformer(InteractionPerformer *i); bool removeInteractionPerformer( InteractionPerformer *i); //Called at each time step: launch all the performers void updatePosition( double dt); //Propagate an event in case to all the performers void handleEvent(core::objectmodel::Event *e);
As this class is templated, it is possible to create compatible MechanicalStates or ForceFields, compatible with the last Body picked.
Interaction Performer
This last class is the one responsible to realize the interaction wanted by the User: Attach a body, Fix a particle, Incise along a path...
The interface of the class InteractionPerformer provides 4 basic methods:
virtual void start()=0; virtual void execute()=0; virtual void handleEvent(core::objectmodel::Event * ); virtual void draw();
- start() is called when the performer is triggered for the first time
- execution() is called at each time step, as long as the InteractionPerformer remains linked to the MouseInteractor
- handleEvent(core::objectmodel::Event *) is called when an event is caught by the MouseInteractor
- draw() is called at each time step
To bo moved:
The aim of the Mouse Manager is to ease the creation of new interactions using the Mouse device. To do so, we made a clear separation of how the mouse is controlled, what operation will be executed and how this will be performed in relation with the Sofa Simulation. Separating the concept of MouseOperation and InteractionPerformer allows the user to design a graphic interface to control the Mouse Operation, and configure at start-up, execution, and termination, the behavior of the InteractionPerformer, performing the action desired on the Sofa Components.
To create a brand new Mouse Interaction, you will need to create two new classes:
- A MouseOperation: implementing the interface of Operation.
- To be able to use the new Mouse Operation, you have to register it in the OperationFactory. For instance, for the Qt interface, you just have to add one line in SofaMouseManager.cpp
RegisterOperation("MyOperation").add< MyOperation >();
- Then, when you launch Sofa, and open the Mouse Manager ( Menu->Edit->Mouse Manager... ), you will see your operation, with the description you gave in the combo list.
- You can easily create the GUI you want to configure your new Mouse Operation. For instance, to Attach a body to the Mouse, you can specify the stiffness of the string used [2]
- An InteractionPerformer: implementing the interface of InteractionPerformer.

