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 // SALOME VTKViewer : build VTK viewer into Salome desktop
24 // File : SVTK_AreaPicker.cxx
28 #include "SVTK_AreaPicker.h"
32 #include <vtkObjectFactory.h>
33 #include <vtkCommand.h>
35 #include <vtkAbstractMapper3D.h>
36 #include <vtkMapper.h>
37 #include <vtkProperty.h>
39 #include <vtkAssemblyPath.h>
40 #include <vtkAssemblyNode.h>
42 #include <vtkRenderWindow.h>
43 #include <vtkMatrix4x4.h>
44 #include <vtkRenderer.h>
45 #include <vtkPoints.h>
46 #include <vtkCamera.h>
51 //----------------------------------------------------------------------------
53 double GetZ( float* theZPtr, int theSelection[4], int theDX, int theDY )
55 return theZPtr[theDX - theSelection[0]
56 + ( theDY - theSelection[1] )
57 * ( theSelection[2] - theSelection[0] + 1 )];
60 //----------------------------------------------------------------------------
62 int Check( float* theZPtr, int theSelection[4], double theTolerance,
63 double theDZ, int theDX, int theDY )
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;
80 //----------------------------------------------------------------------------
82 void GetCenter( const double theBounds[6], double theCenter[3] )
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;
89 //----------------------------------------------------------------------------
91 void CalculatePickPosition( vtkRenderer *theRenderer, double theSelectionX,
92 double theSelectionY, double theSelectionZ, double thePickPosition[3] )
94 // Convert the selection point into world coordinates.
96 theRenderer->SetDisplayPoint( theSelectionX, theSelectionY, theSelectionZ );
97 theRenderer->DisplayToWorld();
98 double* aWorldCoords = theRenderer->GetWorldPoint();
99 if ( aWorldCoords[3] != 0.0 ) {
100 for ( int i = 0; i < 3; i++ ) {
101 thePickPosition[i] = aWorldCoords[i] / aWorldCoords[3];
108 vtkStandardNewMacro( SVTK_AreaPicker )
110 SVTK_AreaPicker::SVTK_AreaPicker()
112 this->Tolerance = 0.005;
113 this->PickPoints = 1;
116 SVTK_AreaPicker::~SVTK_AreaPicker()
120 int SVTK_AreaPicker::Pick( double, double, double, vtkRenderer* )
125 int SVTK_AreaPicker::Pick( double theSelectionX, double theSelectionY,
126 double theSelectionX2, double theSelectionY2, vtkRenderer *theRenderer,
127 SelectionMode theMode )
129 QVector< QPoint > aPoints;
130 aPoints.append( QPoint( (int)theSelectionX, (int)theSelectionY ) );
131 aPoints.append( QPoint( (int)theSelectionX2, (int)theSelectionY2 ) );
132 return Pick( aPoints, theRenderer, theMode );
135 int SVTK_AreaPicker::Pick( QVector< QPoint >& thePoints,
136 vtkRenderer *theRenderer, SelectionMode theMode )
138 // Initialize picking process
140 myCellIdsMap.clear();
141 myPointIdsMap.clear();
142 this->Renderer = theRenderer;
144 if ( theMode == RectangleMode ) {
145 mySelection[0] = thePoints[0].x();
146 mySelection[1] = thePoints[0].y();
147 mySelection[2] = thePoints[1].x();
148 mySelection[3] = thePoints[1].y();
150 else if( theMode == PolygonMode ) {
151 int minX, minY, maxX, maxY;
152 minX = maxX = thePoints[0].x();
153 minY = maxY = thePoints[0].y();
154 for ( int i=0; i < thePoints.size(); i++ ) {
155 if ( thePoints[i].x() < minX )
156 minX = thePoints[i].x();
157 if ( thePoints[i].x() > maxX )
158 maxX = thePoints[i].x();
159 if ( thePoints[i].y() < minY )
160 minY = thePoints[i].y();
161 if ( thePoints[i].y() > maxY )
162 maxY = thePoints[i].y();
164 mySelection[0] = minX;
165 mySelection[1] = minY;
166 mySelection[2] = maxX;
167 mySelection[3] = maxY;
170 // Invoke start pick method if defined
171 this->InvokeEvent( vtkCommand::StartPickEvent, NULL );
173 vtkPropCollection *aProps;
174 if ( this->PickFromList ) aProps = this->GetPickList();
176 aProps = theRenderer->GetViewProps();
178 aProps->InitTraversal();
179 while( vtkProp* aProp = aProps->GetNextProp() ) {
180 aProp->InitPathTraversal();
181 while( vtkAssemblyPath* aPath = aProp->GetNextPath() ) {
182 vtkMapper *aMapper = NULL;
183 bool anIsPickable = false;
184 vtkActor* anActor = NULL;
185 vtkProp *aPropCandidate = aPath->GetLastNode()->GetViewProp();
186 if ( aPropCandidate->GetPickable() && aPropCandidate->GetVisibility() ) {
188 anActor = vtkActor::SafeDownCast( aPropCandidate );
190 aMapper = anActor->GetMapper();
191 if ( anActor->GetProperty()->GetOpacity() <= 0.0 ) anIsPickable =
195 if ( anIsPickable && aMapper && aMapper->GetInput() ) {
196 if ( this->PickPoints ) {
197 TVectorIds& aVisibleIds = myPointIdsMap[anActor];
198 TVectorIds anInVisibleIds;
199 SelectVisiblePoints( thePoints, theRenderer, aMapper->GetInput(),
200 aVisibleIds, anInVisibleIds, this->Tolerance, theMode );
201 if ( aVisibleIds.empty() ) {
202 myPointIdsMap.erase( myPointIdsMap.find( anActor ) );
206 TVectorIds& aVectorIds = myCellIdsMap[anActor];
207 SelectVisibleCells( thePoints, theRenderer, aMapper->GetInput(),
208 aVectorIds, this->Tolerance, theMode );
209 if ( aVectorIds.empty() ) {
210 myCellIdsMap.erase( myCellIdsMap.find( anActor ) );
217 // Invoke end pick method if defined
218 this->InvokeEvent( vtkCommand::EndPickEvent, NULL );
220 return myPointIdsMap.empty() || myCellIdsMap.empty();
223 //----------------------------------------------------------------------------
224 void SVTK_AreaPicker::SelectVisiblePoints( QVector< QPoint >& thePoints,
225 vtkRenderer *theRenderer, vtkDataSet *theInput,
226 SVTK_AreaPicker::TVectorIds& theVisibleIds,
227 SVTK_AreaPicker::TVectorIds& theInVisibleIds, double theTolerance,
228 SelectionMode theMode )
230 theVisibleIds.clear();
231 theInVisibleIds.clear();
233 vtkIdType aNumPts = theInput->GetNumberOfPoints();
234 if ( aNumPts < 1 ) return;
236 theVisibleIds.reserve( aNumPts / 2 + 1 );
237 theInVisibleIds.reserve( aNumPts / 2 + 1 );
239 // Grab the composite perspective transform. This matrix is used to convert
240 // each point to view coordinates. vtkRenderer provides a WorldToView()
241 // method but it computes the composite perspective transform each time
242 // WorldToView() is called. This is expensive, so we get the matrix once
243 // and handle the transformation ourselves.
244 vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
246 theRenderer->GetActiveCamera()->GetCompositeProjectionTransformMatrix(
247 theRenderer->GetTiledAspectRatio(), 0, 1 ) );
249 // We grab the z-buffer for the selection region all at once and probe the resulting array.
250 float *aZPtr = theRenderer->GetRenderWindow()->GetZbufferData( mySelection[0],
251 mySelection[1], mySelection[2], mySelection[3] );
253 for ( vtkIdType aPntId = 0; aPntId < aNumPts; aPntId++ ) {
254 // perform conversion
255 double aX[4] = { 1.0, 1.0, 1.0, 1.0 };
256 theInput->GetPoint( aPntId, aX );
259 aMatrix->MultiplyPoint( aX, aView );
260 if ( aView[3] == 0.0 ) continue;
261 theRenderer->SetViewPoint( aView[0] / aView[3], aView[1] / aView[3],
262 aView[2] / aView[3] );
263 theRenderer->ViewToDisplay();
266 theRenderer->GetDisplayPoint( aDX );
268 bool isInSelection = false;
269 if ( theMode == RectangleMode ) isInSelection = aDX[0] >= mySelection[0]
270 && aDX[0] <= mySelection[2] && aDX[1] >= mySelection[1]
271 && aDX[1] <= mySelection[3];
273 if ( theMode == PolygonMode ) isInSelection =
274 isPointInPolygon( QPoint( (int)aDX[0], (int)aDX[1] ), thePoints );
276 // check whether visible and in selection window
277 if ( isInSelection ) {
278 int aDX0 = int( aDX[0] );
279 int aDX1 = int( aDX[1] );
281 int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2], aDX0, aDX1 );
282 if ( aRet > 0 ) goto ADD_VISIBLE;
283 if ( aRet < 0 ) goto ADD_INVISIBLE;
285 static int aMaxRadius = 5;
286 for ( int aRadius = 1; aRadius < aMaxRadius; aRadius++ ) {
287 int aStartDX[2] = { aDX0 - aRadius, aDX1 - aRadius };
288 for ( int i = 0; i <= aRadius; i++ ) {
289 int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
290 aStartDX[0]++, aStartDX[1] );
291 if ( aRet > 0 ) goto ADD_VISIBLE;
292 if ( aRet < 0 ) goto ADD_INVISIBLE;
294 for ( int i = 0; i <= aRadius; i++ ) {
295 int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
296 aStartDX[0], aStartDX[1]++ );
297 if ( aRet > 0 ) goto ADD_VISIBLE;
298 if ( aRet < 0 ) goto ADD_INVISIBLE;
300 for ( int i = 0; i <= aRadius; i++ ) {
301 int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
302 aStartDX[0]--, aStartDX[1] );
303 if ( aRet > 0 ) goto ADD_VISIBLE;
304 if ( aRet < 0 ) goto ADD_INVISIBLE;
306 for ( int i = 0; i <= aRadius; i++ ) {
307 int aRet = Check( aZPtr, mySelection, theTolerance, aDX[2],
308 aStartDX[0], aStartDX[1]-- );
309 if ( aRet > 0 ) goto ADD_VISIBLE;
310 if ( aRet < 0 ) goto ADD_INVISIBLE;
313 if ( false ) ADD_VISIBLE:theVisibleIds.push_back( aPntId );
314 if ( false ) ADD_INVISIBLE:theInVisibleIds.push_back( aPntId );
320 if ( aZPtr ) delete[] aZPtr;
323 void SVTK_AreaPicker::SelectVisibleCells( QVector< QPoint >& thePoints,
324 vtkRenderer *theRenderer, vtkDataSet *theInput,
325 SVTK_AreaPicker::TVectorIds& theVectorIds, double theTolerance,
326 SelectionMode theMode )
328 theVectorIds.clear();
330 vtkIdType aNumCells = theInput->GetNumberOfCells();
331 if ( aNumCells < 1 ) return;
333 theVectorIds.reserve( aNumCells / 2 + 1 );
335 SVTK_AreaPicker::TVectorIds aVisiblePntIds;
336 SVTK_AreaPicker::TVectorIds anInVisiblePntIds;
337 SelectVisiblePoints( thePoints, theRenderer, theInput, aVisiblePntIds,
338 anInVisiblePntIds, theTolerance, theMode );
340 typedef std::set< vtkIdType > TIdsSet;
341 TIdsSet aVisibleIds( aVisiblePntIds.begin(), aVisiblePntIds.end() );
342 TIdsSet anInVisibleIds( anInVisiblePntIds.begin(), anInVisiblePntIds.end() );
344 // Grab the composite perspective transform. This matrix is used to convert
345 // each point to view coordinates. vtkRenderer provides a WorldToView()
346 // method but it computes the composite perspective transform each time
347 // WorldToView() is called. This is expensive, so we get the matrix once
348 // and handle the transformation ourselves.
349 vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
351 theRenderer->GetActiveCamera()->GetCompositeProjectionTransformMatrix(
352 theRenderer->GetTiledAspectRatio(), 0, 1 ) );
354 for ( vtkIdType aCellId = 0; aCellId < aNumCells; aCellId++ ) {
355 vtkCell* aCell = theInput->GetCell( aCellId );
358 aCell->GetBounds( aBounds );
361 GetCenter( aBounds, aCenter );
364 double aX[4] = { aCenter[0], aCenter[1], aCenter[2], 1.0 };
365 aMatrix->MultiplyPoint( aX, aView );
367 if ( aView[3] == 0.0 ) continue;
369 theRenderer->SetViewPoint( aView[0] / aView[3], aView[1] / aView[3],
370 aView[2] / aView[3] );
371 theRenderer->ViewToDisplay();
374 theRenderer->GetDisplayPoint( aDX );
376 bool isInSelection = false;
377 if ( theMode == RectangleMode ) isInSelection = aDX[0] >= mySelection[0]
378 && aDX[0] <= mySelection[2] && aDX[1] >= mySelection[1]
379 && aDX[1] <= mySelection[3];
381 if ( theMode == PolygonMode ) isInSelection =
382 isPointInPolygon( QPoint( (int)aDX[0], (int)aDX[1] ), thePoints );
383 // check whether visible and in selection window
384 if ( isInSelection ) {
385 vtkIdType aNumPts = aCell->GetNumberOfPoints();
386 bool anIsVisible = true;
387 for ( vtkIdType anId = 0; anId < aNumPts; anId++ ) {
388 vtkIdType aPntId = aCell->GetPointId( anId );
389 anIsVisible = aVisibleIds.find( aPntId ) != aVisibleIds.end();
390 if ( !anIsVisible ) break;
392 if ( anIsVisible ) theVectorIds.push_back( aCellId );
397 bool SVTK_AreaPicker::isPointInPolygon( const QPoint& thePoint, const QVector<QPoint>& thePolygon )
400 if ( thePolygon.size() < 3 ) return false;
402 QVector< QPoint >::const_iterator end = thePolygon.end();
403 QPoint last_pt = thePolygon.back();
405 last_pt.setX( last_pt.x() - thePoint.x() );
406 last_pt.setY( last_pt.y() - thePoint.y() );
410 for ( QVector< QPoint >::const_iterator iter = thePolygon.begin();
411 iter != end; ++iter ) {
412 QPoint cur_pt = *iter;
413 cur_pt.setX( cur_pt.x() - thePoint.x() );
414 cur_pt.setY( cur_pt.y() - thePoint.y() );
416 double del = last_pt.x() * cur_pt.y() - cur_pt.x() * last_pt.y();
417 double xy = cur_pt.x() * last_pt.x() + cur_pt.y() * last_pt.y();
421 ( atan( ( last_pt.x() * last_pt.x() + last_pt.y() * last_pt.y() - xy ) / del )
422 + atan( ( cur_pt.x() * cur_pt.x() + cur_pt.y() * cur_pt.y() - xy ) / del ) );
426 return fabs( sum ) > eps;
429 const SVTK_AreaPicker::TVectorIdsMap&
430 SVTK_AreaPicker::GetPointIdsMap() const
432 return myPointIdsMap;
435 const SVTK_AreaPicker::TVectorIdsMap&
436 SVTK_AreaPicker::GetCellIdsMap() const