d5d1db976de415cf9c1c1fc686d0b7a6436a08eb
[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*  /* call data */)
266 {
267   const Handle(NIS_View) thisView (static_cast<NIS_View *> (ptrData));
268   NCollection_List<NIS_InteractiveContext *>::Iterator anIter;
269 #ifdef CLIP
270   // Find the bounding box of all displayed objects by summing the boxes stored
271   // in the relevant DrawList instances.
272   Bnd_B3f aBndBox;
273   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
274     anIter.Value()->GetBox (aBndBox, pView);
275
276   if (aBndBox.IsVoid() == Standard_False) {
277     const gp_XYZ aBoxSize   = 0.5 * (aBndBox.CornerMax() - aBndBox.CornerMin());
278     const gp_XYZ aBoxCenter = 0.5 * (aBndBox.CornerMax() + aBndBox.CornerMin());
279
280     // Find the ray passing through the clicked point in the view window.
281     Standard_Real anX, anY, aZ;
282     thisView->Convert(0, 0, anX, anY, aZ);  // 3D point for the 3D coordinates
283     const gp_Pnt anEye (anX, anY, aZ);
284     thisView->Proj (anX, anY, aZ);  // vector orthogonal to the view plane
285     const gp_Dir aProj (anX, anY, aZ);
286     const gp_Ax1 anAxis (anEye, aProj);
287
288     const Standard_Real aCenterDist = (anEye.XYZ() - aBoxCenter) * aProj.XYZ();
289     const Standard_Real aBoxExtent /*(fabs(aBoxSize.X() * anX) +
290                                     fabs(aBoxSize.Y() * anY) +
291                                     fabs(aBoxSize.Z() * aZ))*/(100.);
292
293 #define FRONT_CLIPPING_PLANE (GL_CLIP_PLANE0 + 0)
294 #define BACK_CLIPPING_PLANE  (GL_CLIP_PLANE0 + 1)
295     Standard_Real arr[4] = {
296       0.0,  /* Nx */
297       0.0,  /* Ny */
298       1.0,  /* Nz */
299       0.
300     };
301     arr[3] = aBoxExtent + 1.;
302     glClipPlane (BACK_CLIPPING_PLANE, arr);
303     glEnable (BACK_CLIPPING_PLANE);
304     arr[2] = -1.0;
305     arr[3] = aBoxExtent + 1.;
306     glClipPlane (FRONT_CLIPPING_PLANE, arr);
307     glEnable (FRONT_CLIPPING_PLANE);
308   }
309 #endif //IS_DISABLED
310
311   GLboolean isDepthWriteMask, isDepthTest;
312   glGetBooleanv(GL_DEPTH_WRITEMASK,&isDepthWriteMask);
313   glGetBooleanv(GL_DEPTH_TEST,&isDepthTest);
314 //   printf ("GlDepthMask=%d; GlDepthTest=%d\n", depthwritemask, depthtest);
315   glDisableClientState(GL_COLOR_ARRAY);
316   glDisableClientState(GL_EDGE_FLAG_ARRAY);
317   glDisableClientState(GL_INDEX_ARRAY);
318   glDisableClientState(GL_NORMAL_ARRAY);
319   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
320   if (!isDepthTest) {
321     glEnable(GL_DEPTH_TEST);
322     glDepthFunc(GL_LESS);
323     glClearDepth(1.);
324     glClear(GL_DEPTH_BUFFER_BIT);
325   }
326
327   TColStd_MapIteratorOfPackedMapOfInteger anIterM(thisView->myExListId);
328   for (; anIterM.More(); anIterM.Next())
329     if (anIterM.Key() != 0) {
330 #ifdef ARRAY_LISTS
331       glDeleteLists (anIterM.Key(), 5);
332 #else
333       glDeleteLists (anIterM.Key(), 1);
334     }
335 #endif
336   thisView->myExListId.Clear();
337
338   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
339     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Normal);
340
341   // #818151 - selected object is hidden by covered unselected one
342   // display hilighted objects always above the rest ones
343   if (thisView->myIsTopHilight == Standard_True) {
344     glDepthFunc(GL_ALWAYS);
345   }
346
347   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
348     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Hilighted);
349   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
350     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_DynHilighted);
351   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
352     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Transparent);
353
354   // draw top objects always above
355   if (thisView->myIsTopHilight == Standard_False) {
356     glDepthFunc(GL_ALWAYS);
357   }
358
359   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
360     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Top);
361
362   return 0;
363 }
364
365 //=======================================================================
366 //function : DynamicHilight
367 //purpose  : 
368 //=======================================================================
369
370 void NIS_View::DynamicHilight  (const Standard_Integer theX,
371                                 const Standard_Integer theY)
372 {
373   myDetected.Clear();
374   const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
375
376   // ASV: if at least one Context returns IsSelectable()==False, 
377   // hilight is canceled, this method returns
378   if (aSelected.IsNull() == Standard_False) {
379     if (aSelected->IsSelectable() == Standard_False)
380       return;
381   }
382   if (aSelected != myDynHilighted) {
383     const Handle(NIS_View) aView (this);
384     if (myDynHilighted.IsNull() == Standard_False)
385       if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
386         myDynHilighted->GetDrawer()->SetDynamicHilighted(Standard_False,
387                                                          myDynHilighted, aView);
388
389     // 30.07.10 - NKV - synchronize behaviour with AIS interactive context (if need)
390     if (aSelected.IsNull() ||
391         (myDoHilightSelected == Standard_False && 
392          aSelected->GetDrawer()->GetContext()->IsSelected(aSelected)))
393     {
394       myDynHilighted.Nullify();
395     }
396     else {
397       aSelected->GetDrawer()->SetDynamicHilighted (Standard_True,
398                                                    aSelected, aView);
399       myDynHilighted = aSelected;
400     }
401     Redraw();
402   }
403 }
404
405 //=======================================================================
406 //function : DynamicUnhilight
407 //purpose  : 
408 //=======================================================================
409
410 void NIS_View::DynamicUnhilight(const Handle_NIS_InteractiveObject& theObj)
411 {
412   if (theObj == myDynHilighted && theObj.IsNull() == Standard_False) {
413     const Handle(NIS_View) aView (this);
414     if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
415       myDynHilighted->GetDrawer()->SetDynamicHilighted (Standard_False,
416                                                         myDynHilighted, aView);
417     myDynHilighted.Nullify();
418     Redraw();
419   }
420 }
421
422 //=======================================================================
423 //function : Select
424 //purpose  : selection by single click
425 //=======================================================================
426
427 void NIS_View::Select (const Standard_Integer theX,
428                        const Standard_Integer theY,
429                        const Standard_Boolean isForceMultiple,
430                        const Standard_Boolean theRedraw)
431 {
432   myDetected.Clear();
433   const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
434   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
435   for (; anIter.More(); anIter.Next())
436     anIter.Value()->ProcessSelection (aSelected, isForceMultiple);
437   if (aSelected == myDynHilighted && aSelected.IsNull() == Standard_False)
438   {
439     myDynHilighted.Nullify();
440     const Handle(NIS_Drawer)& aDrawer = aSelected->GetDrawer();
441     aDrawer->SetDynamicHilighted (Standard_False, aSelected, this);
442   }
443   if (theRedraw) Redraw();
444 }
445
446 //=======================================================================
447 //function : Select
448 //purpose  : selection by rectange
449 //=======================================================================
450
451 void NIS_View::Select (const Standard_Integer  theXmin,
452                        const Standard_Integer  theYmin,
453                        const Standard_Integer  theXmax,
454                        const Standard_Integer  theYmax,
455                        const Standard_Boolean  isForceMult,
456                        const Standard_Boolean  isFullyIncluded,
457                        const Standard_Boolean  theRedraw)
458 {
459   myDetected.Clear();
460   Standard_Real anX, anY, aZ;
461   if (theXmin == theXmax || theYmin == theYmax)
462     return;
463
464   //Transformed box corresponding to the selected rectangle
465   Proj (anX, anY, aZ);                  // vector orthogonal to the view plane
466   const gp_Dir aProj (anX, anY, aZ);
467
468   Convert(theXmin, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
469   const gp_Pnt anEye (anX, anY, aZ);
470
471   Convert(theXmax, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
472   const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
473   const gp_Ax3 anAx3 (anEye, aProj, anXdir);
474   gp_Trsf aTrf;
475   aTrf.SetTransformation (anAx3);
476   const gp_Trsf aTrfInv = aTrf.Inverted();
477
478   Convert(theXmax, theYmax, anX, anY, aZ); // 3D point for the 3D coordinates
479   gp_XYZ anUpperCorner (anX, anY, aZ);
480   aTrf.Transforms(anUpperCorner);
481
482   // Selecting box
483   Bnd_B3f aBoxSel;
484   aBoxSel.Add (gp_XYZ(0., 0., -10000.));
485   aBoxSel.Add (anUpperCorner);
486
487   TColStd_PackedMapOfInteger mapSelected;
488   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
489   for (; anIterC.More(); anIterC.Next()) {
490     NIS_InteractiveContext * pCtx = anIterC.Value();
491     mapSelected.Clear();
492     pCtx->selectObjects (mapSelected, aBoxSel, aTrfInv, aTrf, isFullyIncluded);
493     pCtx->ProcessSelection (mapSelected, isForceMult);
494   }
495   if (theRedraw) Redraw();
496 }
497
498 //=======================================================================
499 //function : Select
500 //purpose  : Selection by polygon
501 //=======================================================================
502
503 void  NIS_View::Select (const NCollection_List<gp_XY> &thePolygon,
504                         const Standard_Boolean         isForceMult,
505                         const Standard_Boolean         isFullyIncluded,
506                         const Standard_Boolean         theRedraw)
507 {
508   myDetected.Clear();
509   if (thePolygon.IsEmpty())
510     return;
511
512   Standard_Real anX, anY, aZ;
513
514   //Transformed box corresponding to the selected rectangle
515   Proj (anX, anY, aZ);                  // vector orthogonal to the view plane
516   const gp_Dir aProj (anX, anY, aZ);
517
518   const gp_XY &aPf = thePolygon.First();
519   // 3D point for the 3D coordinates
520   Convert((Standard_Integer) aPf.X(), (Standard_Integer) aPf.Y(), anX, anY, aZ);
521   const gp_Pnt anEye (anX, anY, aZ);
522
523   // 3D point for the 3D coordinates
524   const gp_XY &aPl = thePolygon.Last();
525
526   Convert((Standard_Integer) aPl.X(), (Standard_Integer) aPl.Y(), anX, anY, aZ);
527
528   // Compute transformation.
529   const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
530   const gp_Ax3 anAx3 (anEye, aProj, anXdir);
531   gp_Trsf aTrf;
532   aTrf.SetTransformation (anAx3);
533   const gp_Trsf aTrfInv = aTrf.Inverted();
534
535   // Prepare list of 2d points of selection polygon.
536   NCollection_List<gp_XY>           aPoints;
537   NCollection_List<gp_XY>::Iterator anIter(thePolygon);
538   Bnd_B2f                           aPolyBox;
539
540   for (; anIter.More(); anIter.Next()) {
541     const gp_XY &aP = anIter.Value();
542
543     Convert((Standard_Integer) aP.X(), (Standard_Integer) aP.Y(), anX, anY, aZ);
544     gp_XYZ aP3d(anX, anY, aZ);
545
546     aTrf.Transforms(aP3d);
547
548     gp_XY aP2d(aP3d.X(), aP3d.Y());
549
550     aPoints.Append(aP2d);
551     aPolyBox.Add(aP2d);
552   }
553
554   TColStd_PackedMapOfInteger                           mapSelected;
555   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC(myContexts);
556
557   for (; anIterC.More(); anIterC.Next()) {
558     NIS_InteractiveContext * pCtx = anIterC.Value();
559     mapSelected.Clear();
560     pCtx->selectObjects (mapSelected, aPoints, aPolyBox, aTrf, isFullyIncluded);
561     pCtx->ProcessSelection (mapSelected, isForceMult);
562   }
563
564   if (theRedraw) Redraw();
565 }
566
567 //=======================================================================
568 //function : Pick
569 //purpose  : 
570 //=======================================================================
571
572 Handle_NIS_InteractiveObject NIS_View::Pick (const Standard_Integer theX,
573                                              const Standard_Integer theY)
574 {
575   // Find the ray passing through the clicked point in the view window.
576   Standard_Real anX, anY, aZ, anOver;
577   Convert(theX, theY, anX, anY, aZ);  // 3D point for the 3D coordinates
578   const gp_Pnt anEye (anX, anY, aZ);
579   Proj (anX, anY, aZ);                // vector orthogonal to the view plane
580   const gp_Dir aProj (-anX, -anY, -aZ);
581   const gp_Ax1 anAxis (anEye, aProj);
582
583   Convert (theX+1, theY+1, anX, anY, aZ);
584   anOver = ((gp_XYZ(anX, anY, aZ) - anEye.XYZ()) ^ aProj.XYZ()).Modulus() * 1.5;
585
586   return Pick(anAxis, anOver, Standard_True);
587 }
588
589 //=======================================================================
590 //function : Pick
591 //purpose  : 
592 //=======================================================================
593
594 Handle_NIS_InteractiveObject NIS_View::Pick
595                                 (const gp_Ax1&          theAxis,
596                                  const Standard_Real    theOver,
597                                  const Standard_Boolean isOnlySelectable)
598 {
599   typedef NCollection_List<NIS_InteractiveContext::DetectedEnt> LstDetected;
600   Standard_Real                 aDistance (0.1 * RealLast());
601   Handle(NIS_InteractiveObject) aSelected, aTmpSel;
602   LstDetected aDetected;
603
604   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
605   for (; anIterC.More(); anIterC.Next()) {
606     const Standard_Real aDist =
607       anIterC.Value()->selectObject (aTmpSel, aDetected, theAxis, theOver,
608                                      isOnlySelectable);
609     if (aDist < aDistance) {
610       aDistance = aDist;
611       aSelected = aTmpSel;
612     }
613   }
614
615   // simple iterating is enough to create list of detected objects
616   // in the order of increasing distance
617   myDetected.Clear();
618   for (LstDetected::Iterator anIt(aDetected); anIt.More(); anIt.Next())
619     myDetected.Append(anIt.Value().PObj);
620
621   return aSelected;
622 }
623