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 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.
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 Bnd_B3f aBox = GetBndBox();
111 if (aBox.IsVoid() || MyView->IsDefined() == Standard_False)
113 return Standard_False;
116 gp_XYZ aMin = aBox.CornerMin();
117 gp_XYZ aMax = aBox.CornerMax();
119 if (!FitMinMax (myCamera, aMin, aMax, theCoef, 0.0, Standard_False))
121 return Standard_False;
128 return Standard_True;
131 //=======================================================================
132 //function : GetBndBox
134 //=======================================================================
136 Bnd_B3f NIS_View::GetBndBox() const
138 // Calculate the 3D bounding box of visible objects
139 // in all interactive contexts
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);
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]));
163 //=======================================================================
164 //function : GetBndBox
166 //=======================================================================
168 void NIS_View::GetBndBox( Standard_Integer& theXMin, Standard_Integer& theXMax,
169 Standard_Integer& theYMin, Standard_Integer& theYMax ) const
171 theXMin = theYMin = 0;
172 theXMax = theYMax = -1;
174 Bnd_B3f aBox = GetBndBox();
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
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;
196 if (aLimp[0] < aLimp[1] && aLimp[2] < aLimp[3])
199 // WindowFit (aLimp[0], aLimp[2], aLimp[1], aLimp[3]);
209 //=======================================================================
210 //function : MyCallback
212 //=======================================================================
214 int NIS_View::MyCallback (Aspect_Drawable /* Window ID */,
216 Aspect_GraphicCallbackStruct* callData /* call data */)
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)
226 const Handle(NIS_View) thisView (static_cast<NIS_View *> (ptrData));
227 NCollection_List<NIS_InteractiveContext *>::Iterator anIter;
229 // Find the bounding box of all displayed objects by summing the boxes stored
230 // in the relevant DrawList instances.
232 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
233 anIter.Value()->GetBox (aBndBox, pView);
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());
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);
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.);
252 #define FRONT_CLIPPING_PLANE (GL_CLIP_PLANE0 + 0)
253 #define BACK_CLIPPING_PLANE (GL_CLIP_PLANE0 + 1)
254 Standard_Real arr[4] = {
260 arr[3] = aBoxExtent + 1.;
261 glClipPlane (BACK_CLIPPING_PLANE, arr);
262 glEnable (BACK_CLIPPING_PLANE);
264 arr[3] = aBoxExtent + 1.;
265 glClipPlane (FRONT_CLIPPING_PLANE, arr);
266 glEnable (FRONT_CLIPPING_PLANE);
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);
280 glEnable(GL_DEPTH_TEST);
281 glDepthFunc(GL_LESS);
283 glClear(GL_DEPTH_BUFFER_BIT);
286 TColStd_MapIteratorOfPackedMapOfInteger anIterM(thisView->myExListId);
287 for (; anIterM.More(); anIterM.Next())
288 if (anIterM.Key() != 0) {
290 glDeleteLists (anIterM.Key(), 5);
292 glDeleteLists (anIterM.Key(), 1);
295 thisView->myExListId.Clear();
297 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
298 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Normal);
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);
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);
313 // draw top objects always above
314 if (thisView->myIsTopHilight == Standard_False) {
315 glDepthFunc(GL_ALWAYS);
318 for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
319 anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Top);
324 //=======================================================================
325 //function : DynamicHilight
327 //=======================================================================
329 void NIS_View::DynamicHilight (const Standard_Integer theX,
330 const Standard_Integer theY)
333 const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
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)
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);
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)))
353 myDynHilighted.Nullify();
356 aSelected->GetDrawer()->SetDynamicHilighted (Standard_True,
358 myDynHilighted = aSelected;
364 //=======================================================================
365 //function : DynamicUnhilight
367 //=======================================================================
369 void NIS_View::DynamicUnhilight(const Handle(NIS_InteractiveObject)& theObj)
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();
381 //=======================================================================
383 //purpose : selection by single click
384 //=======================================================================
386 void NIS_View::Select (const Standard_Integer theX,
387 const Standard_Integer theY,
388 const Standard_Boolean isForceMultiple,
389 const Standard_Boolean theRedraw)
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)
398 myDynHilighted.Nullify();
399 const Handle(NIS_Drawer)& aDrawer = aSelected->GetDrawer();
400 aDrawer->SetDynamicHilighted (Standard_False, aSelected, this);
402 if (theRedraw) Redraw();
405 //=======================================================================
407 //purpose : selection by rectange
408 //=======================================================================
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)
419 Standard_Real anX, anY, aZ;
420 if (theXmin == theXmax || theYmin == theYmax)
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);
427 Convert(theXmin, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
428 const gp_Pnt anEye (anX, anY, aZ);
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);
434 aTrf.SetTransformation (anAx3);
435 const gp_Trsf aTrfInv = aTrf.Inverted();
437 Convert(theXmax, theYmax, anX, anY, aZ); // 3D point for the 3D coordinates
438 gp_XYZ anUpperCorner (anX, anY, aZ);
439 aTrf.Transforms(anUpperCorner);
443 aBoxSel.Add (gp_XYZ(0., 0., -10000.));
444 aBoxSel.Add (anUpperCorner);
446 TColStd_PackedMapOfInteger mapSelected;
447 NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
448 for (; anIterC.More(); anIterC.Next()) {
449 NIS_InteractiveContext * pCtx = anIterC.Value();
451 pCtx->selectObjects (mapSelected, aBoxSel, aTrfInv, aTrf, isFullyIncluded);
452 pCtx->ProcessSelection (mapSelected, isForceMult);
454 if (theRedraw) Redraw();
457 //=======================================================================
459 //purpose : Selection by polygon
460 //=======================================================================
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)
468 if (thePolygon.IsEmpty())
471 Standard_Real anX, anY, aZ;
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);
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);
482 // 3D point for the 3D coordinates
483 const gp_XY &aPl = thePolygon.Last();
485 Convert((Standard_Integer) aPl.X(), (Standard_Integer) aPl.Y(), anX, anY, aZ);
487 // Compute transformation.
488 const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
489 if (anXdir.Modulus() <= gp::Resolution())
494 const gp_Ax3 anAx3 (anEye, aProj, anXdir);
496 aTrf.SetTransformation (anAx3);
498 // Prepare list of 2d points of selection polygon.
499 NCollection_List<gp_XY> aPoints;
500 NCollection_List<gp_XY>::Iterator anIter(thePolygon);
503 for (; anIter.More(); anIter.Next()) {
504 const gp_XY &aP = anIter.Value();
506 Convert((Standard_Integer) aP.X(), (Standard_Integer) aP.Y(), anX, anY, aZ);
507 gp_XYZ aP3d(anX, anY, aZ);
509 aTrf.Transforms(aP3d);
511 gp_XY aP2d(aP3d.X(), aP3d.Y());
513 aPoints.Append(aP2d);
517 TColStd_PackedMapOfInteger mapSelected;
518 NCollection_List<NIS_InteractiveContext *>::Iterator anIterC(myContexts);
520 for (; anIterC.More(); anIterC.Next()) {
521 NIS_InteractiveContext * pCtx = anIterC.Value();
523 pCtx->selectObjects (mapSelected, aPoints, aPolyBox, aTrf, isFullyIncluded);
524 pCtx->ProcessSelection (mapSelected, isForceMult);
527 if (theRedraw) Redraw();
530 //=======================================================================
533 //=======================================================================
535 Handle(NIS_InteractiveObject) NIS_View::Pick (const Standard_Integer theX,
536 const Standard_Integer theY)
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);
546 Convert (theX+1, theY+1, anX, anY, aZ);
547 anOver = ((gp_XYZ(anX, anY, aZ) - anEye.XYZ()) ^ aProj.XYZ()).Modulus() * 1.5;
549 return Pick(anAxis, anOver, Standard_True);
552 //=======================================================================
555 //=======================================================================
557 Handle(NIS_InteractiveObject) NIS_View::Pick
558 (const gp_Ax1& theAxis,
559 const Standard_Real theOver,
560 const Standard_Boolean isOnlySelectable)
562 typedef NCollection_List<NIS_InteractiveContext::DetectedEnt> LstDetected;
563 Standard_Real aDistance (0.1 * RealLast());
564 Handle(NIS_InteractiveObject) aSelected, aTmpSel;
565 LstDetected aDetected;
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,
572 if (aDist < aDistance) {
578 // simple iterating is enough to create list of detected objects
579 // in the order of increasing distance
581 for (LstDetected::Iterator anIt(aDetected); anIt.More(); anIt.Next())
582 myDetected.Append(anIt.Value().PObj);