]> SALOME platform Git repositories - modules/gui.git/blob - src/SVTK/SVTK_AreaPicker.cxx
Salome HOME
6480919f481acbb6a370f93c9882649929c72c9a
[modules/gui.git] / src / SVTK / SVTK_AreaPicker.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
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.
10 //
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.
15 //
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
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  SALOME VTKViewer : build VTK viewer into Salome desktop
24 //  File   : SVTK_AreaPicker.cxx
25 //  Author : 
26 //  Module : SALOME
27 //
28 #include "SVTK_AreaPicker.h"
29
30 #include <set>
31
32 #include <vtkObjectFactory.h>
33 #include <vtkCommand.h>
34
35 #include <vtkAbstractMapper3D.h>
36 #include <vtkMapper.h>
37 #include <vtkProperty.h>
38
39 #include <vtkAssemblyPath.h>
40 #include <vtkAssemblyNode.h>
41
42 #include <vtkRenderWindow.h>
43 #include <vtkMatrix4x4.h>
44 #include <vtkRenderer.h>
45 #include <vtkPoints.h>
46 #include <vtkCamera.h>
47 #include <vtkCell.h>
48
49 namespace
50 {
51   //----------------------------------------------------------------------------
52   inline
53   double GetZ( float* theZPtr, int theSelection[4], int theDX, int theDY )
54   {
55     return theZPtr[theDX - theSelection[0]
56         + ( theDY - theSelection[1] )
57             * ( theSelection[2] - theSelection[0] + 1 )];
58   }
59
60   //----------------------------------------------------------------------------
61   inline
62   int Check( float* theZPtr, int theSelection[4], double theTolerance,
63       double theDZ, int theDX, int theDY )
64   {
65     int aRet = 0;
66     double aZ = -1.0;
67     if ( theDX >= theSelection[0] && theDX <= theSelection[2]
68         && theDY >= theSelection[1] && theDY <= theSelection[3] ) {
69       // Access the value from the captured zbuffer.  Note, we only
70       // captured a portion of the zbuffer, so we need to offset dx by
71       // the selection window.
72       aZ = GetZ( theZPtr, theSelection, theDX, theDY );
73       if ( aZ > theTolerance && aZ < 1.0 - theTolerance ) {
74         aRet = fabs( aZ - theDZ ) <= theTolerance;
75       }
76     }
77     return aRet;
78   }
79
80   //----------------------------------------------------------------------------
81   inline
82   void GetCenter( const double theBounds[6], double theCenter[3] )
83   {
84     theCenter[0] = ( theBounds[1] + theBounds[0] ) / 2.0;
85     theCenter[1] = ( theBounds[3] + theBounds[2] ) / 2.0;
86     theCenter[2] = ( theBounds[5] + theBounds[4] ) / 2.0;
87   }
88
89   //----------------------------------------------------------------------------
90   void CalculatePickPosition( vtkRenderer *theRenderer, double theSelectionX,
91       double theSelectionY, double theSelectionZ, double thePickPosition[3] )
92   {
93     // Convert the selection point into world coordinates.
94     //
95     theRenderer->SetDisplayPoint( theSelectionX, theSelectionY, theSelectionZ );
96     theRenderer->DisplayToWorld();
97     double* aWorldCoords = theRenderer->GetWorldPoint();
98     if ( aWorldCoords[3] != 0.0 ) {
99       for ( int i = 0; i < 3; i++ ) {
100         thePickPosition[i] = aWorldCoords[i] / aWorldCoords[3];
101       }
102     }
103   }
104 }
105
106 vtkStandardNewMacro( SVTK_AreaPicker )
107 ;
108
109 SVTK_AreaPicker::SVTK_AreaPicker()
110 {
111   this->Tolerance = 0.005;
112   this->PickPoints = 1;
113 }
114
115 SVTK_AreaPicker::~SVTK_AreaPicker()
116 {
117 }
118
119 int SVTK_AreaPicker::Pick( double, double, double, vtkRenderer* )
120 {
121   return 0;
122 }
123
124 int SVTK_AreaPicker::Pick( double theSelectionX, double theSelectionY,
125     double theSelectionX2, double theSelectionY2, vtkRenderer *theRenderer,
126     SelectionMode theMode )
127 {
128   QVector< QPoint > aPoints;
129   aPoints.append( QPoint( (int)theSelectionX, (int)theSelectionY ) );
130   aPoints.append( QPoint( (int)theSelectionX2, (int)theSelectionY2 ) );
131   return Pick( aPoints, theRenderer, theMode );
132 }
133
134 int SVTK_AreaPicker::Pick( QVector< QPoint >& thePoints,
135     vtkRenderer *theRenderer, SelectionMode theMode )
136 {
137   //  Initialize picking process
138   this->Initialize();
139   myCellIdsMap.clear();
140   myPointIdsMap.clear();
141   this->Renderer = theRenderer;
142
143   if ( theMode == RectangleMode ) {
144     mySelection[0] = thePoints[0].x();
145     mySelection[1] = thePoints[0].y();
146     mySelection[2] = thePoints[1].x();
147     mySelection[3] = thePoints[1].y();
148   }
149   else if( theMode == PolygonMode ) {
150     int minX, minY, maxX, maxY;
151     minX = maxX = thePoints[0].x();
152     minY = maxY = thePoints[0].y();
153     for ( int i=0; i < thePoints.size(); i++ ) {
154       if ( thePoints[i].x() < minX )
155       minX = thePoints[i].x();
156       if ( thePoints[i].x() > maxX )
157       maxX = thePoints[i].x();
158       if ( thePoints[i].y() < minY )
159       minY = thePoints[i].y();
160       if ( thePoints[i].y() > maxY )
161       maxY = thePoints[i].y();
162     }
163     mySelection[0] = minX;
164     mySelection[1] = minY;
165     mySelection[2] = maxX;
166     mySelection[3] = maxY;
167   }
168
169   // Invoke start pick method if defined
170   this->InvokeEvent( vtkCommand::StartPickEvent, NULL );
171
172   vtkPropCollection *aProps;
173   if ( this->PickFromList ) aProps = this->GetPickList();
174   else
175     aProps = theRenderer->GetViewProps();
176
177   aProps->InitTraversal();
178   while( vtkProp* aProp = aProps->GetNextProp() ) {
179     aProp->InitPathTraversal();
180     while( vtkAssemblyPath* aPath = aProp->GetNextPath() ) {
181       vtkMapper *aMapper = NULL;
182       bool anIsPickable = false;
183       vtkActor* anActor = NULL;
184       vtkProp *aPropCandidate = aPath->GetLastNode()->GetViewProp();
185       if ( aPropCandidate->GetPickable() && aPropCandidate->GetVisibility() ) {
186         anIsPickable = true;
187         anActor = vtkActor::SafeDownCast( aPropCandidate );
188         if ( anActor ) {
189           aMapper = anActor->GetMapper();
190           if ( anActor->GetProperty()->GetOpacity() <= 0.0 ) anIsPickable =
191               false;
192         }
193       }
194       if ( anIsPickable && aMapper && aMapper->GetInput() ) {
195         if ( this->PickPoints ) {
196           TVectorIds& aVisibleIds = myPointIdsMap[anActor];
197           TVectorIds anInVisibleIds;
198           SelectVisiblePoints( thePoints, theRenderer, aMapper->GetInput(),
199               aVisibleIds, anInVisibleIds, this->Tolerance, theMode );
200           if ( aVisibleIds.empty() ) {
201             myPointIdsMap.erase( myPointIdsMap.find( anActor ) );
202           }
203         }
204         else {
205           TVectorIds& aVectorIds = myCellIdsMap[anActor];
206           SelectVisibleCells( thePoints, theRenderer, aMapper->GetInput(),
207               aVectorIds, this->Tolerance, theMode );
208           if ( aVectorIds.empty() ) {
209             myCellIdsMap.erase( myCellIdsMap.find( anActor ) );
210           }
211         }
212       }
213     }
214   }
215
216   // Invoke end pick method if defined
217   this->InvokeEvent( vtkCommand::EndPickEvent, NULL );
218
219   return myPointIdsMap.empty() || myCellIdsMap.empty();
220 }
221
222 //----------------------------------------------------------------------------
223 void SVTK_AreaPicker::SelectVisiblePoints( QVector< QPoint >& thePoints,
224     vtkRenderer *theRenderer, vtkDataSet *theInput,
225     SVTK_AreaPicker::TVectorIds& theVisibleIds,
226     SVTK_AreaPicker::TVectorIds& theInVisibleIds, double theTolerance,
227     SelectionMode theMode )
228 {
229   theVisibleIds.clear();
230   theInVisibleIds.clear();
231
232   vtkIdType aNumPts = theInput->GetNumberOfPoints();
233   if ( aNumPts < 1 ) return;
234
235   theVisibleIds.reserve( aNumPts / 2 + 1 );
236   theInVisibleIds.reserve( aNumPts / 2 + 1 );
237
238   // Grab the composite perspective transform.  This matrix is used to convert
239   // each point to view coordinates.  vtkRenderer provides a WorldToView()
240   // method but it computes the composite perspective transform each time
241   // WorldToView() is called.  This is expensive, so we get the matrix once
242   // and handle the transformation ourselves.
243   vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
244   aMatrix->DeepCopy(
245       theRenderer->GetActiveCamera()->GetCompositeProjectionTransformMatrix(
246           theRenderer->GetTiledAspectRatio(), 0, 1 ) );
247
248   // We grab the z-buffer for the selection region all at once and probe the resulting array.
249   float *aZPtr = theRenderer->GetRenderWindow()->GetZbufferData( mySelection[0],
250       mySelection[1], mySelection[2], mySelection[3] );
251
252   for ( vtkIdType aPntId = 0; aPntId < aNumPts; aPntId++ ) {
253     // perform conversion
254     double aX[4] = { 1.0, 1.0, 1.0, 1.0 };
255     theInput->GetPoint( aPntId, aX );
256
257     double aView[4];
258     aMatrix->MultiplyPoint( aX, aView );
259     if ( aView[3] == 0.0 ) continue;
260     theRenderer->SetViewPoint( aView[0] / aView[3], aView[1] / aView[3],
261         aView[2] / aView[3] );
262     theRenderer->ViewToDisplay();
263
264     double aDX[3];
265     theRenderer->GetDisplayPoint( aDX );
266
267     bool isInSelection;
268     if ( theMode == RectangleMode ) isInSelection = aDX[0] >= mySelection[0]
269         && aDX[0] <= mySelection[2] && aDX[1] >= mySelection[1]
270         && aDX[1] <= mySelection[3];
271     else
272       if ( theMode == PolygonMode ) isInSelection =
273         isPointInPolygon( QPoint( (int)aDX[0], (int)aDX[1] ), thePoints );
274
275     // check whether visible and in selection window
276     if ( isInSelection ) {
277       int aDX0 = int( aDX[0] );
278       int aDX1 = int( aDX[1] );
279
280       int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2], aDX0, aDX1 );
281       if ( aRet > 0 ) goto ADD_VISIBLE;
282       if ( aRet < 0 ) goto ADD_INVISIBLE;
283
284       static int aMaxRadius = 5;
285       for ( int aRadius = 1; aRadius < aMaxRadius; aRadius++ ) {
286         int aStartDX[2] = { aDX0 - aRadius, aDX1 - aRadius };
287         for ( int i = 0; i <= aRadius; i++ ) {
288           int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
289               aStartDX[0]++, aStartDX[1] );
290           if ( aRet > 0 ) goto ADD_VISIBLE;
291           if ( aRet < 0 ) goto ADD_INVISIBLE;
292         }
293         for ( int i = 0; i <= aRadius; i++ ) {
294           int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
295               aStartDX[0], aStartDX[1]++ );
296           if ( aRet > 0 ) goto ADD_VISIBLE;
297           if ( aRet < 0 ) goto ADD_INVISIBLE;
298         }
299         for ( int i = 0; i <= aRadius; i++ ) {
300           int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
301               aStartDX[0]--, aStartDX[1] );
302           if ( aRet > 0 ) goto ADD_VISIBLE;
303           if ( aRet < 0 ) goto ADD_INVISIBLE;
304         }
305         for ( int i = 0; i <= aRadius; i++ ) {
306           int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
307               aStartDX[0], aStartDX[1]-- );
308           if ( aRet > 0 ) goto ADD_VISIBLE;
309           if ( aRet < 0 ) goto ADD_INVISIBLE;
310         }
311       }
312       if ( false ) ADD_VISIBLE:theVisibleIds.push_back( aPntId );
313       if ( false ) ADD_INVISIBLE:theInVisibleIds.push_back( aPntId );
314     }
315   }  //for all points
316
317   aMatrix->Delete();
318
319   if ( aZPtr ) delete[] aZPtr;
320 }
321
322 void SVTK_AreaPicker::SelectVisibleCells( QVector< QPoint >& thePoints,
323     vtkRenderer *theRenderer, vtkDataSet *theInput,
324     SVTK_AreaPicker::TVectorIds& theVectorIds, double theTolerance,
325     SelectionMode theMode )
326 {
327   theVectorIds.clear();
328
329   vtkIdType aNumCells = theInput->GetNumberOfCells();
330   if ( aNumCells < 1 ) return;
331
332   theVectorIds.reserve( aNumCells / 2 + 1 );
333
334   SVTK_AreaPicker::TVectorIds aVisiblePntIds;
335   SVTK_AreaPicker::TVectorIds anInVisiblePntIds;
336   SelectVisiblePoints( thePoints, theRenderer, theInput, aVisiblePntIds,
337       anInVisiblePntIds, theTolerance, theMode );
338
339   typedef std::set< vtkIdType > TIdsSet;
340   TIdsSet aVisibleIds( aVisiblePntIds.begin(), aVisiblePntIds.end() );
341   TIdsSet anInVisibleIds( anInVisiblePntIds.begin(), anInVisiblePntIds.end() );
342
343   // Grab the composite perspective transform.  This matrix is used to convert
344   // each point to view coordinates.  vtkRenderer provides a WorldToView()
345   // method but it computes the composite perspective transform each time
346   // WorldToView() is called.  This is expensive, so we get the matrix once
347   // and handle the transformation ourselves.
348   vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
349   aMatrix->DeepCopy(
350       theRenderer->GetActiveCamera()->GetCompositeProjectionTransformMatrix(
351           theRenderer->GetTiledAspectRatio(), 0, 1 ) );
352
353   for ( vtkIdType aCellId = 0; aCellId < aNumCells; aCellId++ ) {
354     vtkCell* aCell = theInput->GetCell( aCellId );
355
356     double aBounds[6];
357     aCell->GetBounds( aBounds );
358
359     double aCenter[3];
360     GetCenter( aBounds, aCenter );
361
362     double aView[4];
363     double aX[4] = { aCenter[0], aCenter[1], aCenter[2], 1.0 };
364     aMatrix->MultiplyPoint( aX, aView );
365
366     if ( aView[3] == 0.0 ) continue;
367
368     theRenderer->SetViewPoint( aView[0] / aView[3], aView[1] / aView[3],
369         aView[2] / aView[3] );
370     theRenderer->ViewToDisplay();
371
372     double aDX[3];
373     theRenderer->GetDisplayPoint( aDX );
374
375     bool isInSelection;
376     if ( theMode == RectangleMode ) isInSelection = aDX[0] >= mySelection[0]
377         && aDX[0] <= mySelection[2] && aDX[1] >= mySelection[1]
378         && aDX[1] <= mySelection[3];
379     else
380       if ( theMode == PolygonMode ) isInSelection =
381         isPointInPolygon( QPoint( (int)aDX[0], (int)aDX[1] ), thePoints );
382     // check whether visible and in selection window
383     if ( isInSelection ) {
384       vtkIdType aNumPts = aCell->GetNumberOfPoints();
385       bool anIsVisible = true;
386       for ( vtkIdType anId = 0; anId < aNumPts; anId++ ) {
387         vtkIdType aPntId = aCell->GetPointId( anId );
388         anIsVisible = aVisibleIds.find( aPntId ) != aVisibleIds.end();
389         if ( !anIsVisible ) break;
390       }
391       if ( anIsVisible ) theVectorIds.push_back( aCellId );
392     }
393   }  //for all parts
394 }
395
396 bool SVTK_AreaPicker::isPointInPolygon( const QPoint& thePoint, const QVector<QPoint>& thePolygon )
397 {
398   double eps = 1.0;
399   if ( thePolygon.size() < 3 ) return false;
400
401   QVector< QPoint >::const_iterator end = thePolygon.end();
402   QPoint last_pt = thePolygon.back();
403
404   last_pt.setX( last_pt.x() - thePoint.x() );
405   last_pt.setY( last_pt.y() - thePoint.y() );
406
407   double sum = 0.0;
408
409   for ( QVector< QPoint >::const_iterator iter = thePolygon.begin();
410       iter != end; ++iter ) {
411     QPoint cur_pt = *iter;
412     cur_pt.setX( cur_pt.x() - thePoint.x() );
413     cur_pt.setY( cur_pt.y() - thePoint.y() );
414
415     double del = last_pt.x() * cur_pt.y() - cur_pt.x() * last_pt.y();
416     double xy = cur_pt.x() * last_pt.x() + cur_pt.y() * last_pt.y();
417
418     if ( del )
419       sum +=
420       ( atan( ( last_pt.x() * last_pt.x() + last_pt.y() * last_pt.y() - xy ) / del )
421       + atan( ( cur_pt.x() * cur_pt.x() + cur_pt.y() * cur_pt.y() - xy ) / del ) );
422
423     last_pt = cur_pt;
424   }
425   return fabs( sum ) > eps;
426 }
427
428 const SVTK_AreaPicker::TVectorIdsMap&
429 SVTK_AreaPicker::GetPointIdsMap() const
430 {
431   return myPointIdsMap;
432 }
433
434 const SVTK_AreaPicker::TVectorIdsMap&
435 SVTK_AreaPicker::GetCellIdsMap() const
436 {
437   return myCellIdsMap;
438 }