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