Salome HOME
Join modifications from branch BR_DEBUG_3_2_0b1
[modules/gui.git] / src / SVTK / SVTK_RectPicker.cxx
1 //  SALOME VTKViewer : build VTK viewer into Salome desktop
2 //
3 //  Copyright (C) 2003  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 //
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   vtkFloatingPointType
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         vtkFloatingPointType theTolerance,
70         vtkFloatingPointType theDZ,
71         int theDX,
72         int theDY)
73   {
74     int aRet = 0;
75     vtkFloatingPointType 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                       vtkFloatingPointType 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                       GetCompositePerspectiveTransformMatrix(1,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     for(int iY = theSelection[1]; iY <= theSelection[3];  iY++){
128       for(int iX = theSelection[0]; iX <= theSelection[2];  iX++){
129         cout<<GetZ(aZPtr,theSelection,iX,iY)<<" ";
130       }
131       cout<<endl;
132     }
133     */
134     for(vtkIdType aPntId = 0; aPntId < aNumPts; aPntId++){
135       // perform conversion
136       vtkFloatingPointType aX[4] = {1.0, 1.0, 1.0, 1.0};
137       theInput->GetPoint(aPntId,aX);
138
139       vtkFloatingPointType aView[4];
140       aMatrix->MultiplyPoint(aX,aView);
141       if(aView[3] == 0.0)
142         continue;
143       theRenderer->SetViewPoint(aView[0]/aView[3], 
144                                 aView[1]/aView[3],
145                                 aView[2]/aView[3]);
146       theRenderer->ViewToDisplay();
147
148       vtkFloatingPointType aDX[3];
149       theRenderer->GetDisplayPoint(aDX);
150       
151       // check whether visible and in selection window 
152       if(aDX[0] >= theSelection[0] && aDX[0] <= theSelection[2] &&
153          aDX[1] >= theSelection[1] && aDX[1] <= theSelection[3])
154       {
155         //cout<<"aPntId "<<aPntId<<"; aDX = {"<<aDX[0]<<", "<<aDX[1]<<", "<<aDX[2]<<"}\n";
156         int aDX0 = int(aDX[0]);
157         int aDX1 = int(aDX[1]);
158
159         int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aDX0,aDX1);
160         if(aRet > 0)
161           goto ADD_VISIBLE;
162         if(aRet < 0)
163           goto ADD_INVISIBLE;
164
165         static int aMaxRadius = 5;
166         for(int aRadius = 1; aRadius < aMaxRadius; aRadius++){
167           int aStartDX[2] = {aDX0 - aRadius, aDX1 - aRadius};
168           for(int i = 0; i <= aRadius; i++){
169             int aRet = Check(aZPtr,theSelection,theTolerance,aDX[2],aStartDX[0]++,aStartDX[1]);
170             if(aRet > 0)
171               goto ADD_VISIBLE;
172             if(aRet < 0)
173               goto ADD_INVISIBLE;
174           }
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         }
197         if(false)
198           ADD_VISIBLE : theVisibleIds.push_back(aPntId);
199         if(false)
200           ADD_INVISIBLE : theInVisibleIds.push_back(aPntId);
201       }
202     }//for all points
203
204     aMatrix->Delete();
205
206     if(aZPtr)
207       delete [] aZPtr;
208   }
209
210
211   //----------------------------------------------------------------------------
212   inline
213   void
214   GetCenter(const vtkFloatingPointType theBounds[6],
215             vtkFloatingPointType theCenter[3])
216   {
217     theCenter[0] = (theBounds[1] + theBounds[0]) / 2.0;
218     theCenter[1] = (theBounds[3] + theBounds[2]) / 2.0;
219     theCenter[2] = (theBounds[5] + theBounds[4]) / 2.0;
220   }
221
222   void
223   SelectVisibleCells(int theSelection[4],
224                      vtkRenderer *theRenderer,
225                      vtkDataSet *theInput,
226                      SVTK_RectPicker::TVectorIds& theVectorIds,
227                      vtkFloatingPointType theTolerance)
228   {
229     theVectorIds.clear();
230
231     vtkIdType aNumCells = theInput->GetNumberOfCells();
232     if(aNumCells < 1)
233       return;
234     
235     theVectorIds.reserve(aNumCells/2 + 1);
236
237     SVTK_RectPicker::TVectorIds aVisiblePntIds;
238     SVTK_RectPicker::TVectorIds anInVisiblePntIds;
239     SelectVisiblePoints(theSelection,
240                         theRenderer,
241                         theInput,
242                         aVisiblePntIds,
243                         anInVisiblePntIds,
244                         theTolerance);
245
246     typedef std::set<vtkIdType> TIdsSet;
247     TIdsSet aVisibleIds(aVisiblePntIds.begin(),aVisiblePntIds.end());
248     TIdsSet anInVisibleIds(anInVisiblePntIds.begin(),anInVisiblePntIds.end());
249
250     // Grab the composite perspective transform.  This matrix is used to convert
251     // each point to view coordinates.  vtkRenderer provides a WorldToView()
252     // method but it computes the composite perspective transform each time
253     // WorldToView() is called.  This is expensive, so we get the matrix once
254     // and handle the transformation ourselves.
255     vtkMatrix4x4 *aMatrix = vtkMatrix4x4::New();
256     aMatrix->DeepCopy(theRenderer->GetActiveCamera()->
257                       GetCompositePerspectiveTransformMatrix(1,0,1));
258
259     for(vtkIdType aCellId = 0; aCellId < aNumCells; aCellId++){
260       vtkCell* aCell = theInput->GetCell(aCellId);
261
262       vtkFloatingPointType aBounds[6];
263       aCell->GetBounds(aBounds);
264
265       vtkFloatingPointType aCenter[3];
266       GetCenter(aBounds,aCenter);
267
268       vtkFloatingPointType aView[4];
269       vtkFloatingPointType aX[4] = {aCenter[0], aCenter[1], aCenter[2], 1.0};
270       aMatrix->MultiplyPoint(aX,aView);
271
272       if(aView[3] == 0.0)
273         continue;
274
275       theRenderer->SetViewPoint(aView[0]/aView[3], 
276                                 aView[1]/aView[3],
277                                 aView[2]/aView[3]);
278       theRenderer->ViewToDisplay();
279
280       vtkFloatingPointType aDX[3];
281       theRenderer->GetDisplayPoint(aDX);
282       
283       // check whether visible and in selection window 
284       if(aDX[0] >= theSelection[0] && aDX[0] <= theSelection[2] &&
285          aDX[1] >= theSelection[1] && aDX[1] <= theSelection[3])
286       {
287
288         //cout<<"aCellId = "<<aCellId<<": ";
289         vtkIdType aNumPts = aCell->GetNumberOfPoints();
290         bool anIsVisible = true;
291         for(vtkIdType anId = 0; anId < aNumPts; anId++){
292           vtkIdType aPntId = aCell->GetPointId(anId);
293           //cout<<aPntId<<"; ";
294           anIsVisible = aVisibleIds.find(aPntId) != aVisibleIds.end();
295           if(!anIsVisible)
296             break;
297         }
298         //cout<<"\t"<<anIsVisible<<"\n";
299         if(anIsVisible)
300           theVectorIds.push_back(aCellId);
301       }
302     }//for all parts
303   }
304
305   //----------------------------------------------------------------------------
306   void
307   CalculatePickPosition(vtkRenderer *theRenderer,
308                         vtkFloatingPointType theSelectionX, 
309                         vtkFloatingPointType theSelectionY, 
310                         vtkFloatingPointType theSelectionZ,
311                         vtkFloatingPointType thePickPosition[3])
312   {
313     // Convert the selection point into world coordinates.
314     //
315     theRenderer->SetDisplayPoint(theSelectionX, theSelectionY, theSelectionZ);
316     theRenderer->DisplayToWorld();
317     vtkFloatingPointType* aWorldCoords = theRenderer->GetWorldPoint();
318     if ( aWorldCoords[3] != 0.0 ) {
319       for (int i=0; i < 3; i++) {
320         thePickPosition[i] = aWorldCoords[i] / aWorldCoords[3];
321       }
322     }
323   }
324 }
325
326 vtkStandardNewMacro(SVTK_RectPicker);
327
328 SVTK_RectPicker
329 ::SVTK_RectPicker()
330 {
331   this->Tolerance = 0.005;
332   this->PickPoints = 1;
333 }
334
335 SVTK_RectPicker
336 ::~SVTK_RectPicker()
337 {}
338
339 int
340 SVTK_RectPicker
341 ::Pick(vtkFloatingPointType, 
342        vtkFloatingPointType, 
343        vtkFloatingPointType, 
344        vtkRenderer*)
345 {
346   return 0;
347 }
348
349 int
350 SVTK_RectPicker
351 ::Pick(vtkFloatingPointType theSelection[3], 
352        vtkFloatingPointType theSelection2[3], 
353        vtkRenderer *theRenderer)
354 {
355   return Pick(theSelection[0], theSelection[1], theSelection[2], 
356               theSelection2[0], theSelection2[1], theSelection2[2],
357               theRenderer);
358 }
359
360 int 
361 SVTK_RectPicker
362 ::Pick(vtkFloatingPointType theSelectionX, 
363        vtkFloatingPointType theSelectionY, 
364        vtkFloatingPointType theSelectionZ, 
365        vtkFloatingPointType theSelectionX2, 
366        vtkFloatingPointType theSelectionY2, 
367        vtkFloatingPointType theSelectionZ2,
368        vtkRenderer *theRenderer)
369 {
370   //  Initialize picking process
371   this->Initialize();
372   myCellIdsMap.clear();
373   myPointIdsMap.clear();
374   this->Renderer = theRenderer;
375
376   // Get camera focal point and position. Convert to display (screen) 
377   // coordinates. We need a depth value for z-buffer.
378   //
379   vtkCamera* aCamera = theRenderer->GetActiveCamera();
380
381   vtkFloatingPointType aCameraFP[4];
382   aCamera->GetFocalPoint(aCameraFP); 
383   aCameraFP[3] = 1.0;
384
385   theRenderer->SetWorldPoint(aCameraFP);
386   theRenderer->WorldToDisplay();
387   vtkFloatingPointType* aDisplayCoords = theRenderer->GetDisplayPoint();
388   vtkFloatingPointType aSelectionZ = aDisplayCoords[2];
389
390   this->SelectionPoint[0] = theSelectionX;
391   this->SelectionPoint[1] = theSelectionY;
392   this->SelectionPoint[2] = theSelectionZ;
393
394   // Convert the selection point into world coordinates.
395   //
396   CalculatePickPosition(theRenderer,
397                         theSelectionX,
398                         theSelectionY,
399                         aSelectionZ,
400                         this->PickPosition);
401
402   this->SelectionPoint2[0] = theSelectionX2;
403   this->SelectionPoint2[1] = theSelectionY2;
404   this->SelectionPoint2[2] = theSelectionZ2;
405
406   // Convert the selection point into world coordinates.
407   //
408   CalculatePickPosition(theRenderer,
409                         theSelectionX2,
410                         theSelectionY2,
411                         aSelectionZ,
412                         this->PickPosition2);
413
414   // Invoke start pick method if defined
415   this->InvokeEvent(vtkCommand::StartPickEvent,NULL);
416
417   vtkPropCollection *aProps;
418   if ( this->PickFromList ) 
419     aProps = this->GetPickList();
420   else 
421     aProps = theRenderer->GetProps();
422
423   aProps->InitTraversal();
424   while ( vtkProp* aProp = aProps->GetNextProp() ) {
425     aProp->InitPathTraversal();
426     while ( vtkAssemblyPath* aPath = aProp->GetNextPath() ) {
427       vtkMapper *aMapper = NULL;
428       bool anIsPickable = false;
429       vtkActor* anActor = NULL;
430       vtkProp *aPropCandidate = aPath->GetLastNode()->GetProp();
431       if ( aPropCandidate->GetPickable() && aPropCandidate->GetVisibility() ) {
432         anIsPickable = true;
433         anActor = vtkActor::SafeDownCast(aPropCandidate);
434         if ( anActor ) {
435           aMapper = anActor->GetMapper();
436           if ( anActor->GetProperty()->GetOpacity() <= 0.0 )
437             anIsPickable = false;
438         }
439       }
440       if ( anIsPickable  &&  aMapper && aMapper->GetInput()) {
441         int aSelectionPoint[4] = {int(theSelectionX),
442                                   int(theSelectionY),
443                                   int(theSelectionX2),
444                                   int(theSelectionY2)};
445         if ( this->PickPoints ) {
446           TVectorIds& aVisibleIds = myPointIdsMap[anActor];
447           TVectorIds anInVisibleIds;
448           SelectVisiblePoints(aSelectionPoint,
449                               theRenderer,
450                               aMapper->GetInput(),
451                               aVisibleIds,
452                               anInVisibleIds,
453                               this->Tolerance);
454           if ( aVisibleIds.empty() ) {
455             myPointIdsMap.erase(myPointIdsMap.find(anActor));
456           }
457         } else {
458           TVectorIds& aVectorIds = myCellIdsMap[anActor];
459           SelectVisibleCells(aSelectionPoint,
460                              theRenderer,
461                              aMapper->GetInput(),
462                              aVectorIds,
463                              this->Tolerance);
464           if ( aVectorIds.empty() ) {
465             myCellIdsMap.erase(myCellIdsMap.find(anActor));
466           }
467         }
468       }
469     }
470   }
471
472   // Invoke end pick method if defined
473   this->InvokeEvent(vtkCommand::EndPickEvent,NULL);
474
475   return myPointIdsMap.empty() || myCellIdsMap.empty();
476 }
477
478
479 const SVTK_RectPicker::TVectorIdsMap& 
480 SVTK_RectPicker
481 ::GetPointIdsMap() const
482 {
483   return myPointIdsMap;
484 }
485
486 const SVTK_RectPicker::TVectorIdsMap& 
487 SVTK_RectPicker
488 ::GetCellIdsMap() const
489 {
490   return myCellIdsMap;
491 }
492