1 // Created on: 2007-07-06
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2007-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
7 // This library is free software; you can redistribute it and / or modify it
8 // under the terms of the GNU Lesser General Public 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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <NIS_View.hxx>
17 #include <NIS_InteractiveContext.hxx>
18 #include <NIS_InteractiveObject.hxx>
20 #include <Visual3d_View.hxx>
21 #include <Bnd_B2f.hxx>
22 #include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
24 #include <OpenGl_GlCore11.hxx>
26 IMPLEMENT_STANDARD_HANDLE (NIS_View, V3d_View)
27 IMPLEMENT_STANDARD_RTTIEXT (NIS_View, V3d_View)
29 //=======================================================================
30 //function : NIS_View()
31 //purpose : Constructor
32 //=======================================================================
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)
40 if (!theWindow.IsNull())
41 V3d_View::SetWindow (theWindow, NULL, &MyCallback, this);
44 //=======================================================================
45 //function : SetWindow
47 //=======================================================================
49 void NIS_View::SetWindow(const Handle(Aspect_Window) &theWindow)
51 V3d_View::SetWindow (theWindow, NULL, &MyCallback, this);
54 // //=======================================================================
55 // //function : ~NIS_View
56 // //purpose : Destructor
57 // //=======================================================================
59 // NIS_View::~NIS_View()
63 //=======================================================================
64 //function : AddContext
66 //=======================================================================
68 void NIS_View::AddContext (NIS_InteractiveContext * theCtx)
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)
75 if (anIter.More() == Standard_False)
76 myContexts.Append (theCtx);
79 //=======================================================================
80 //function : RemoveContext
82 //=======================================================================
84 void NIS_View::RemoveContext (NIS_InteractiveContext * theCtx)
86 NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
87 for (; anIter.More(); anIter.Next())
88 if (anIter.Value() == theCtx) {
89 myContexts.Remove (anIter);
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);
102 //=======================================================================
103 //function : FitAll3d
105 //=======================================================================
107 Standard_Boolean NIS_View::FitAll3d (const Quantity_Coefficient theCoef)
109 Standard_Boolean aResult(Standard_False);
111 Bnd_B3f aBox = GetBndBox();
113 // Check that the box is not empty
114 if (aBox.IsVoid() == Standard_False && MyView->IsDefined() == Standard_True)
116 // Convert the 3D box to 2D representation in view coordinates
119 const gp_XYZ aCorner[2] = { aBox.CornerMin(), aBox.CornerMax() };
122 const gp_XYZ& aBMin = aCorner[0];
123 const gp_XYZ& aBMax = aCorner[1];
125 gp_Pnt anAABBCenter ((aBMin.X() + aBMax.X()) * 0.5,
126 (aBMin.Y() + aBMax.Y()) * 0.5,
127 (aBMin.Z() + aBMax.Z()) * 0.5);
129 gp_Vec aCenter2AABB (myCamera->Center(), anAABBCenter);
130 gp_Dir aDir = myCamera->Direction();
132 // distance projection onto camera direction
133 Standard_Real aDistToBox = -aCenter2AABB.Dot (aDir);
134 gp_Vec aZShift = gp_Vec (aDir).Reversed().Scaled (aDistToBox);
136 gp_Pnt anEyeBefore = myCamera->Eye();
137 gp_Pnt aCenterBefore = myCamera->Center();
139 myCamera->BeginUpdate();
140 myCamera->SetEye (myCamera->Eye().Translated (aZShift));
141 myCamera->SetCenter (myCamera->Center().Translated (aZShift));
142 myCamera->EndUpdate();
144 Standard_Real Umin = RealLast();
145 Standard_Real Umax = RealFirst();
146 Standard_Real Vmin = RealLast();
147 Standard_Real Vmax = RealFirst();
148 Standard_Real U, V, W;
150 Standard_Boolean doFit = Standard_True;
153 for (Standard_Integer i = 0; i < 8; i++) {
154 if (i & 0x1) aCoord.SetX (aCorner[0].X());
155 else aCoord.SetX (aCorner[1].X());
156 if (i & 0x2) aCoord.SetY (aCorner[0].Y());
157 else aCoord.SetY (aCorner[1].Y());
158 if (i & 0x4) aCoord.SetZ (aCorner[0].Z());
159 else aCoord.SetZ (aCorner[1].Z());
161 MyView->Projects(aCoord.X(), aCoord.Y(), aCoord.Z(), U, V, W);
163 Umin = Min(Umin, U); Umax = Max(Umax, U);
164 Vmin = Min(Vmin, V); Vmax = Max(Vmax, V);
172 if ( (Umax > Umin) && (Vmax > Vmin) )
174 gp_Pnt ViewDims = myCamera->ViewDimensions();
175 Standard_Real DxvOld = ViewDims.X();
177 Standard_Real Xrp, Yrp, DxvNew, DyvNew;
179 DxvNew = Abs(Umax - Umin); DyvNew = Abs(Vmax - Vmin);
180 DxvNew *= (1. + theCoef);
181 DyvNew *= (1. + theCoef);
183 Standard_Real aRatio = DxvNew / DxvOld;
185 Xrp = (Umin + Umax)/2. ; Yrp = (Vmin + Vmax)/2. ;
186 Umin = Xrp - DxvNew/2. ; Umax = Xrp + DxvNew/2. ;
187 Vmin = Yrp - DyvNew/2. ; Vmax = Yrp + DyvNew/2. ;
190 FitAll (Umin, Vmin, Umax, Vmax);
192 // ratio 1e+6 often gives calculation error(s), reduce it
193 // if (aRatio < 1e+6) doFit = Standard_False;
196 doFit = Standard_False;
199 aResult = Standard_True;
203 doFit = Standard_False;
209 myCamera->BeginUpdate();
210 myCamera->SetCenter (aCenterBefore);
211 myCamera->SetEye (anEyeBefore);
212 myCamera->EndUpdate();
219 //=======================================================================
220 //function : GetBndBox
222 //=======================================================================
224 Bnd_B3f NIS_View::GetBndBox() const
226 // Calculate the 3D bounding box of visible objects
227 // in all interactive contexts
229 NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
230 for (; anIterC.More(); anIterC.Next()) {
231 NCollection_Map<Handle_NIS_Drawer>::Iterator anIterD
232 (anIterC.Value()->myDrawers);
233 for (; anIterD.More(); anIterD.Next()) {
234 const Handle(NIS_Drawer)& aDrawer = anIterD.Value();
235 Bnd_B3f aBoxD = aDrawer->GetBox (this);
240 // Take the bounding box of AIS objects displayed in the view
241 Standard_Real aVal[6];
242 View()->MinMaxValues(aVal[0], aVal[1], aVal[2], aVal[3], aVal[4], aVal[5]);
243 if (aVal[3] < 0.5 * RealLast()) {
244 aBox.Add (gp_XYZ (aVal[0], aVal[1], aVal[2]));
245 aBox.Add (gp_XYZ (aVal[3], aVal[4], aVal[5]));
251 //=======================================================================
252 //function : GetBndBox
254 //=======================================================================
256 void NIS_View::GetBndBox( Standard_Integer& theXMin, Standard_Integer& theXMax,
257 Standard_Integer& theYMin, Standard_Integer& theYMax ) const
259 theXMin = theYMin = 0;
260 theXMax = theYMax = -1;
262 Bnd_B3f aBox = GetBndBox();
264 // Check that the box is not empty
265 if (aBox.IsVoid() == Standard_False) {
266 // Convert the 3D box to 2D representation in pixel coordinates
268 Standard_Integer anXp, anYp;
269 const gp_XYZ aCorner[2] = { aBox.CornerMin(), aBox.CornerMax() };
270 Standard_Integer aLimp[4] = { 1000000, -1000000, 1000000, -1000000 };
271 for (Standard_Integer i = 0; i < 8; i++) {
272 if (i & 0x1) aCoord.SetX (aCorner[0].X());
273 else aCoord.SetX (aCorner[1].X());
274 if (i & 0x2) aCoord.SetY (aCorner[0].Y());
275 else aCoord.SetY (aCorner[1].Y());
276 if (i & 0x4) aCoord.SetZ (aCorner[0].Z());
277 else aCoord.SetZ (aCorner[1].Z());
278 Convert( aCoord.X(), aCoord.Y(), aCoord.Z(), anXp, anYp );
279 if (aLimp[0] > anXp) aLimp[0] = anXp;
280 if (aLimp[1] < anXp) aLimp[1] = anXp;
281 if (aLimp[2] > anYp) aLimp[2] = anYp;
282 if (aLimp[3] < anYp) aLimp[3] = anYp;
284 if (aLimp[0] < aLimp[1] && aLimp[2] < aLimp[3])
287 // WindowFit (aLimp[0], aLimp[2], aLimp[1], aLimp[3]);
297 //=======================================================================
298 //function : MyCallback
300 //=======================================================================
302 int NIS_View::MyCallback (Aspect_Drawable /* Window ID */,
304 Aspect_GraphicCallbackStruct* callData /* call data */)
306 // Avoid multiple rendering of the scene ( accordingly with update of
307 // callback mechanism, that invokes additional callbacks before
308 // underlay and overlay redrawing with OCC_PRE_REDRAW and OCC_PRE_OVERLAY
309 // bits added to the "reason" value of the callback data structure;
310 // see comments to OCC_REDRAW_ADDITIONAL_CALLBACKS definition )
311 if (callData->reason & OCC_REDRAW_ADDITIONAL_CALLBACKS)
314 const Handle(NIS_View) thisView (static_cast<NIS_View *> (ptrData));
315 NCollection_List<NIS_InteractiveContext *>::Iterator anIter;
317 // Find the bounding box of all displayed objects by summing the boxes stored
318 // in the relevant DrawList instances.
320 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
321 anIter.Value()->GetBox (aBndBox, pView);
323 if (aBndBox.IsVoid() == Standard_False) {
324 const gp_XYZ aBoxSize = 0.5 * (aBndBox.CornerMax() - aBndBox.CornerMin());
325 const gp_XYZ aBoxCenter = 0.5 * (aBndBox.CornerMax() + aBndBox.CornerMin());
327 // Find the ray passing through the clicked point in the view window.
328 Standard_Real anX, anY, aZ;
329 thisView->Convert(0, 0, anX, anY, aZ); // 3D point for the 3D coordinates
330 const gp_Pnt anEye (anX, anY, aZ);
331 thisView->Proj (anX, anY, aZ); // vector orthogonal to the view plane
332 const gp_Dir aProj (anX, anY, aZ);
333 const gp_Ax1 anAxis (anEye, aProj);
335 const Standard_Real aCenterDist = (anEye.XYZ() - aBoxCenter) * aProj.XYZ();
336 const Standard_Real aBoxExtent /*(fabs(aBoxSize.X() * anX) +
337 fabs(aBoxSize.Y() * anY) +
338 fabs(aBoxSize.Z() * aZ))*/(100.);
340 #define FRONT_CLIPPING_PLANE (GL_CLIP_PLANE0 + 0)
341 #define BACK_CLIPPING_PLANE (GL_CLIP_PLANE0 + 1)
342 Standard_Real arr[4] = {
348 arr[3] = aBoxExtent + 1.;
349 glClipPlane (BACK_CLIPPING_PLANE, arr);
350 glEnable (BACK_CLIPPING_PLANE);
352 arr[3] = aBoxExtent + 1.;
353 glClipPlane (FRONT_CLIPPING_PLANE, arr);
354 glEnable (FRONT_CLIPPING_PLANE);
358 GLboolean isDepthWriteMask, isDepthTest;
359 glGetBooleanv(GL_DEPTH_WRITEMASK,&isDepthWriteMask);
360 glGetBooleanv(GL_DEPTH_TEST,&isDepthTest);
361 // printf ("GlDepthMask=%d; GlDepthTest=%d\n", depthwritemask, depthtest);
362 glDisableClientState(GL_COLOR_ARRAY);
363 glDisableClientState(GL_EDGE_FLAG_ARRAY);
364 glDisableClientState(GL_INDEX_ARRAY);
365 glDisableClientState(GL_NORMAL_ARRAY);
366 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
368 glEnable(GL_DEPTH_TEST);
369 glDepthFunc(GL_LESS);
371 glClear(GL_DEPTH_BUFFER_BIT);
374 TColStd_MapIteratorOfPackedMapOfInteger anIterM(thisView->myExListId);
375 for (; anIterM.More(); anIterM.Next())
376 if (anIterM.Key() != 0) {
378 glDeleteLists (anIterM.Key(), 5);
380 glDeleteLists (anIterM.Key(), 1);
383 thisView->myExListId.Clear();
385 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
386 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Normal);
388 // #818151 - selected object is hidden by covered unselected one
389 // display hilighted objects always above the rest ones
390 if (thisView->myIsTopHilight == Standard_True) {
391 glDepthFunc(GL_ALWAYS);
394 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
395 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Hilighted);
396 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
397 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_DynHilighted);
398 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
399 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Transparent);
401 // draw top objects always above
402 if (thisView->myIsTopHilight == Standard_False) {
403 glDepthFunc(GL_ALWAYS);
406 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
407 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Top);
412 //=======================================================================
413 //function : DynamicHilight
415 //=======================================================================
417 void NIS_View::DynamicHilight (const Standard_Integer theX,
418 const Standard_Integer theY)
421 const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
423 // ASV: if at least one Context returns IsSelectable()==False,
424 // hilight is canceled, this method returns
425 if (aSelected.IsNull() == Standard_False) {
426 if (aSelected->IsSelectable() == Standard_False)
429 if (aSelected != myDynHilighted) {
430 const Handle(NIS_View) aView (this);
431 if (myDynHilighted.IsNull() == Standard_False)
432 if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
433 myDynHilighted->GetDrawer()->SetDynamicHilighted(Standard_False,
434 myDynHilighted, aView);
436 // 30.07.10 - NKV - synchronize behaviour with AIS interactive context (if need)
437 if (aSelected.IsNull() ||
438 (myDoHilightSelected == Standard_False &&
439 aSelected->GetDrawer()->GetContext()->IsSelected(aSelected)))
441 myDynHilighted.Nullify();
444 aSelected->GetDrawer()->SetDynamicHilighted (Standard_True,
446 myDynHilighted = aSelected;
452 //=======================================================================
453 //function : DynamicUnhilight
455 //=======================================================================
457 void NIS_View::DynamicUnhilight(const Handle_NIS_InteractiveObject& theObj)
459 if (theObj == myDynHilighted && theObj.IsNull() == Standard_False) {
460 const Handle(NIS_View) aView (this);
461 if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
462 myDynHilighted->GetDrawer()->SetDynamicHilighted (Standard_False,
463 myDynHilighted, aView);
464 myDynHilighted.Nullify();
469 //=======================================================================
471 //purpose : selection by single click
472 //=======================================================================
474 void NIS_View::Select (const Standard_Integer theX,
475 const Standard_Integer theY,
476 const Standard_Boolean isForceMultiple,
477 const Standard_Boolean theRedraw)
480 const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
481 NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
482 for (; anIter.More(); anIter.Next())
483 anIter.Value()->ProcessSelection (aSelected, isForceMultiple);
484 if (aSelected == myDynHilighted && aSelected.IsNull() == Standard_False)
486 myDynHilighted.Nullify();
487 const Handle(NIS_Drawer)& aDrawer = aSelected->GetDrawer();
488 aDrawer->SetDynamicHilighted (Standard_False, aSelected, this);
490 if (theRedraw) Redraw();
493 //=======================================================================
495 //purpose : selection by rectange
496 //=======================================================================
498 void NIS_View::Select (const Standard_Integer theXmin,
499 const Standard_Integer theYmin,
500 const Standard_Integer theXmax,
501 const Standard_Integer theYmax,
502 const Standard_Boolean isForceMult,
503 const Standard_Boolean isFullyIncluded,
504 const Standard_Boolean theRedraw)
507 Standard_Real anX, anY, aZ;
508 if (theXmin == theXmax || theYmin == theYmax)
511 //Transformed box corresponding to the selected rectangle
512 Proj (anX, anY, aZ); // vector orthogonal to the view plane
513 const gp_Dir aProj (anX, anY, aZ);
515 Convert(theXmin, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
516 const gp_Pnt anEye (anX, anY, aZ);
518 Convert(theXmax, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
519 const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
520 const gp_Ax3 anAx3 (anEye, aProj, anXdir);
522 aTrf.SetTransformation (anAx3);
523 const gp_Trsf aTrfInv = aTrf.Inverted();
525 Convert(theXmax, theYmax, anX, anY, aZ); // 3D point for the 3D coordinates
526 gp_XYZ anUpperCorner (anX, anY, aZ);
527 aTrf.Transforms(anUpperCorner);
531 aBoxSel.Add (gp_XYZ(0., 0., -10000.));
532 aBoxSel.Add (anUpperCorner);
534 TColStd_PackedMapOfInteger mapSelected;
535 NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
536 for (; anIterC.More(); anIterC.Next()) {
537 NIS_InteractiveContext * pCtx = anIterC.Value();
539 pCtx->selectObjects (mapSelected, aBoxSel, aTrfInv, aTrf, isFullyIncluded);
540 pCtx->ProcessSelection (mapSelected, isForceMult);
542 if (theRedraw) Redraw();
545 //=======================================================================
547 //purpose : Selection by polygon
548 //=======================================================================
550 void NIS_View::Select (const NCollection_List<gp_XY> &thePolygon,
551 const Standard_Boolean isForceMult,
552 const Standard_Boolean isFullyIncluded,
553 const Standard_Boolean theRedraw)
556 if (thePolygon.IsEmpty())
559 Standard_Real anX, anY, aZ;
561 //Transformed box corresponding to the selected rectangle
562 Proj (anX, anY, aZ); // vector orthogonal to the view plane
563 const gp_Dir aProj (anX, anY, aZ);
565 const gp_XY &aPf = thePolygon.First();
566 // 3D point for the 3D coordinates
567 Convert((Standard_Integer) aPf.X(), (Standard_Integer) aPf.Y(), anX, anY, aZ);
568 const gp_Pnt anEye (anX, anY, aZ);
570 // 3D point for the 3D coordinates
571 const gp_XY &aPl = thePolygon.Last();
573 Convert((Standard_Integer) aPl.X(), (Standard_Integer) aPl.Y(), anX, anY, aZ);
575 // Compute transformation.
576 const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
577 if (anXdir.Modulus() <= gp::Resolution())
582 const gp_Ax3 anAx3 (anEye, aProj, anXdir);
584 aTrf.SetTransformation (anAx3);
586 // Prepare list of 2d points of selection polygon.
587 NCollection_List<gp_XY> aPoints;
588 NCollection_List<gp_XY>::Iterator anIter(thePolygon);
591 for (; anIter.More(); anIter.Next()) {
592 const gp_XY &aP = anIter.Value();
594 Convert((Standard_Integer) aP.X(), (Standard_Integer) aP.Y(), anX, anY, aZ);
595 gp_XYZ aP3d(anX, anY, aZ);
597 aTrf.Transforms(aP3d);
599 gp_XY aP2d(aP3d.X(), aP3d.Y());
601 aPoints.Append(aP2d);
605 TColStd_PackedMapOfInteger mapSelected;
606 NCollection_List<NIS_InteractiveContext *>::Iterator anIterC(myContexts);
608 for (; anIterC.More(); anIterC.Next()) {
609 NIS_InteractiveContext * pCtx = anIterC.Value();
611 pCtx->selectObjects (mapSelected, aPoints, aPolyBox, aTrf, isFullyIncluded);
612 pCtx->ProcessSelection (mapSelected, isForceMult);
615 if (theRedraw) Redraw();
618 //=======================================================================
621 //=======================================================================
623 Handle_NIS_InteractiveObject NIS_View::Pick (const Standard_Integer theX,
624 const Standard_Integer theY)
626 // Find the ray passing through the clicked point in the view window.
627 Standard_Real anX, anY, aZ, anOver;
628 Convert(theX, theY, anX, anY, aZ); // 3D point for the 3D coordinates
629 const gp_Pnt anEye (anX, anY, aZ);
630 Proj (anX, anY, aZ); // vector orthogonal to the view plane
631 const gp_Dir aProj (-anX, -anY, -aZ);
632 const gp_Ax1 anAxis (anEye, aProj);
634 Convert (theX+1, theY+1, anX, anY, aZ);
635 anOver = ((gp_XYZ(anX, anY, aZ) - anEye.XYZ()) ^ aProj.XYZ()).Modulus() * 1.5;
637 return Pick(anAxis, anOver, Standard_True);
640 //=======================================================================
643 //=======================================================================
645 Handle_NIS_InteractiveObject NIS_View::Pick
646 (const gp_Ax1& theAxis,
647 const Standard_Real theOver,
648 const Standard_Boolean isOnlySelectable)
650 typedef NCollection_List<NIS_InteractiveContext::DetectedEnt> LstDetected;
651 Standard_Real aDistance (0.1 * RealLast());
652 Handle(NIS_InteractiveObject) aSelected, aTmpSel;
653 LstDetected aDetected;
655 NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
656 for (; anIterC.More(); anIterC.Next()) {
657 const Standard_Real aDist =
658 anIterC.Value()->selectObject (aTmpSel, aDetected, theAxis, theOver,
660 if (aDist < aDistance) {
666 // simple iterating is enough to create list of detected objects
667 // in the order of increasing distance
669 for (LstDetected::Iterator anIt(aDetected); anIt.More(); anIt.Next())
670 myDetected.Append(anIt.Value().PObj);