Salome HOME
Merge from BR_PORTING_VTK6 01/03/2013
[modules/gui.git] / src / SVTK / SVTK_RectPicker.cxx
1 // Copyright (C) 2007-2012  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.
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_RectPicker.cxx
25 //  Author : 
26 //  Module : SALOME
27 //
28 #include "SVTK_RectPicker.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 <vtkDataSet.h>
46 #include <vtkPoints.h>
47 #include <vtkCamera.h>
48 #include <vtkCell.h>
49
50 namespace
51 {
52   //----------------------------------------------------------------------------
53   inline
54   double
55   GetZ(float* theZPtr,
56        int theSelection[4],
57        int theDX,
58        int theDY)
59   {
60     return theZPtr[theDX - theSelection[0] + (theDY - theSelection[1])*(theSelection[2] - theSelection[0] + 1)];
61   }
62
63
64   //----------------------------------------------------------------------------
65   inline
66   int
67   Check(float* theZPtr,
68         int theSelection[4],
69         double theTolerance,
70         double theDZ,
71         int theDX,
72         int theDY)
73   {
74     int aRet = 0;
75     double aZ = -1.0;
76     if(theDX >= theSelection[0] && theDX <= theSelection[2] &&
77        theDY >= theSelection[1] && theDY <= theSelection[3])
78     {
79       // Access the value from the captured zbuffer.  Note, we only
80       // captured a portion of the zbuffer, so we need to offset dx by
81       // the selection window.
82       aZ = GetZ(theZPtr,theSelection,theDX,theDY);
83       if(aZ > theTolerance && aZ < 1.0 - theTolerance){
84         aRet = fabs(aZ - theDZ) <= theTolerance;
85       }
86     }
87
88     //cout<<"\tCheck = {"<<theDX<<", "<<theDY<<", "<<theDZ<<", "<<aZ<<"} = "<<aRet<<"\n";
89     return aRet;
90   }
91
92
93   //----------------------------------------------------------------------------
94   void
95   SelectVisiblePoints(int theSelection[4],
96                       vtkRenderer *theRenderer,
97                       vtkDataSet *theInput,
98                       SVTK_RectPicker::TVectorIds& theVisibleIds,
99                       SVTK_RectPicker::TVectorIds& theInVisibleIds,
100                       double theTolerance)
101   {
102     theVisibleIds.clear();
103     theInVisibleIds.clear();
104
105     vtkIdType aNumPts = theInput->GetNumberOfPoints();
106     if(aNumPts < 1)
107       return;
108     
109     theVisibleIds.reserve(aNumPts/2 + 1);
110     theInVisibleIds.reserve(aNumPts/2 + 1);
111
112     // Grab the composite perspective transform.  This matrix is used to convert
113     // each point to view coordinates.  vtkRenderer provides a WorldToView()
114     // method but it computes the composite perspective transform each time
115     // WorldToView() is called.  This is expensive, so we get the matrix once
116     // and handle the transformation ourselves.
117     vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
118     aMatrix->DeepCopy( theRenderer->GetActiveCamera()->
119                        GetCompositeProjectionTransformMatrix( theRenderer->GetTiledAspectRatio(), 0, 1 ) );
120
121     // We grab the z-buffer for the selection region all at once and probe the resulting array.
122     float *aZPtr = theRenderer->GetRenderWindow()->
123       GetZbufferData(theSelection[0], theSelection[1], theSelection[2], theSelection[3]);
124
125     //cout<<"theSelection = {"<<theSelection[0]<<", "<<theSelection[1]<<", "<<theSelection[2]<<", "<<theSelection[3]<<"}\n";
126
127     //cout<<"\t";
128     for(int iX = theSelection[0]; iX <= theSelection[2];  iX++){
129       //cout<<iX<<"\t";
130     }
131     //cout<<endl;
132
133     for(int iY = theSelection[1]; iY <= theSelection[3];  iY++){
134       //cout<<iY<<"\t";
135       for(int iX = theSelection[0]; iX <= theSelection[2];  iX++){
136         //cout<<std::setprecision(4)<<GetZ(aZPtr,theSelection,iX,iY)<<"\t";
137       }
138       //cout<<endl;
139     }
140
141     for(vtkIdType aPntId = 0; aPntId < aNumPts; aPntId++){
142       // perform conversion
143       double aX[4] = {1.0, 1.0, 1.0, 1.0};
144       theInput->GetPoint(aPntId,aX);
145
146       double aView[4];
147       aMatrix->MultiplyPoint(aX,aView);
148       if(aView[3] == 0.0)
149         continue;
150       theRenderer->SetViewPoint(aView[0]/aView[3], 
151                                 aView[1]/aView[3],
152                                 aView[2]/aView[3]);
153       theRenderer->ViewToDisplay();
154
155       double aDX[3];
156       theRenderer->GetDisplayPoint(aDX);
157       
158       // check whether visible and in selection window 
159       if(aDX[0] >= theSelection[0] && aDX[0] <= theSelection[2] &&
160          aDX[1] >= theSelection[1] && aDX[1] <= theSelection[3])
161       {
162         //cout<<"aPntId "<<aPntId<<"; aDX = {"<<aDX[0]<<", "<<aDX[1]<<", "<<aDX[2]<<"}\n";
163         int aDX0 = int(aDX[0]);
164         int aDX1 = int(aDX[1]);
165
166         int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aDX0,aDX1);
167         if(aRet > 0)
168           goto ADD_VISIBLE;
169         if(aRet < 0)
170           goto ADD_INVISIBLE;
171
172         static int aMaxRadius = 5;
173         for(int aRadius = 1; aRadius < aMaxRadius; aRadius++){
174           int aStartDX[2] = {aDX0 - aRadius, aDX1 - aRadius};
175           for(int i = 0; i <= aRadius; i++){
176             int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aStartDX[0]++,aStartDX[1]);
177             if(aRet > 0)
178               goto ADD_VISIBLE;
179             if(aRet < 0)
180               goto ADD_INVISIBLE;
181           }
182           for(int i = 0; i <= aRadius; i++){
183             int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aStartDX[0],aStartDX[1]++);
184             if(aRet > 0)
185               goto ADD_VISIBLE;
186             if(aRet < 0)
187               goto ADD_INVISIBLE;
188           }
189           for(int i = 0; i <= aRadius; i++){
190             int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aStartDX[0]--,aStartDX[1]);
191             if(aRet > 0)
192               goto ADD_VISIBLE;
193             if(aRet < 0)
194               goto ADD_INVISIBLE;
195           }
196           for(int i = 0; i <= aRadius; i++){
197             int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aStartDX[0],aStartDX[1]--);
198             if(aRet > 0)
199               goto ADD_VISIBLE;
200             if(aRet < 0)
201               goto ADD_INVISIBLE;
202           }
203         }
204         if(false)
205           ADD_VISIBLE : theVisibleIds.push_back(aPntId);
206         if(false)
207           ADD_INVISIBLE : theInVisibleIds.push_back(aPntId);
208       }
209     }//for all points
210
211     aMatrix->Delete();
212
213     if(aZPtr)
214       delete [] aZPtr;
215   }
216
217
218   //----------------------------------------------------------------------------
219   inline
220   void
221   GetCenter(const double theBounds[6],
222             double theCenter[3])
223   {
224     theCenter[0] = (theBounds[1] + theBounds[0]) / 2.0;
225     theCenter[1] = (theBounds[3] + theBounds[2]) / 2.0;
226     theCenter[2] = (theBounds[5] + theBounds[4]) / 2.0;
227   }
228
229   void
230   SelectVisibleCells(int theSelection[4],
231                      vtkRenderer *theRenderer,
232                      vtkDataSet *theInput,
233                      SVTK_RectPicker::TVectorIds& theVectorIds,
234                      double theTolerance)
235   {
236     theVectorIds.clear();
237
238     vtkIdType aNumCells = theInput->GetNumberOfCells();
239     if(aNumCells < 1)
240       return;
241     
242     theVectorIds.reserve(aNumCells/2 + 1);
243
244     SVTK_RectPicker::TVectorIds aVisiblePntIds;
245     SVTK_RectPicker::TVectorIds anInVisiblePntIds;
246     SelectVisiblePoints(theSelection,
247                         theRenderer,
248                         theInput,
249                         aVisiblePntIds,
250                         anInVisiblePntIds,
251                         theTolerance);
252
253     typedef std::set<vtkIdType> TIdsSet;
254     TIdsSet aVisibleIds(aVisiblePntIds.begin(),aVisiblePntIds.end());
255     TIdsSet anInVisibleIds(anInVisiblePntIds.begin(),anInVisiblePntIds.end());
256
257     // Grab the composite perspective transform.  This matrix is used to convert
258     // each point to view coordinates.  vtkRenderer provides a WorldToView()
259     // method but it computes the composite perspective transform each time
260     // WorldToView() is called.  This is expensive, so we get the matrix once
261     // and handle the transformation ourselves.
262     vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
263     aMatrix->DeepCopy(theRenderer->GetActiveCamera()->
264                       GetCompositeProjectionTransformMatrix( theRenderer->GetTiledAspectRatio(), 0, 1 ) );
265
266     for(vtkIdType aCellId = 0; aCellId < aNumCells; aCellId++){
267       vtkCell* aCell = theInput->GetCell(aCellId);
268
269       double aBounds[6];
270       aCell->GetBounds(aBounds);
271
272       double aCenter[3];
273       GetCenter(aBounds,aCenter);
274
275       double aView[4];
276       double aX[4] = {aCenter[0], aCenter[1], aCenter[2], 1.0};
277       aMatrix->MultiplyPoint(aX,aView);
278
279       if(aView[3] == 0.0)
280         continue;
281
282       theRenderer->SetViewPoint(aView[0]/aView[3], 
283                                 aView[1]/aView[3],
284                                 aView[2]/aView[3]);
285       theRenderer->ViewToDisplay();
286
287       double aDX[3];
288       theRenderer->GetDisplayPoint(aDX);
289       
290       // check whether visible and in selection window 
291       if(aDX[0] >= theSelection[0] && aDX[0] <= theSelection[2] &&
292          aDX[1] >= theSelection[1] && aDX[1] <= theSelection[3])
293       {
294
295         //cout<<"aCellId = "<<aCellId<<": ";
296         vtkIdType aNumPts = aCell->GetNumberOfPoints();
297         bool anIsVisible = true;
298         for(vtkIdType anId = 0; anId < aNumPts; anId++){
299           vtkIdType aPntId = aCell->GetPointId(anId);
300           //cout<<aPntId<<"; ";
301           anIsVisible = aVisibleIds.find(aPntId) != aVisibleIds.end();
302           if(!anIsVisible)
303             break;
304         }
305         //cout<<"\t"<<anIsVisible<<"\n";
306         if(anIsVisible)
307           theVectorIds.push_back(aCellId);
308       }
309     }//for all parts
310   }
311
312   //----------------------------------------------------------------------------
313   void
314   CalculatePickPosition(vtkRenderer *theRenderer,
315                         double theSelectionX, 
316                         double theSelectionY, 
317                         double theSelectionZ,
318                         double thePickPosition[3])
319   {
320     // Convert the selection point into world coordinates.
321     //
322     theRenderer->SetDisplayPoint(theSelectionX, theSelectionY, theSelectionZ);
323     theRenderer->DisplayToWorld();
324     double* aWorldCoords = theRenderer->GetWorldPoint();
325     if ( aWorldCoords[3] != 0.0 ) {
326       for (int i=0; i < 3; i++) {
327         thePickPosition[i] = aWorldCoords[i] / aWorldCoords[3];
328       }
329     }
330   }
331 }
332
333 vtkStandardNewMacro(SVTK_RectPicker);
334
335 SVTK_RectPicker
336 ::SVTK_RectPicker()
337 {
338   this->Tolerance = 0.005;
339   this->PickPoints = 1;
340 }
341
342 SVTK_RectPicker
343 ::~SVTK_RectPicker()
344 {}
345
346 int
347 SVTK_RectPicker
348 ::Pick(double, 
349        double, 
350        double, 
351        vtkRenderer*)
352 {
353   return 0;
354 }
355
356 int
357 SVTK_RectPicker
358 ::Pick(double theSelection[3], 
359        double theSelection2[3], 
360        vtkRenderer *theRenderer)
361 {
362   return Pick(theSelection[0], theSelection[1], theSelection[2], 
363               theSelection2[0], theSelection2[1], theSelection2[2],
364               theRenderer);
365 }
366
367 int 
368 SVTK_RectPicker
369 ::Pick(double theSelectionX, 
370        double theSelectionY, 
371        double theSelectionZ, 
372        double theSelectionX2, 
373        double theSelectionY2, 
374        double theSelectionZ2,
375        vtkRenderer *theRenderer)
376 {
377   //  Initialize picking process
378   this->Initialize();
379   myCellIdsMap.clear();
380   myPointIdsMap.clear();
381   this->Renderer = theRenderer;
382
383   // Get camera focal point and position. Convert to display (screen) 
384   // coordinates. We need a depth value for z-buffer.
385   //
386   vtkCamera* aCamera = theRenderer->GetActiveCamera();
387
388   double aCameraFP[4];
389   aCamera->GetFocalPoint(aCameraFP); 
390   aCameraFP[3] = 1.0;
391
392   theRenderer->SetWorldPoint(aCameraFP);
393   theRenderer->WorldToDisplay();
394   double* aDisplayCoords = theRenderer->GetDisplayPoint();
395   double aSelectionZ = aDisplayCoords[2];
396
397   this->SelectionPoint[0] = theSelectionX;
398   this->SelectionPoint[1] = theSelectionY;
399   this->SelectionPoint[2] = theSelectionZ;
400
401   // Convert the selection point into world coordinates.
402   //
403   CalculatePickPosition(theRenderer,
404                         theSelectionX,
405                         theSelectionY,
406                         aSelectionZ,
407                         this->PickPosition);
408
409   this->SelectionPoint2[0] = theSelectionX2;
410   this->SelectionPoint2[1] = theSelectionY2;
411   this->SelectionPoint2[2] = theSelectionZ2;
412
413   // Convert the selection point into world coordinates.
414   //
415   CalculatePickPosition(theRenderer,
416                         theSelectionX2,
417                         theSelectionY2,
418                         aSelectionZ,
419                         this->PickPosition2);
420
421   // Invoke start pick method if defined
422   this->InvokeEvent(vtkCommand::StartPickEvent,NULL);
423
424   vtkPropCollection *aProps;
425   if ( this->PickFromList ) 
426     aProps = this->GetPickList();
427   else 
428     aProps = theRenderer->GetViewProps();
429
430   aProps->InitTraversal();
431   while ( vtkProp* aProp = aProps->GetNextProp() ) {
432     aProp->InitPathTraversal();
433     while ( vtkAssemblyPath* aPath = aProp->GetNextPath() ) {
434       vtkMapper *aMapper = NULL;
435       bool anIsPickable = false;
436       vtkActor* anActor = NULL;
437       vtkProp *aPropCandidate = aPath->GetLastNode()->GetViewProp();
438       if ( aPropCandidate->GetPickable() && aPropCandidate->GetVisibility() ) {
439         anIsPickable = true;
440         anActor = vtkActor::SafeDownCast(aPropCandidate);
441         if ( anActor ) {
442           aMapper = anActor->GetMapper();
443           if ( anActor->GetProperty()->GetOpacity() <= 0.0 )
444             anIsPickable = false;
445         }
446       }
447       if ( anIsPickable  &&  aMapper && aMapper->GetInput()) {
448         int aSelectionPoint[4] = {int(theSelectionX),
449                                   int(theSelectionY),
450                                   int(theSelectionX2),
451                                   int(theSelectionY2)};
452         if ( this->PickPoints ) {
453           TVectorIds& aVisibleIds = myPointIdsMap[anActor];
454           TVectorIds anInVisibleIds;
455           SelectVisiblePoints(aSelectionPoint,
456                               theRenderer,
457                               aMapper->GetInput(),
458                               aVisibleIds,
459                               anInVisibleIds,
460                               this->Tolerance);
461           if ( aVisibleIds.empty() ) {
462             myPointIdsMap.erase(myPointIdsMap.find(anActor));
463           }
464         } else {
465           TVectorIds& aVectorIds = myCellIdsMap[anActor];
466           SelectVisibleCells(aSelectionPoint,
467                              theRenderer,
468                              aMapper->GetInput(),
469                              aVectorIds,
470                              this->Tolerance);
471           if ( aVectorIds.empty() ) {
472             myCellIdsMap.erase(myCellIdsMap.find(anActor));
473           }
474         }
475       }
476     }
477   }
478
479   // Invoke end pick method if defined
480   this->InvokeEvent(vtkCommand::EndPickEvent,NULL);
481
482   return myPointIdsMap.empty() || myCellIdsMap.empty();
483 }
484
485
486 const SVTK_RectPicker::TVectorIdsMap& 
487 SVTK_RectPicker
488 ::GetPointIdsMap() const
489 {
490   return myPointIdsMap;
491 }
492
493 const SVTK_RectPicker::TVectorIdsMap& 
494 SVTK_RectPicker
495 ::GetCellIdsMap() const
496 {
497   return myCellIdsMap;
498 }