1 // Copyright (C) 2007-2021 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 < (int)myPlanes.size(); ++aPlaneIt )
130 if ( myPlanes[aPlaneIt] == thePlane )
142 Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
144 if ( anAISContext->IsSelected( 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 < (int)myPlanes.size(); ++aPlaneIt )
161 if ( myPlanes[aPlaneIt] == thePlane )
173 Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
175 if ( !anAISContext->IsSelected( 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 gp_Dir aPlaneN = aPlanePln.Axis().Direction();
224 gp_Dir aPlaneX = aPlanePln.XAxis().Direction();
225 gp_Dir aPlaneY = aPlanePln.YAxis().Direction();
226 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 /* todo: aDistance2Center, aCenterOnMousePln not used
327 Standard_Real aDistance2Center = myMouseDragPln.Distance( aPlaneCenter );
328 gp_Pnt aCenterOnMousePln = aMousePlnN * gp_Vec( aPickPoint, aPlaneCenter ) < 0.0
329 ? aPlaneCenter.Translated( aMousePlnN * aDistance2Center )
330 : aPlaneCenter.Translated( aMousePlnN * -aDistance2Center );
333 myRotationAxis = gp_Ax1( myRotationCenter, aMousePlnN );
337 gp_Vec aMousePlnN = gp_Vec( aPlaneY );
339 myMouseDragPln = gp_Pln( aPickPoint, aMousePlnN );
341 /* todo: aDistance2Center, aCenterOnMousePln not used
342 Standard_Real aDistance2Center = myMouseDragPln.Distance( aPlaneCenter );
343 gp_Pnt aCenterOnMousePln = aMousePlnN * gp_Vec( aPickPoint, aPlaneCenter ) < 0.0
344 ? aPlaneCenter.Translated( aMousePlnN * aDistance2Center )
345 : aPlaneCenter.Translated( aMousePlnN * -aDistance2Center );
348 myRotationAxis = gp_Ax1( myRotationCenter, aMousePlnN );
351 myPlaneReferenceCS = gp_Ax3( aPlaneCenter, aPlaneN, aPlaneX );
365 \brief Performs dragging operation on the passed interactive plane.
366 \param theDragPos [in] the position of mouse dragging.
367 \param theDragOp [in] the drag operation to start.
368 \param thePlane [in] the operated plane.
369 \param theView [in] the view.
371 void OCCViewer_ClipPlaneInteractor::performDragging( const QPoint& theDragPos,
372 const DragOperation theDragOp,
373 const Handle(AIS_Plane)& thePlane,
374 const Handle(V3d_View)& theView )
376 Standard_Real P[3], D[3];
377 theView->ConvertWithProj( theDragPos.x(), theDragPos.y(), P[0], P[1], P[2], D[0], D[1], D[2] );
378 gp_Lin aProjection = gp_Lin( gp_Pnt( P[0], P[1], P[2] ), gp_Dir( D[0], D[1], D[2] ) );
380 // get point on the plane
381 Handle(Geom_Line) aCrossLine = new Geom_Line( aProjection );
382 Handle(Geom_Plane) aCrossPlane = new Geom_Plane( myMouseDragPln );
384 GeomAPI_IntCS aFindCross( aCrossLine, aCrossPlane );
385 if ( !aFindCross.IsDone() || aFindCross.NbPoints() == 0 )
390 gp_Pnt aDragPoint = aFindCross.Point( 1 );
391 gp_Pnt aPlaneCenter = myPlaneReferenceCS.Location();
392 gp_Vec aPlaneN = myPlaneReferenceCS.Direction();
393 gp_Vec aPlaneX = myPlaneReferenceCS.XDirection();
394 gp_Pln aPlanePln = gp_Pln( aPlaneCenter, aPlaneN );
398 // sliding the plane along its normal
399 case DragOperation_Slide:
401 Standard_Real aTranslation =
402 gp_Vec( aPlaneCenter, aDragPoint ) * gp_Vec( aPlaneN ) > 0.0
403 ? aPlanePln.Distance( aDragPoint )
404 : -aPlanePln.Distance( aDragPoint );
406 gp_Pnt aNewCenter = aPlaneCenter.Translated( aPlaneN * aTranslation );
408 myPlaneReferenceCS = gp_Ax3( aNewCenter, aPlaneN, aPlaneX );
410 adjustBounds( myPlaneReferenceCS, myMinMax );
412 thePlane->SetComponent( new Geom_Plane( myPlaneReferenceCS ) );
413 thePlane->SetCenter( myPlaneReferenceCS.Location() );
414 thePlane->SetToUpdate();
415 thePlane->UpdateSelection();
417 myViewer->getAISContext()->Update( thePlane , Standard_True );
421 case DragOperation_Rotate:
423 // project the dragging point on rotated plane
424 gp_Dir aRotAxis = myRotationAxis.Direction();
425 gp_Pln aDragAtCenterPln = gp_Pln( myRotationCenter, aRotAxis );
426 gp_Pnt aDragAtCenterPnt = gp_Vec( myRotationCenter, aDragPoint ) * gp_Vec( aRotAxis ) < 0.0
427 ? aDragPoint.Translated( gp_Vec( aRotAxis ) * aDragAtCenterPln.Distance( aDragPoint ) )
428 : aDragPoint.Translated( gp_Vec( aRotAxis ) * -aDragAtCenterPln.Distance( aDragPoint ) );
430 gp_Pnt aDragOnPlanePnt = gp_Vec( aPlaneCenter, aDragAtCenterPnt ) * gp_Vec( aPlaneN ) < 0.0
431 ? aDragAtCenterPnt.Translated( gp_Vec( aPlaneN ) * aPlanePln.Distance( aDragAtCenterPnt ) )
432 : aDragAtCenterPnt.Translated( gp_Vec( aPlaneN ) * -aPlanePln.Distance( aDragAtCenterPnt ) );
434 gp_Vec aDragPointVector( myRotationCenter, aDragAtCenterPnt );
435 gp_Vec aProjPointVector( myRotationCenter, aDragOnPlanePnt );
437 // check for rotation tolerance
438 if ( aDragPointVector.Magnitude() < 0.01 || aProjPointVector.Magnitude() < 0.01 )
443 Standard_Real aTurnAngle = aProjPointVector.AngleWithRef( aDragPointVector, myRotationAxis.Direction() );
445 gp_Trsf aRotationTrsf;
446 aRotationTrsf.SetRotation( myRotationAxis, aTurnAngle );
447 myPlaneReferenceCS.Transform( aRotationTrsf );
449 adjustBounds( myPlaneReferenceCS, myMinMax );
451 gp_Ax3 aPlaneCS( myPlaneReferenceCS.Location(), myPlaneReferenceCS.Direction() );
453 thePlane->SetComponent( new Geom_Plane( aPlaneCS ) );
454 thePlane->SetCenter( myPlaneReferenceCS.Location() );
455 thePlane->SetToUpdate();
456 thePlane->UpdateSelection();
458 myViewer->getAISContext()->Update( thePlane , Standard_True );
467 \brief Adjusts min-max bounds of the plane.
468 \param thePlane [in/out] the plane.
469 \param theMinMax [in] the min max bounds
471 void OCCViewer_ClipPlaneInteractor::adjustBounds( gp_Ax3& thePlane, const Bnd_Box& theMinMax )
473 gp_Trsf aRelativeTransform;
474 aRelativeTransform.SetTransformation( gp_Ax3(), thePlane );
475 Bnd_Box aRelativeBounds = theMinMax.Transformed( aRelativeTransform );
477 Standard_Real aXmin, aXmax, aYmin, aYmax, aZmin, aZmax;
478 aRelativeBounds.Get( aXmin, aYmin, aZmin, aXmax, aYmax, aZmax );
480 if ( aZmax < 0.0 ) // out in positive direction
482 thePlane.Translate( gp_Vec( thePlane.Direction() ) * aZmax );
484 else if ( aZmin > 0.0 ) // out in negative direction
486 thePlane.Translate( gp_Vec( thePlane.Direction() ) * aZmin );
491 \brief Handle mouse press events. Starts interaction with detected plane.
492 \param theEvent [in] the user event.
493 \param theViewPort [in] the viewport.
495 bool OCCViewer_ClipPlaneInteractor::mousePress( QMouseEvent* theEvent,
496 OCCViewer_ViewPort3d* theViewPort )
498 if ( theEvent->button() != Qt::LeftButton )
503 Handle(V3d_View) aView3D = theViewPort->getView();
505 Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
507 // check detection of plane
508 anAISContext->MoveTo( theEvent->x(), theEvent->y(), aView3D , Standard_True );
510 if ( !anAISContext->HasDetected() )
515 // check that there is only one detected entity
516 anAISContext->InitDetected();
518 Handle(AIS_Plane) aPlane;
519 Handle(SelectMgr_EntityOwner) aDetectedOwner = anAISContext->DetectedOwner();
520 if ( !aDetectedOwner.IsNull() )
522 aPlane = Handle(AIS_Plane)::DownCast( aDetectedOwner->Selectable() );
525 if ( aPlane.IsNull() )
527 aPlane = Handle(AIS_Plane)::DownCast( anAISContext->DetectedInteractive() );
530 myIsClickable = isClickable( aPlane );
532 // process mouse click on the object
535 myViewer->getAISContext()->SetSelected( aPlane, Standard_True );
536 emit planeClicked( aPlane );
539 myIsDraggable = isDraggable( aPlane );
541 if ( !myIsClickable && !myIsDraggable )
546 myPickPos = theEvent->pos();
547 myInteractedPlane = aPlane;
553 \brief Handle mouse move events. Performs dragging if interaction is in progress.
554 \param theEvent [in] the user event.
555 \param theViewPort [in] the viewport.
557 bool OCCViewer_ClipPlaneInteractor::mouseMove( QMouseEvent* theEvent,
558 OCCViewer_ViewPort3d* theViewPort )
560 if ( !isPerforming() )
565 // no dragging operation can be performed...
566 if ( !myIsDraggable )
571 Handle(V3d_View) aView3D = theViewPort->getView();
573 myDragPos = theEvent->pos();
575 // checking whether it is possible to start dragging operation
576 if ( myPerformingOp == DragOperation_Undef )
578 int aDx = myDragPos.x() - myPickPos.x();
579 int aDy = myDragPos.y() - myPickPos.y();
580 if ( ( aDx * aDx + aDy * aDy ) < 16 )
585 DragOperation aDragOp =
586 theEvent->modifiers().testFlag(Qt::ControlModifier)
587 ? DragOperation_Rotate
588 : DragOperation_Slide;
590 myIsDraggable = startDragging( myPickPos, myDragPos, aDragOp, myInteractedPlane, aView3D );
592 if ( !myIsDraggable )
597 myPerformingOp = aDragOp;
600 // performing dragging operation
601 performDragging( myDragPos, myPerformingOp, myInteractedPlane, aView3D );
603 emit planeDragged( myInteractedPlane );
609 \brief Handle mouse release events. Stops interaction.
610 \param theEvent [in] the user event.
611 \param theViewPort [in] the viewport.
613 bool OCCViewer_ClipPlaneInteractor::mouseRelease( QMouseEvent* /*theEvent*/,
614 OCCViewer_ViewPort3d* /*theViewPort*/ )
616 if ( !isPerforming() )
621 myMouseDragPln = gp_Pln();
622 myPerformingOp = DragOperation_Undef;
623 myPickPos = QPoint();
624 myDragPos = QPoint();
625 myInteractedPlane = NULL;
626 myIsDraggable = false;
627 myIsClickable = false;
628 myViewer->getAISContext()->ClearSelected( Standard_True );
633 \brief Handle mouse double clicking events events. Stops the event propagation if
634 interaction with plane is in progress.
635 \param theEvent [in] the user event.
636 \param theViewPort [in] the viewport.
638 bool OCCViewer_ClipPlaneInteractor::mouseDoubleClick( QMouseEvent* /*theEvent*/,
639 OCCViewer_ViewPort3d* /*theViewPort*/ )
641 return isPerforming();
645 \brief Handle key pressing events. Stops the event propagation if
646 interaction with plane is in progress.
647 \param theEvent [in] the user event.
648 \param theViewPort [in] the viewport.
650 bool OCCViewer_ClipPlaneInteractor::keyPress( QKeyEvent* theEvent,
651 OCCViewer_ViewPort3d* theViewPort )
653 // react to pressing & releasing ctrl key modifier
654 if ( !isPerforming() )
659 DragOperation aDragOp =
660 theEvent->modifiers().testFlag(Qt::ControlModifier)
661 ? DragOperation_Rotate
662 : DragOperation_Slide;
664 if ( aDragOp != myPerformingOp )
666 myPerformingOp = DragOperation_Undef;
667 myIsDraggable = isDraggable( myInteractedPlane );
668 myPickPos = theViewPort->mapFromGlobal( QCursor::pos() );
675 \brief Handle key releasing events. Stops the event propagation if
676 interaction with plane is in progress.
677 \param theEvent [in] the user event.
678 \param theViewPort [in] the viewport.
680 bool OCCViewer_ClipPlaneInteractor::keyRelease( QKeyEvent* theEvent,
681 OCCViewer_ViewPort3d* theViewPort )
683 // react to pressing & releasing ctrl key modifier
684 if ( !isPerforming() )
689 DragOperation aDragOp =
690 theEvent->modifiers().testFlag(Qt::ControlModifier)
691 ? DragOperation_Rotate
692 : DragOperation_Slide;
694 if ( aDragOp != myPerformingOp )
696 myPerformingOp = DragOperation_Undef;
697 myIsDraggable = isDraggable( myInteractedPlane );
698 myPickPos = theViewPort->mapFromGlobal( QCursor::pos() );