1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #include "OCCViewer_ClipPlaneInteractor.h"
24 #include "OCCViewer_ViewPort3d.h"
25 #include "OCCViewer_ViewModel.h"
27 #include <AIS_InteractiveContext.hxx>
28 #include <SelectMgr_EntityOwner.hxx>
29 #include <V3d_View.hxx>
30 #include <Geom_Line.hxx>
31 #include <Geom_Plane.hxx>
32 #include <GeomAPI_IntCS.hxx>
33 #include <gce_MakeDir.hxx>
35 #include <QMouseEvent>
40 \param theVM [in] the view manager.
41 \param theParent [in] the parent object.
43 OCCViewer_ClipPlaneInteractor::OCCViewer_ClipPlaneInteractor( OCCViewer_ViewManager* theVM,
45 : OCCViewer_ViewportInputFilter( theVM, theParent ),
46 myPerformingOp( DragOperation_Undef ),
47 myRotationCenter( gp::Origin() ),
48 myIsDraggable( false ),
49 myIsClickable( false )
54 \brief Get sequence of planes to interact with.
55 \return the sequence of accepted planes.
57 const OCCViewer_ClipPlaneInteractor::SeqOfPlanes& OCCViewer_ClipPlaneInteractor::planes() const
63 \brief Sets sequence of planes allowed for interaction.
64 \param thePlanes [in] the sequence of accepted planes.
66 void OCCViewer_ClipPlaneInteractor::setPlanes( const SeqOfPlanes& thePlanes )
72 \brief Sets center of rotation for the scene.
73 \param theCenter [in] the center of rotation.
75 void OCCViewer_ClipPlaneInteractor::setRotationCenter( const gp_Pnt& theCenter )
77 myRotationCenter = theCenter;
81 \brief Sets minimum and maximum bounding of the scene.
82 The sliding movements are limited to minimum
84 \param theMinMax [in] the minimum and maximum bounds.
86 void OCCViewer_ClipPlaneInteractor::setMinMax( const Bnd_Box& theMinMax )
92 \brief Enables or disables event processing within the viewer.
94 void OCCViewer_ClipPlaneInteractor::setEnabled( const bool theIsEnabled )
100 myMouseDragPln = gp_Pln();
101 myPerformingOp = DragOperation_Undef;
102 myPickPos = QPoint();
103 myDragPos = QPoint();
104 myInteractedPlane = NULL;
105 myIsDraggable = false;
106 myIsClickable = false;
108 OCCViewer_ViewportInputFilter::setEnabled( theIsEnabled );
112 \brief Checks whether the interactive operation is in progress.
113 \return \c true if the interaction is performed on IO object meaning
114 that no other operations in viewer should be processed.
116 bool OCCViewer_ClipPlaneInteractor::isPerforming() const
118 return !myInteractedPlane.IsNull();
122 \brief Checks whether the interactive plane can be clicked.
123 \return \c true if the click interaction is supported for the plane.
125 bool OCCViewer_ClipPlaneInteractor::isClickable( const Handle(AIS_Plane)& thePlane )
127 bool isFound = Standard_False;
128 for ( int aPlaneIt = 0; aPlaneIt < myPlanes.size(); ++aPlaneIt )
130 if ( myPlanes[aPlaneIt] == thePlane )
142 Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
144 if ( anAISContext->IsSelected( Handle(AIS_InteractiveObject)::DownCast(thePlane) ) )
153 \brief Checks whether the interactive plane can be dragged.
154 \return \c true if the dragging interaction is supported for the plane.
156 bool OCCViewer_ClipPlaneInteractor::isDraggable( const Handle(AIS_Plane)& thePlane )
158 bool isFound = Standard_False;
159 for ( int aPlaneIt = 0; aPlaneIt < myPlanes.size(); ++aPlaneIt )
161 if ( myPlanes[aPlaneIt] == thePlane )
173 Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
175 if ( !anAISContext->IsSelected( Handle(AIS_InteractiveObject)::DownCast(thePlane) ) )
184 \brief Checks whether it is possible to start interaction with plane.
185 \param thePickPos [in] the position of mouse picking.
186 \param theDragPos [in] the position of initial mouse dragging.
187 \param theDragOp [in] the drag operation to start.
188 \param thePlane [in] the detected plane.
189 \param theView [in] the view.
191 bool OCCViewer_ClipPlaneInteractor::startDragging( const QPoint& thePickPos,
192 const QPoint& theDragPos,
193 const DragOperation theDragOp,
194 const Handle(AIS_Plane)& thePlane,
195 const Handle(V3d_View)& theView )
197 // get point of view plane intersection with the plane
198 Standard_Real P[3], D[3];
199 theView->ConvertWithProj( thePickPos.x(), thePickPos.y(), P[0], P[1], P[2], D[0], D[1], D[2] );
200 gp_Lin aPickProj = gp_Lin( gp_Pnt( P[0], P[1], P[2] ), gp_Dir( D[0], D[1], D[2] ) );
201 gp_Pln aPlanePln = thePlane->Component()->Pln();
203 Handle(Geom_Line) aPickLine = new Geom_Line( aPickProj );
204 Handle(Geom_Plane) aCrossPlane = new Geom_Plane( aPlanePln );
206 GeomAPI_IntCS aFindPick( aPickLine, aCrossPlane );
207 if ( !aFindPick.IsDone() || aFindPick.NbPoints() == 0 )
212 // check plane geometry
213 Standard_Real aSizeX = 0.0;
214 Standard_Real aSizeY = 0.0;
215 thePlane->Size(aSizeX, aSizeY);
216 if ( aSizeX < Precision::Confusion() || aSizeY < Precision::Confusion() )
221 gp_Pnt aPickPoint = aFindPick.Point( 1 );
223 const gp_Dir& aPlaneN = aPlanePln.Axis().Direction();
224 const gp_Dir& aPlaneX = aPlanePln.XAxis().Direction();
225 const gp_Dir& aPlaneY = aPlanePln.YAxis().Direction();
226 const gp_Pnt& aPlaneCenter = aPlanePln.Location();
230 // sliding operation is started
231 case DragOperation_Slide :
233 if ( aPlaneN.IsParallel( aPickProj.Direction(), M_PI / 180.0 ) )
238 gp_Dir aMousePlnDir = ( aPickProj.Direction() ^ aPlaneN ) ^ aPlaneN;
240 myMouseDragPln = gp_Pln( aPickPoint, aMousePlnDir );
241 myPlaneReferenceCS = gp_Ax3( aPlaneCenter, aPlaneN, aPlaneX );
246 // rotation operation is requested
247 case DragOperation_Rotate :
249 theView->ConvertWithProj( theDragPos.x(), theDragPos.y(), P[0], P[1], P[2], D[0], D[1], D[2] );
250 gp_Lin aDragProj = gp_Lin( gp_Pnt( P[0], P[1], P[2] ), gp_Dir( D[0], D[1], D[2] ) );
252 if ( aPickPoint.Distance( aDragProj.Location() ) < Precision::Confusion() )
257 // to determine whether we rotate around first or second axis, we
258 // construct a virtual "arm" as vector of from center of rotation
259 // to the picked point. Then we calculate dragging directions for both axes
260 // depending on the difference of picking and dragging mouse coordinates.
261 // The direction which is physically more easy to turn the "arm" is choosen
262 // and the corresponding plane for dragging is selected.
264 gp_Vec anArm = gp_Vec( myRotationCenter, aPickPoint );
265 Standard_Real anArmLength = anArm.Magnitude();
266 if ( anArmLength < Precision::Confusion() )
271 Handle(Geom_Line) aDragLine = new Geom_Line( aDragProj );
272 Standard_Real aMomentArm1 = 0.0;
273 Standard_Real aMomentArm2 = 0.0;
274 Standard_Real anArmLength1 = Abs( anArm * gp_Vec( aPlaneN ^ aPlaneX ) );
275 Standard_Real anArmLength2 = Abs( anArm * gp_Vec( aPlaneN ^ aPlaneY ) );
277 // check virtual "arm" dragging moment for first axis of rotation
278 if ( !aPlaneX.IsNormal( aDragProj.Direction(), M_PI / 180.0 ) && ( anArmLength1 / anArmLength > 0.3 ) )
280 Handle(Geom_Plane) aDragPln = new Geom_Plane( aPickPoint, aPlaneX );
282 gp_Pnt aDragPnt = aPickPoint;
283 gp_Vec aDragDir = gp_Vec( 0.0, 0.0, 0.0 );
284 GeomAPI_IntCS aFindCross( aDragLine, aDragPln );
285 if ( aFindCross.IsDone() && aFindCross.NbPoints() != 0 )
287 aDragPnt = aFindCross.Point( 1 );
290 if ( aDragPnt.Distance( aPickPoint ) > Precision::Confusion() )
292 aDragDir = gp_Vec( aPickPoint, aDragPnt );
295 aMomentArm1 = anArmLength1 * ( 1.0 - Abs( aDragDir.Normalized() * anArm.Normalized() ) );
298 // check virtual "arm" dragging moment for second axis of rotation
299 if ( !aPlaneY.IsNormal( aDragProj.Direction(), M_PI / 180.0 )&& ( anArmLength2 / anArmLength > 0.3 ) )
301 Handle(Geom_Plane) aDragPln = new Geom_Plane( aPickPoint, aPlaneY );
303 gp_Pnt aDragPnt = aPickPoint;
304 gp_Vec aDragDir = gp_Vec( 0.0, 0.0, 0.0 );
305 GeomAPI_IntCS aFindCross( aDragLine, aDragPln );
306 if ( aFindCross.IsDone() && aFindCross.NbPoints() != 0 )
308 aDragPnt = aFindCross.Point( 1 );
311 if ( aDragPnt.Distance( aPickPoint ) > Precision::Confusion() )
313 aDragDir = gp_Vec( aPickPoint, aDragPnt );
316 aMomentArm2 = anArmLength2 * ( 1.0 - Abs( aDragDir.Normalized() * anArm.Normalized() ) );
319 // choose the best plane for dragging
320 if ( aMomentArm1 >= aMomentArm2 )
322 gp_Vec aMousePlnN = gp_Vec( aPlaneX );
324 myMouseDragPln = gp_Pln( aPickPoint, aMousePlnN );
326 Standard_Real aDistance2Center = myMouseDragPln.Distance( aPlaneCenter );
327 gp_Pnt aCenterOnMousePln = aMousePlnN * gp_Vec( aPickPoint, aPlaneCenter ) < 0.0
328 ? aPlaneCenter.Translated( aMousePlnN * aDistance2Center )
329 : aPlaneCenter.Translated( aMousePlnN * -aDistance2Center );
331 myRotationAxis = gp_Ax1( myRotationCenter, aMousePlnN );
335 gp_Vec aMousePlnN = gp_Vec( aPlaneY );
337 myMouseDragPln = gp_Pln( aPickPoint, aMousePlnN );
339 Standard_Real aDistance2Center = myMouseDragPln.Distance( aPlaneCenter );
340 gp_Pnt aCenterOnMousePln = aMousePlnN * gp_Vec( aPickPoint, aPlaneCenter ) < 0.0
341 ? aPlaneCenter.Translated( aMousePlnN * aDistance2Center )
342 : aPlaneCenter.Translated( aMousePlnN * -aDistance2Center );
344 myRotationAxis = gp_Ax1( myRotationCenter, aMousePlnN );
347 myPlaneReferenceCS = gp_Ax3( aPlaneCenter, aPlaneN, aPlaneX );
357 \brief Performs dragging operation on the passed interactive plane.
358 \param theDragPos [in] the position of mouse dragging.
359 \param theDragOp [in] the drag operation to start.
360 \param thePlane [in] the operated plane.
361 \param theView [in] the view.
363 void OCCViewer_ClipPlaneInteractor::performDragging( const QPoint& theDragPos,
364 const DragOperation theDragOp,
365 const Handle(AIS_Plane)& thePlane,
366 const Handle(V3d_View)& theView )
368 Standard_Real P[3], D[3];
369 theView->ConvertWithProj( theDragPos.x(), theDragPos.y(), P[0], P[1], P[2], D[0], D[1], D[2] );
370 gp_Lin aProjection = gp_Lin( gp_Pnt( P[0], P[1], P[2] ), gp_Dir( D[0], D[1], D[2] ) );
372 // get point on the plane
373 Handle(Geom_Line) aCrossLine = new Geom_Line( aProjection );
374 Handle(Geom_Plane) aCrossPlane = new Geom_Plane( myMouseDragPln );
376 GeomAPI_IntCS aFindCross( aCrossLine, aCrossPlane );
377 if ( !aFindCross.IsDone() || aFindCross.NbPoints() == 0 )
382 gp_Pnt aDragPoint = aFindCross.Point( 1 );
383 gp_Pnt aPlaneCenter = myPlaneReferenceCS.Location();
384 gp_Vec aPlaneN = myPlaneReferenceCS.Direction();
385 gp_Vec aPlaneX = myPlaneReferenceCS.XDirection();
386 gp_Pln aPlanePln = gp_Pln( aPlaneCenter, aPlaneN );
390 // sliding the plane along its normal
391 case DragOperation_Slide:
393 Standard_Real aTranslation =
394 gp_Vec( aPlaneCenter, aDragPoint ) * gp_Vec( aPlaneN ) > 0.0
395 ? aPlanePln.Distance( aDragPoint )
396 : -aPlanePln.Distance( aDragPoint );
398 gp_Pnt aNewCenter = aPlaneCenter.Translated( aPlaneN * aTranslation );
400 myPlaneReferenceCS = gp_Ax3( aNewCenter, aPlaneN, aPlaneX );
402 adjustBounds( myPlaneReferenceCS, myMinMax );
404 thePlane->SetComponent( new Geom_Plane( myPlaneReferenceCS ) );
405 thePlane->SetCenter( myPlaneReferenceCS.Location() );
406 thePlane->SetToUpdate();
407 thePlane->UpdateSelection();
409 myViewer->getAISContext()->Update( thePlane , Standard_True );
413 case DragOperation_Rotate:
415 // project the dragging point on rotated plane
416 gp_Dir aRotAxis = myRotationAxis.Direction();
417 gp_Pln aDragAtCenterPln = gp_Pln( myRotationCenter, aRotAxis );
418 gp_Pnt aDragAtCenterPnt = gp_Vec( myRotationCenter, aDragPoint ) * gp_Vec( aRotAxis ) < 0.0
419 ? aDragPoint.Translated( gp_Vec( aRotAxis ) * aDragAtCenterPln.Distance( aDragPoint ) )
420 : aDragPoint.Translated( gp_Vec( aRotAxis ) * -aDragAtCenterPln.Distance( aDragPoint ) );
422 gp_Pnt aDragOnPlanePnt = gp_Vec( aPlaneCenter, aDragAtCenterPnt ) * gp_Vec( aPlaneN ) < 0.0
423 ? aDragAtCenterPnt.Translated( gp_Vec( aPlaneN ) * aPlanePln.Distance( aDragAtCenterPnt ) )
424 : aDragAtCenterPnt.Translated( gp_Vec( aPlaneN ) * -aPlanePln.Distance( aDragAtCenterPnt ) );
426 gp_Vec aDragPointVector( myRotationCenter, aDragAtCenterPnt );
427 gp_Vec aProjPointVector( myRotationCenter, aDragOnPlanePnt );
429 // check for rotation tolerance
430 if ( aDragPointVector.Magnitude() < 0.01 || aProjPointVector.Magnitude() < 0.01 )
435 Standard_Real aTurnAngle = aProjPointVector.AngleWithRef( aDragPointVector, myRotationAxis.Direction() );
437 gp_Trsf aRotationTrsf;
438 aRotationTrsf.SetRotation( myRotationAxis, aTurnAngle );
439 myPlaneReferenceCS.Transform( aRotationTrsf );
441 adjustBounds( myPlaneReferenceCS, myMinMax );
443 gp_Ax3 aPlaneCS( myPlaneReferenceCS.Location(), myPlaneReferenceCS.Direction() );
445 thePlane->SetComponent( new Geom_Plane( aPlaneCS ) );
446 thePlane->SetCenter( myPlaneReferenceCS.Location() );
447 thePlane->SetToUpdate();
448 thePlane->UpdateSelection();
450 myViewer->getAISContext()->Update( thePlane , Standard_True );
457 \brief Adjusts min-max bounds of the plane.
458 \param thePlane [in/out] the plane.
459 \param theMinMax [in] the min max bounds
461 void OCCViewer_ClipPlaneInteractor::adjustBounds( gp_Ax3& thePlane, const Bnd_Box& theMinMax )
463 gp_Trsf aRelativeTransform;
464 aRelativeTransform.SetTransformation( gp_Ax3(), thePlane );
465 Bnd_Box aRelativeBounds = theMinMax.Transformed( aRelativeTransform );
467 Standard_Real aXmin, aXmax, aYmin, aYmax, aZmin, aZmax;
468 aRelativeBounds.Get( aXmin, aYmin, aZmin, aXmax, aYmax, aZmax );
470 if ( aZmax < 0.0 ) // out in positive direction
472 thePlane.Translate( gp_Vec( thePlane.Direction() ) * aZmax );
474 else if ( aZmin > 0.0 ) // out in negative direction
476 thePlane.Translate( gp_Vec( thePlane.Direction() ) * aZmin );
481 \brief Handle mouse press events. Starts interaction with detected plane.
482 \param theEvent [in] the user event.
483 \param theViewPort [in] the viewport.
485 bool OCCViewer_ClipPlaneInteractor::mousePress( QMouseEvent* theEvent,
486 OCCViewer_ViewPort3d* theViewPort )
488 if ( theEvent->button() != Qt::LeftButton )
493 Handle(V3d_View) aView3D = theViewPort->getView();
495 Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
497 // check detection of plane
498 anAISContext->MoveTo( theEvent->x(), theEvent->y(), aView3D , Standard_True );
500 if ( !anAISContext->HasDetected() )
505 // check that there is only one detected entity
506 anAISContext->InitDetected();
508 Handle(AIS_Plane) aPlane;
509 Handle(SelectMgr_EntityOwner) aDetectedOwner = anAISContext->DetectedOwner();
510 if ( !aDetectedOwner.IsNull() )
512 aPlane = Handle(AIS_Plane)::DownCast( aDetectedOwner->Selectable() );
515 if ( aPlane.IsNull() )
517 aPlane = Handle(AIS_Plane)::DownCast( anAISContext->DetectedInteractive() );
520 myIsClickable = isClickable( aPlane );
522 // process mouse click on the object
525 myViewer->getAISContext()->SetSelected( Handle(AIS_InteractiveObject)::DownCast(aPlane) , Standard_True );
526 emit planeClicked( aPlane );
529 myIsDraggable = isDraggable( aPlane );
531 if ( !myIsClickable && !myIsDraggable )
536 myPickPos = theEvent->pos();
537 myInteractedPlane = aPlane;
543 \brief Handle mouse move events. Performs dragging if interaction is in progress.
544 \param theEvent [in] the user event.
545 \param theViewPort [in] the viewport.
547 bool OCCViewer_ClipPlaneInteractor::mouseMove( QMouseEvent* theEvent,
548 OCCViewer_ViewPort3d* theViewPort )
550 if ( !isPerforming() )
555 // no dragging operation can be performed...
556 if ( !myIsDraggable )
561 Handle(V3d_View) aView3D = theViewPort->getView();
563 myDragPos = theEvent->pos();
565 // checking whether it is possible to start dragging operation
566 if ( myPerformingOp == DragOperation_Undef )
568 int aDx = myDragPos.x() - myPickPos.x();
569 int aDy = myDragPos.y() - myPickPos.y();
570 if ( ( aDx * aDx + aDy * aDy ) < 16 )
575 DragOperation aDragOp =
576 theEvent->modifiers().testFlag(Qt::ControlModifier)
577 ? DragOperation_Rotate
578 : DragOperation_Slide;
580 myIsDraggable = startDragging( myPickPos, myDragPos, aDragOp, myInteractedPlane, aView3D );
582 if ( !myIsDraggable )
587 myPerformingOp = aDragOp;
590 // performing dragging operation
591 performDragging( myDragPos, myPerformingOp, myInteractedPlane, aView3D );
593 emit planeDragged( myInteractedPlane );
599 \brief Handle mouse release events. Stops interaction.
600 \param theEvent [in] the user event.
601 \param theViewPort [in] the viewport.
603 bool OCCViewer_ClipPlaneInteractor::mouseRelease( QMouseEvent* theEvent,
604 OCCViewer_ViewPort3d* theViewPort )
606 if ( !isPerforming() )
611 myMouseDragPln = gp_Pln();
612 myPerformingOp = DragOperation_Undef;
613 myPickPos = QPoint();
614 myDragPos = QPoint();
615 myInteractedPlane = NULL;
616 myIsDraggable = false;
617 myIsClickable = false;
622 \brief Handle mouse double clicking events events. Stops the event propagation if
623 interaction with plane is in progress.
624 \param theEvent [in] the user event.
625 \param theViewPort [in] the viewport.
627 bool OCCViewer_ClipPlaneInteractor::mouseDoubleClick( QMouseEvent* theEvent,
628 OCCViewer_ViewPort3d* theViewPort )
630 return isPerforming();
634 \brief Handle key pressing events. Stops the event propagation if
635 interaction with plane is in progress.
636 \param theEvent [in] the user event.
637 \param theViewPort [in] the viewport.
639 bool OCCViewer_ClipPlaneInteractor::keyPress( QKeyEvent* theEvent,
640 OCCViewer_ViewPort3d* theViewPort )
642 // react to pressing & releasing ctrl key modifier
643 if ( !isPerforming() )
648 DragOperation aDragOp =
649 theEvent->modifiers().testFlag(Qt::ControlModifier)
650 ? DragOperation_Rotate
651 : DragOperation_Slide;
653 if ( aDragOp != myPerformingOp )
655 myPerformingOp = DragOperation_Undef;
656 myIsDraggable = isDraggable( myInteractedPlane );
657 myPickPos = theViewPort->mapFromGlobal( QCursor::pos() );
664 \brief Handle key releasing events. Stops the event propagation if
665 interaction with plane is in progress.
666 \param theEvent [in] the user event.
667 \param theViewPort [in] the viewport.
669 bool OCCViewer_ClipPlaneInteractor::keyRelease( QKeyEvent* theEvent,
670 OCCViewer_ViewPort3d* theViewPort )
672 // react to pressing & releasing ctrl key modifier
673 if ( !isPerforming() )
678 DragOperation aDragOp =
679 theEvent->modifiers().testFlag(Qt::ControlModifier)
680 ? DragOperation_Rotate
681 : DragOperation_Slide;
683 if ( aDragOp != myPerformingOp )
685 myPerformingOp = DragOperation_Undef;
686 myIsDraggable = isDraggable( myInteractedPlane );
687 myPickPos = theViewPort->mapFromGlobal( QCursor::pos() );