0021981: Additional callback before redraw procedure
[occt.git] / src / NIS / NIS_View.cxx
1 // File:      NIS_View.cxx
2 // Created:   06.07.07 19:51
3 // Author:    Alexander GRIGORIEV
4 // Copyright: Open Cascade 2007
5
6 #include <NIS_View.hxx>
7 #include <NIS_InteractiveContext.hxx>
8 #include <NIS_InteractiveObject.hxx>
9 #include <gp_Ax1.hxx>
10 #include <Visual3d_View.hxx>
11 #include <Bnd_B2f.hxx>
12 #include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
13 #ifdef WNT
14 #include <Windows.h>
15 #endif
16 #include <GL/gl.h>
17
18 IMPLEMENT_STANDARD_HANDLE  (NIS_View, V3d_OrthographicView)
19 IMPLEMENT_STANDARD_RTTIEXT (NIS_View, V3d_OrthographicView)
20
21 //=======================================================================
22 //function : NIS_View()
23 //purpose  : Constructor
24 //=======================================================================
25
26 NIS_View::NIS_View (const Handle(V3d_Viewer)&    theViewer,
27                     const Handle(Aspect_Window)& theWindow)
28   : V3d_OrthographicView (theViewer), 
29     myIsTopHilight(Standard_False),
30     myDoHilightSelected(Standard_True)
31 {
32   if (!theWindow.IsNull())
33     V3d_View::SetWindow (theWindow, NULL, &MyCallback, this);
34 }
35
36 //=======================================================================
37 //function : SetWindow
38 //purpose  : 
39 //=======================================================================
40
41 void NIS_View::SetWindow(const Handle(Aspect_Window) &theWindow)
42 {
43   V3d_View::SetWindow (theWindow, NULL, &MyCallback, this);
44 }
45
46 // //=======================================================================
47 // //function : ~NIS_View
48 // //purpose  : Destructor
49 // //=======================================================================
50
51 // NIS_View::~NIS_View()
52 // {}
53
54
55 //=======================================================================
56 //function : AddContext
57 //purpose  : 
58 //=======================================================================
59
60 void  NIS_View::AddContext (NIS_InteractiveContext * theCtx)
61 {
62   // Check that the given context is not among already attached contexts
63   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
64   for (; anIter.More(); anIter.Next())
65     if (anIter.Value() == theCtx)
66       break;
67   if (anIter.More() == Standard_False)
68     myContexts.Append (theCtx);
69 }
70
71 //=======================================================================
72 //function : RemoveContext
73 //purpose  : 
74 //=======================================================================
75
76 void NIS_View::RemoveContext (NIS_InteractiveContext * theCtx)
77 {
78   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
79   for (; anIter.More(); anIter.Next())
80     if (anIter.Value() == theCtx) {
81       myContexts.Remove (anIter);
82       break;
83     }
84
85   NCollection_Map<Handle_NIS_Drawer>::Iterator anIterD (theCtx->GetDrawers ());
86   for (; anIterD.More(); anIterD.Next()) {
87     const Handle(NIS_Drawer)& aDrawer = anIterD.Value();
88     if (aDrawer.IsNull() == Standard_False) {
89       aDrawer->UpdateExListId(this);
90     }
91   }
92 }
93
94 //=======================================================================
95 //function : FitAll3d
96 //purpose  : 
97 //=======================================================================
98
99 Standard_Boolean NIS_View::FitAll3d (const Quantity_Coefficient theCoef)
100 {
101   Standard_Boolean aResult(Standard_False);
102   /*
103   Standard_Integer aLimp[4] = { 1000000, -1000000, 1000000, -1000000 };
104   GetBndBox( aLimp[0], aLimp[1], aLimp[2], aLimp[3] );
105   if (aLimp[1] > -1000000 && aLimp[3] > -1000000 &&
106       aLimp[0] < aLimp[1] && aLimp[2] < aLimp[3])
107   {
108     // Scale the view
109     WindowFit (aLimp[0], aLimp[2], aLimp[1], aLimp[3]);
110     aResult = Standard_True;
111   }
112   */
113
114   Bnd_B3f aBox = GetBndBox();
115
116   // Check that the box is not empty
117   if (aBox.IsVoid() == Standard_False && MyView->IsDefined() == Standard_True) {
118     // Convert the 3D box to 2D representation in view coordinates
119     Standard_Real Umin,Umax,Vmin,Vmax,U,V,W;
120     gp_XYZ aCoord;
121
122     const gp_XYZ aCorner[2] = { aBox.CornerMin(), aBox.CornerMax() };
123
124     Standard_Boolean doFit = Standard_True;
125     while (doFit) {
126
127     for (Standard_Integer i = 0; i < 8; i++) {
128       if (i & 0x1) aCoord.SetX (aCorner[0].X());
129       else         aCoord.SetX (aCorner[1].X());
130       if (i & 0x2) aCoord.SetY (aCorner[0].Y());
131       else         aCoord.SetY (aCorner[1].Y());
132       if (i & 0x4) aCoord.SetZ (aCorner[0].Z());
133       else         aCoord.SetZ (aCorner[1].Z());
134
135       MyView->Projects(aCoord.X(), aCoord.Y(), aCoord.Z(), U, V, W);
136       if (i) {
137         Umin = Min(Umin, U); Umax = Max(Umax, U);
138         Vmin = Min(Vmin, V); Vmax = Max(Vmax, V);
139       }
140       else {
141         Umin = Umax = U;
142         Vmin = Vmax = V;
143       }
144     }
145
146     if ( (Umax > Umin) && (Vmax > Vmin) ) {
147       Standard_Real OldUmin,OldUmax,OldVmin,OldVmax;
148       MyViewMapping.WindowLimit(OldUmin, OldVmin, OldUmax, OldVmax);
149       Standard_Real DxvOld = Abs(OldUmax - OldUmin);
150
151       // make a margin
152       Standard_Real Xrp, Yrp, DxvNew, DyvNew;
153
154       DxvNew = Abs(Umax - Umin); DyvNew = Abs(Vmax - Vmin);
155       DxvNew *= (1. + theCoef);
156       DyvNew *= (1. + theCoef);
157
158       Standard_Real aRatio = DxvNew / DxvOld;
159
160       Xrp = (Umin + Umax)/2. ; Yrp = (Vmin + Vmax)/2. ;
161       Umin = Xrp - DxvNew/2. ; Umax = Xrp + DxvNew/2. ;
162       Vmin = Yrp - DyvNew/2. ; Vmax = Yrp + DyvNew/2. ;
163
164       // fit view
165       FitAll(Umin, Vmin, Umax, Vmax);
166
167       // ratio 1e+6 often gives calculation error(s), reduce it
168       // if (aRatio < 1e+6) doFit = Standard_False;
169       if (aRatio < 100) doFit = Standard_False;
170       aResult = Standard_True;
171     }
172     else doFit = Standard_False;
173
174     }
175   }
176
177   return aResult;
178 }
179
180 //=======================================================================
181 //function : GetBndBox
182 //purpose  : 
183 //=======================================================================
184
185 Bnd_B3f NIS_View::GetBndBox() const
186 {
187   // Calculate the 3D bounding box of visible objects
188   // in all interactive contexts
189   Bnd_B3f aBox;
190   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
191   for (; anIterC.More(); anIterC.Next()) {
192     NCollection_Map<Handle_NIS_Drawer>::Iterator anIterD
193       (anIterC.Value()->myDrawers);
194     for (; anIterD.More(); anIterD.Next()) {
195       const Handle(NIS_Drawer)& aDrawer = anIterD.Value();
196       Bnd_B3f aBoxD = aDrawer->GetBox (this);
197       aBox.Add (aBoxD);
198     }
199   }
200
201   // Take the bounding box of AIS objects displayed in the view
202   Standard_Real aVal[6];
203   View()->MinMaxValues(aVal[0], aVal[1], aVal[2], aVal[3], aVal[4], aVal[5]);
204   if (aVal[3] < 0.5 * RealLast()) {
205     aBox.Add (gp_XYZ (aVal[0], aVal[1], aVal[2]));
206     aBox.Add (gp_XYZ (aVal[3], aVal[4], aVal[5]));
207   }
208
209   return aBox;
210 }
211
212 //=======================================================================
213 //function : GetBndBox
214 //purpose  : 
215 //=======================================================================
216
217 void NIS_View::GetBndBox( Standard_Integer& theXMin, Standard_Integer& theXMax, 
218                           Standard_Integer& theYMin, Standard_Integer& theYMax ) const
219 {
220   theXMin = theYMin = 0; 
221   theXMax = theYMax = -1;
222
223   Bnd_B3f aBox = GetBndBox();
224
225   // Check that the box is not empty
226   if (aBox.IsVoid() == Standard_False) {
227     // Convert the 3D box to 2D representation in pixel coordinates
228     gp_XYZ aCoord;
229     Standard_Integer anXp, anYp;
230     const gp_XYZ aCorner[2] = { aBox.CornerMin(), aBox.CornerMax() };
231     Standard_Integer aLimp[4] = { 1000000, -1000000, 1000000, -1000000 };
232     for (Standard_Integer i = 0; i < 8; i++) {
233       if (i & 0x1) aCoord.SetX (aCorner[0].X());
234       else         aCoord.SetX (aCorner[1].X());
235       if (i & 0x2) aCoord.SetY (aCorner[0].Y());
236       else         aCoord.SetY (aCorner[1].Y());
237       if (i & 0x4) aCoord.SetZ (aCorner[0].Z());
238       else         aCoord.SetZ (aCorner[1].Z());
239       Convert( aCoord.X(), aCoord.Y(), aCoord.Z(), anXp, anYp );
240       if (aLimp[0] > anXp) aLimp[0] = anXp;
241       if (aLimp[1] < anXp) aLimp[1] = anXp;
242       if (aLimp[2] > anYp) aLimp[2] = anYp;
243       if (aLimp[3] < anYp) aLimp[3] = anYp;
244     }
245     if (aLimp[0] < aLimp[1] && aLimp[2] < aLimp[3])
246     {
247       // Scale the view
248       // WindowFit (aLimp[0], aLimp[2], aLimp[1], aLimp[3]);  
249       theXMin = aLimp[0];
250       theXMax = aLimp[1];
251       theYMin = aLimp[2];
252       theYMax = aLimp[3];
253     } 
254   }
255 }
256
257
258 //=======================================================================
259 //function : MyCallback
260 //purpose  : 
261 //=======================================================================
262
263 int NIS_View::MyCallback (Aspect_Drawable                /* Window ID */,
264                           void*                          ptrData, 
265                           Aspect_GraphicCallbackStruct*  callData /* call data */)
266 {
267   // Avoid multiple rendering of the scene ( accordingly with update of
268   // callback mechanism, that invokes additional callbacks before
269   // underlay and overlay redrawing with OCC_PRE_REDRAW and OCC_PRE_OVERLAY
270   // bits added to the "reason" value of the callback data structure;
271   // see comments to OCC_REDRAW_ADDITIONAL_CALLBACKS definition )
272   if (callData->reason & OCC_REDRAW_ADDITIONAL_CALLBACKS)
273     return 0;
274   
275   const Handle(NIS_View) thisView (static_cast<NIS_View *> (ptrData));
276   NCollection_List<NIS_InteractiveContext *>::Iterator anIter;
277 #ifdef CLIP
278   // Find the bounding box of all displayed objects by summing the boxes stored
279   // in the relevant DrawList instances.
280   Bnd_B3f aBndBox;
281   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
282     anIter.Value()->GetBox (aBndBox, pView);
283
284   if (aBndBox.IsVoid() == Standard_False) {
285     const gp_XYZ aBoxSize   = 0.5 * (aBndBox.CornerMax() - aBndBox.CornerMin());
286     const gp_XYZ aBoxCenter = 0.5 * (aBndBox.CornerMax() + aBndBox.CornerMin());
287
288     // Find the ray passing through the clicked point in the view window.
289     Standard_Real anX, anY, aZ;
290     thisView->Convert(0, 0, anX, anY, aZ);  // 3D point for the 3D coordinates
291     const gp_Pnt anEye (anX, anY, aZ);
292     thisView->Proj (anX, anY, aZ);  // vector orthogonal to the view plane
293     const gp_Dir aProj (anX, anY, aZ);
294     const gp_Ax1 anAxis (anEye, aProj);
295
296     const Standard_Real aCenterDist = (anEye.XYZ() - aBoxCenter) * aProj.XYZ();
297     const Standard_Real aBoxExtent /*(fabs(aBoxSize.X() * anX) +
298                                     fabs(aBoxSize.Y() * anY) +
299                                     fabs(aBoxSize.Z() * aZ))*/(100.);
300
301 #define FRONT_CLIPPING_PLANE (GL_CLIP_PLANE0 + 0)
302 #define BACK_CLIPPING_PLANE  (GL_CLIP_PLANE0 + 1)
303     Standard_Real arr[4] = {
304       0.0,  /* Nx */
305       0.0,  /* Ny */
306       1.0,  /* Nz */
307       0.
308     };
309     arr[3] = aBoxExtent + 1.;
310     glClipPlane (BACK_CLIPPING_PLANE, arr);
311     glEnable (BACK_CLIPPING_PLANE);
312     arr[2] = -1.0;
313     arr[3] = aBoxExtent + 1.;
314     glClipPlane (FRONT_CLIPPING_PLANE, arr);
315     glEnable (FRONT_CLIPPING_PLANE);
316   }
317 #endif //IS_DISABLED
318
319   GLboolean isDepthWriteMask, isDepthTest;
320   glGetBooleanv(GL_DEPTH_WRITEMASK,&isDepthWriteMask);
321   glGetBooleanv(GL_DEPTH_TEST,&isDepthTest);
322 //   printf ("GlDepthMask=%d; GlDepthTest=%d\n", depthwritemask, depthtest);
323   glDisableClientState(GL_COLOR_ARRAY);
324   glDisableClientState(GL_EDGE_FLAG_ARRAY);
325   glDisableClientState(GL_INDEX_ARRAY);
326   glDisableClientState(GL_NORMAL_ARRAY);
327   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
328   if (!isDepthTest) {
329     glEnable(GL_DEPTH_TEST);
330     glDepthFunc(GL_LESS);
331     glClearDepth(1.);
332     glClear(GL_DEPTH_BUFFER_BIT);
333   }
334
335   TColStd_MapIteratorOfPackedMapOfInteger anIterM(thisView->myExListId);
336   for (; anIterM.More(); anIterM.Next())
337     if (anIterM.Key() != 0) {
338 #ifdef ARRAY_LISTS
339       glDeleteLists (anIterM.Key(), 5);
340 #else
341       glDeleteLists (anIterM.Key(), 1);
342     }
343 #endif
344   thisView->myExListId.Clear();
345
346   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
347     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Normal);
348
349   // #818151 - selected object is hidden by covered unselected one
350   // display hilighted objects always above the rest ones
351   if (thisView->myIsTopHilight == Standard_True) {
352     glDepthFunc(GL_ALWAYS);
353   }
354
355   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
356     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Hilighted);
357   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
358     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_DynHilighted);
359   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
360     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Transparent);
361
362   // draw top objects always above
363   if (thisView->myIsTopHilight == Standard_False) {
364     glDepthFunc(GL_ALWAYS);
365   }
366
367   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
368     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Top);
369
370   return 0;
371 }
372
373 //=======================================================================
374 //function : DynamicHilight
375 //purpose  : 
376 //=======================================================================
377
378 void NIS_View::DynamicHilight  (const Standard_Integer theX,
379                                 const Standard_Integer theY)
380 {
381   myDetected.Clear();
382   const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
383
384   // ASV: if at least one Context returns IsSelectable()==False, 
385   // hilight is canceled, this method returns
386   if (aSelected.IsNull() == Standard_False) {
387     if (aSelected->IsSelectable() == Standard_False)
388       return;
389   }
390   if (aSelected != myDynHilighted) {
391     const Handle(NIS_View) aView (this);
392     if (myDynHilighted.IsNull() == Standard_False)
393       if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
394         myDynHilighted->GetDrawer()->SetDynamicHilighted(Standard_False,
395                                                          myDynHilighted, aView);
396
397     // 30.07.10 - NKV - synchronize behaviour with AIS interactive context (if need)
398     if (aSelected.IsNull() ||
399         (myDoHilightSelected == Standard_False && 
400          aSelected->GetDrawer()->GetContext()->IsSelected(aSelected)))
401     {
402       myDynHilighted.Nullify();
403     }
404     else {
405       aSelected->GetDrawer()->SetDynamicHilighted (Standard_True,
406                                                    aSelected, aView);
407       myDynHilighted = aSelected;
408     }
409     Redraw();
410   }
411 }
412
413 //=======================================================================
414 //function : DynamicUnhilight
415 //purpose  : 
416 //=======================================================================
417
418 void NIS_View::DynamicUnhilight(const Handle_NIS_InteractiveObject& theObj)
419 {
420   if (theObj == myDynHilighted && theObj.IsNull() == Standard_False) {
421     const Handle(NIS_View) aView (this);
422     if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
423       myDynHilighted->GetDrawer()->SetDynamicHilighted (Standard_False,
424                                                         myDynHilighted, aView);
425     myDynHilighted.Nullify();
426     Redraw();
427   }
428 }
429
430 //=======================================================================
431 //function : Select
432 //purpose  : selection by single click
433 //=======================================================================
434
435 void NIS_View::Select (const Standard_Integer theX,
436                        const Standard_Integer theY,
437                        const Standard_Boolean isForceMultiple,
438                        const Standard_Boolean theRedraw)
439 {
440   myDetected.Clear();
441   const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
442   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
443   for (; anIter.More(); anIter.Next())
444     anIter.Value()->ProcessSelection (aSelected, isForceMultiple);
445   if (aSelected == myDynHilighted && aSelected.IsNull() == Standard_False)
446   {
447     myDynHilighted.Nullify();
448     const Handle(NIS_Drawer)& aDrawer = aSelected->GetDrawer();
449     aDrawer->SetDynamicHilighted (Standard_False, aSelected, this);
450   }
451   if (theRedraw) Redraw();
452 }
453
454 //=======================================================================
455 //function : Select
456 //purpose  : selection by rectange
457 //=======================================================================
458
459 void NIS_View::Select (const Standard_Integer  theXmin,
460                        const Standard_Integer  theYmin,
461                        const Standard_Integer  theXmax,
462                        const Standard_Integer  theYmax,
463                        const Standard_Boolean  isForceMult,
464                        const Standard_Boolean  isFullyIncluded,
465                        const Standard_Boolean  theRedraw)
466 {
467   myDetected.Clear();
468   Standard_Real anX, anY, aZ;
469   if (theXmin == theXmax || theYmin == theYmax)
470     return;
471
472   //Transformed box corresponding to the selected rectangle
473   Proj (anX, anY, aZ);                  // vector orthogonal to the view plane
474   const gp_Dir aProj (anX, anY, aZ);
475
476   Convert(theXmin, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
477   const gp_Pnt anEye (anX, anY, aZ);
478
479   Convert(theXmax, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
480   const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
481   const gp_Ax3 anAx3 (anEye, aProj, anXdir);
482   gp_Trsf aTrf;
483   aTrf.SetTransformation (anAx3);
484   const gp_Trsf aTrfInv = aTrf.Inverted();
485
486   Convert(theXmax, theYmax, anX, anY, aZ); // 3D point for the 3D coordinates
487   gp_XYZ anUpperCorner (anX, anY, aZ);
488   aTrf.Transforms(anUpperCorner);
489
490   // Selecting box
491   Bnd_B3f aBoxSel;
492   aBoxSel.Add (gp_XYZ(0., 0., -10000.));
493   aBoxSel.Add (anUpperCorner);
494
495   TColStd_PackedMapOfInteger mapSelected;
496   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
497   for (; anIterC.More(); anIterC.Next()) {
498     NIS_InteractiveContext * pCtx = anIterC.Value();
499     mapSelected.Clear();
500     pCtx->selectObjects (mapSelected, aBoxSel, aTrfInv, aTrf, isFullyIncluded);
501     pCtx->ProcessSelection (mapSelected, isForceMult);
502   }
503   if (theRedraw) Redraw();
504 }
505
506 //=======================================================================
507 //function : Select
508 //purpose  : Selection by polygon
509 //=======================================================================
510
511 void  NIS_View::Select (const NCollection_List<gp_XY> &thePolygon,
512                         const Standard_Boolean         isForceMult,
513                         const Standard_Boolean         isFullyIncluded,
514                         const Standard_Boolean         theRedraw)
515 {
516   myDetected.Clear();
517   if (thePolygon.IsEmpty())
518     return;
519
520   Standard_Real anX, anY, aZ;
521
522   //Transformed box corresponding to the selected rectangle
523   Proj (anX, anY, aZ);                  // vector orthogonal to the view plane
524   const gp_Dir aProj (anX, anY, aZ);
525
526   const gp_XY &aPf = thePolygon.First();
527   // 3D point for the 3D coordinates
528   Convert((Standard_Integer) aPf.X(), (Standard_Integer) aPf.Y(), anX, anY, aZ);
529   const gp_Pnt anEye (anX, anY, aZ);
530
531   // 3D point for the 3D coordinates
532   const gp_XY &aPl = thePolygon.Last();
533
534   Convert((Standard_Integer) aPl.X(), (Standard_Integer) aPl.Y(), anX, anY, aZ);
535
536   // Compute transformation.
537   const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
538   const gp_Ax3 anAx3 (anEye, aProj, anXdir);
539   gp_Trsf aTrf;
540   aTrf.SetTransformation (anAx3);
541   const gp_Trsf aTrfInv = aTrf.Inverted();
542
543   // Prepare list of 2d points of selection polygon.
544   NCollection_List<gp_XY>           aPoints;
545   NCollection_List<gp_XY>::Iterator anIter(thePolygon);
546   Bnd_B2f                           aPolyBox;
547
548   for (; anIter.More(); anIter.Next()) {
549     const gp_XY &aP = anIter.Value();
550
551     Convert((Standard_Integer) aP.X(), (Standard_Integer) aP.Y(), anX, anY, aZ);
552     gp_XYZ aP3d(anX, anY, aZ);
553
554     aTrf.Transforms(aP3d);
555
556     gp_XY aP2d(aP3d.X(), aP3d.Y());
557
558     aPoints.Append(aP2d);
559     aPolyBox.Add(aP2d);
560   }
561
562   TColStd_PackedMapOfInteger                           mapSelected;
563   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC(myContexts);
564
565   for (; anIterC.More(); anIterC.Next()) {
566     NIS_InteractiveContext * pCtx = anIterC.Value();
567     mapSelected.Clear();
568     pCtx->selectObjects (mapSelected, aPoints, aPolyBox, aTrf, isFullyIncluded);
569     pCtx->ProcessSelection (mapSelected, isForceMult);
570   }
571
572   if (theRedraw) Redraw();
573 }
574
575 //=======================================================================
576 //function : Pick
577 //purpose  : 
578 //=======================================================================
579
580 Handle_NIS_InteractiveObject NIS_View::Pick (const Standard_Integer theX,
581                                              const Standard_Integer theY)
582 {
583   // Find the ray passing through the clicked point in the view window.
584   Standard_Real anX, anY, aZ, anOver;
585   Convert(theX, theY, anX, anY, aZ);  // 3D point for the 3D coordinates
586   const gp_Pnt anEye (anX, anY, aZ);
587   Proj (anX, anY, aZ);                // vector orthogonal to the view plane
588   const gp_Dir aProj (-anX, -anY, -aZ);
589   const gp_Ax1 anAxis (anEye, aProj);
590
591   Convert (theX+1, theY+1, anX, anY, aZ);
592   anOver = ((gp_XYZ(anX, anY, aZ) - anEye.XYZ()) ^ aProj.XYZ()).Modulus() * 1.5;
593
594   return Pick(anAxis, anOver, Standard_True);
595 }
596
597 //=======================================================================
598 //function : Pick
599 //purpose  : 
600 //=======================================================================
601
602 Handle_NIS_InteractiveObject NIS_View::Pick
603                                 (const gp_Ax1&          theAxis,
604                                  const Standard_Real    theOver,
605                                  const Standard_Boolean isOnlySelectable)
606 {
607   typedef NCollection_List<NIS_InteractiveContext::DetectedEnt> LstDetected;
608   Standard_Real                 aDistance (0.1 * RealLast());
609   Handle(NIS_InteractiveObject) aSelected, aTmpSel;
610   LstDetected aDetected;
611
612   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
613   for (; anIterC.More(); anIterC.Next()) {
614     const Standard_Real aDist =
615       anIterC.Value()->selectObject (aTmpSel, aDetected, theAxis, theOver,
616                                      isOnlySelectable);
617     if (aDist < aDistance) {
618       aDistance = aDist;
619       aSelected = aTmpSel;
620     }
621   }
622
623   // simple iterating is enough to create list of detected objects
624   // in the order of increasing distance
625   myDetected.Clear();
626   for (LstDetected::Iterator anIt(aDetected); anIt.More(); anIt.Next())
627     myDetected.Append(anIt.Value().PObj);
628
629   return aSelected;
630 }
631