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