Salome HOME
Feature #86: The hierarchy in the Object Browser (T 19).
[modules/hydro.git] / src / HYDROGUI / HYDROGUI_Shape.cxx
1 // Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include "HYDROGUI_Shape.h"
24
25 #include "HYDROGUI_DataObject.h"
26 #include "HYDROGUI_Tool.h"
27
28 #include <AIS_Drawer.hxx>
29 #include <AIS_TexturedShape.hxx>
30
31 #include <BRepBuilderAPI_MakeEdge.hxx>
32 #include <BRepBuilderAPI_MakeWire.hxx>
33 #include <BRepBuilderAPI_MakeFace.hxx>
34
35 #include <gp_Pnt.hxx>
36
37 #include <Graphic3d_AspectFillArea3d.hxx>
38 #include <Graphic3d_MaterialAspect.hxx>
39
40 #include <HYDROData_Document.h>
41 #include <HYDROData_Image.h>
42 #include <HYDROData_ImmersibleZone.h>
43 #include <HYDROData_Polyline.h>
44 #include <HYDROData_Region.h>
45 #include <HYDROData_Zone.h>
46 #include <HYDROData_Obstacle.h>
47
48 #include <TopoDS.hxx>
49 #include <TopoDS_Wire.hxx>
50 #include <TopoDS_Face.hxx>
51
52 #include <TopExp_Explorer.hxx>
53
54 #include <BRep_Builder.hxx>
55
56 #include <Precision.hxx>
57
58 #include <Prs3d_ShadingAspect.hxx>
59 #include <Prs3d_LineAspect.hxx>
60 #include <Prs3d_IsoAspect.hxx>
61
62 #include <QColor>
63 #include <QFile>
64
65 HYDROGUI_Shape::HYDROGUI_Shape( const Handle(AIS_InteractiveContext)& theContext,
66                                 const Handle(HYDROData_Entity)&       theObject )
67 : myContext( theContext ),
68   myObject( theObject ),
69   myIsHighlight( false ),
70   myFillingColor( Qt::transparent ),
71   myBorderColor( Qt::black ),
72   myHighlightColor( Qt::white ),
73   myIsToUpdate( false ),
74   myIsVisible( true ),
75   myDisplayMode( AIS_WireFrame )
76 {
77 }
78
79 HYDROGUI_Shape::~HYDROGUI_Shape()
80 {
81   erase();
82
83   if ( !myShape.IsNull() )
84     myShape.Nullify();
85
86   removeTextureFile();
87 }
88
89 void HYDROGUI_Shape::display( const bool theIsUpdateViewer )
90 {
91   if ( myContext.IsNull() || myShape.IsNull() || !isVisible() )
92     return;
93
94   myContext->Display( myShape, theIsUpdateViewer );
95 }
96
97 void HYDROGUI_Shape::erase( const bool theIsUpdateViewer )
98 {
99   if ( myContext.IsNull() || myShape.IsNull() )
100     return;
101
102   myContext->Erase( myShape, theIsUpdateViewer );
103 }
104
105 void HYDROGUI_Shape::update( const bool theIsUpdateViewer )
106 {
107   setIsToUpdate( false );
108
109   if ( myContext.IsNull() )
110     return;
111
112   // Try to retrieve information from object
113   if ( !myObject.IsNull() )
114   {
115     Handle(HYDROData_Document) aDocument = HYDROData_Document::Document( myObject->Label() );
116   
117     if ( myObject->IsKind( STANDARD_TYPE(HYDROData_ImmersibleZone) ) )
118     {
119       Handle(HYDROData_ImmersibleZone) aZoneObj =
120         Handle(HYDROData_ImmersibleZone)::DownCast( myObject );
121
122       TopoDS_Shape aZoneShape = aZoneObj->GetTopShape();
123       if ( !aZoneShape.IsNull() ) {
124         if ( aZoneShape.ShapeType() == TopAbs_FACE ) {
125           TopoDS_Face aZoneFace = TopoDS::Face( aZoneShape );
126           setFace( aZoneFace, false, false );
127         } else {
128           myTopoShape = aZoneShape;
129           myDisplayMode = myTextureFileName.isEmpty() ? AIS_Shaded : AIS_Shaded+2;
130
131           buildShape();
132           updateShape( false, false );
133         }
134       }
135
136       QColor aFillingColor = aZoneObj->GetFillingColor();
137       QColor aBorderColor = aZoneObj->GetBorderColor();
138
139       setFillingColor( aFillingColor, false, false );
140       setBorderColor( aBorderColor, false, false );
141     }
142     else if ( myObject->IsKind( STANDARD_TYPE(HYDROData_Polyline) ) )
143     {
144       Handle(HYDROData_Polyline) aPolyline =
145         Handle(HYDROData_Polyline)::DownCast( myObject );
146
147       TopoDS_Shape aPolylineShape = aPolyline->GetTopShape();
148
149       if ( !aPolylineShape.IsNull() ) {
150         if ( aPolylineShape.ShapeType() == TopAbs_WIRE ) {
151           TopoDS_Wire aPolylineWire = TopoDS::Wire( aPolylineShape );
152           setWire( aPolylineWire, false, false );  
153         } else {
154           myTopoShape = aPolylineShape;
155           myDisplayMode = AIS_WireFrame;
156
157           buildShape();
158           updateShape( false, false );
159         }
160       }
161     }
162     else if ( myObject->IsKind( STANDARD_TYPE(HYDROData_Zone) ) )
163     {
164       Handle(HYDROData_Zone) aZone =
165         Handle(HYDROData_Zone)::DownCast( myObject );
166
167       TopoDS_Face aZoneFace = TopoDS::Face( aZone->GetShape() );
168
169       // Generate the filling color for zone
170       QStringList aGeomObjectsNames;
171
172       HYDROData_SequenceOfObjects aRefObjects = aZone->GetGeometryObjects();
173       HYDROData_SequenceOfObjects::Iterator anIter( aRefObjects );
174       for ( ; anIter.More(); anIter.Next() )
175       {
176         Handle(HYDROData_Object) aRefbject = 
177           Handle(HYDROData_Object)::DownCast( anIter.Value() );
178         if ( aRefbject.IsNull() )
179           continue;
180
181         QString aRefObjectName = aRefbject->GetName();
182         if ( aRefObjectName.isEmpty() )
183           continue;
184
185         aGeomObjectsNames.append( aRefObjectName );
186       }
187        
188       setFace( aZoneFace, false, false );
189       if (aZone->IsMergingNeed() && aZone->GetMergeType() == HYDROData_Zone::Merge_UNKNOWN )
190       {
191         // Red color for a zone with bathymetry conflict
192         setFillingColor( Qt::red );
193       }
194       else
195       {
196         setFillingColor( HYDROGUI_Tool::GenerateFillingColor( aDocument, aGeomObjectsNames ) );
197       }
198     }
199     else if ( myObject->IsKind( STANDARD_TYPE(HYDROData_Image) ) )
200     {
201       Handle(HYDROData_Image) anImageObj =
202         Handle(HYDROData_Image)::DownCast( myObject );
203
204       removeTextureFile();
205
206       QString aTextureFileName = generateTextureFileName( anImageObj );
207
208       QImage anImage = anImageObj->Image();
209       QString aFilePath = anImageObj->GetFilePath();
210       QTransform aTrsf = anImageObj->Trsf();
211
212       int aWidth = anImage.width();
213       int aHeight = anImage.height();
214
215       QTransform anInversion = QTransform::fromScale( -1, -1 );
216       anImage = anImage.transformed( anInversion * aTrsf, Qt::SmoothTransformation );
217
218       // temporary optimization, to reduce the saved image size (and the texture quality)
219       QImage anImageToSave = reduceTexture( anImage, 500 );
220       anImageToSave.save( aTextureFileName );
221
222       QPointF aPoint1( 0, 0 );
223       QPointF aPoint2( aWidth, 0 );
224       QPointF aPoint3( aWidth, aHeight );
225       QPointF aPoint4( 0, aHeight );
226
227       aPoint1 = aTrsf.map( aPoint1 );
228       aPoint2 = aTrsf.map( aPoint2 );
229       aPoint3 = aTrsf.map( aPoint3 );
230       aPoint4 = aTrsf.map( aPoint4 );
231
232       QPolygonF aPolygon = QPolygonF() << aPoint1 << aPoint2 << aPoint3 << aPoint4;
233       QRectF aRect = aPolygon.boundingRect();
234
235       gp_Pnt aPnt1( aRect.topLeft().x(), aRect.topLeft().y(), 0 );
236       gp_Pnt aPnt2( aRect.topRight().x(), aRect.topRight().y(), 0 );
237       gp_Pnt aPnt3( aRect.bottomRight().x(), aRect.bottomRight().y(), 0 );
238       gp_Pnt aPnt4( aRect.bottomLeft().x(), aRect.bottomLeft().y(), 0 );
239
240       TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge( aPnt1, aPnt2 ).Edge();
241       TopoDS_Edge anEdge2 = BRepBuilderAPI_MakeEdge( aPnt2, aPnt3 ).Edge();
242       TopoDS_Edge anEdge3 = BRepBuilderAPI_MakeEdge( aPnt3, aPnt4 ).Edge();
243       TopoDS_Edge anEdge4 = BRepBuilderAPI_MakeEdge( aPnt4, aPnt1 ).Edge();
244
245       TopoDS_Wire aWire = BRepBuilderAPI_MakeWire( anEdge1, anEdge2, anEdge3, anEdge4 ).Wire();
246
247       setTextureFileName( aTextureFileName, false, false );
248       setFace( aWire, false, false );
249     }
250     else if ( myObject->IsKind( STANDARD_TYPE(HYDROData_Obstacle) ) )
251     {
252       Handle(HYDROData_Obstacle) anObstacle =
253         Handle(HYDROData_Obstacle)::DownCast( myObject );
254
255       //TODO BEGIN of the block of code to be reimplemented
256       //TODO GetTopShape() to be used in future
257       myTopoShape = anObstacle->GetShape3D();
258       myDisplayMode = AIS_Shaded;
259
260       QColor aFillingColor = anObstacle->GetFillingColor();
261       QColor aBorderColor = anObstacle->GetBorderColor();
262
263       setFillingColor( aFillingColor, false, false );
264       setBorderColor( aBorderColor, false, false );
265
266       buildShape();
267       updateShape( false, false );
268       //TODO END of the block of code to be reimplemented
269     }
270   }
271  
272   if ( myShape.IsNull() || !isVisible() )
273     return;
274
275   myContext->Display( myShape, theIsUpdateViewer );
276 }
277
278 void HYDROGUI_Shape::setVisible( const bool theState,
279                                  const bool theIsUpdateViewer )
280 {
281   if ( myIsVisible == theState )
282     return;
283
284   myIsVisible = theState;
285
286   if ( myShape.IsNull() )
287     return;
288
289   if ( myIsVisible )
290     myContext->Display( myShape, theIsUpdateViewer );
291   else
292     myContext->Erase( myShape, theIsUpdateViewer );
293 }
294
295 void HYDROGUI_Shape::highlight( bool theIsHighlight )
296 {
297   if ( myIsHighlight == theIsHighlight )
298     return;
299
300   myIsHighlight = theIsHighlight;
301
302   if ( myContext.IsNull() || myShape.IsNull() )
303     return;
304
305   colorShapeBorder( getActiveColor() );
306   myContext->Display( myShape );
307 }
308
309 bool HYDROGUI_Shape::isHighlighted() const
310 {
311   return myIsHighlight;
312 }
313
314 void HYDROGUI_Shape::setWire( const TopoDS_Wire& theWire,
315                               const bool         theToDisplay,
316                               const bool         theIsUpdateViewer )
317 {
318   myTopoShape = theWire;
319   myDisplayMode = AIS_WireFrame;
320
321   buildShape();
322   updateShape( theToDisplay, theIsUpdateViewer );
323 }
324
325 void HYDROGUI_Shape::setFaces( const TopoDS_Compound& theWires,
326                                const bool             theToDisplay,
327                                const bool             theIsUpdateViewer )
328 {
329   TopExp_Explorer anExp( theWires, TopAbs_WIRE );
330   TopoDS_Compound aCompound;
331   BRep_Builder aBuilder;
332     aBuilder.MakeCompound( aCompound );
333
334   for ( ; anExp.More(); anExp.Next() ) {
335     TopoDS_Wire aWire = TopoDS::Wire( anExp.Current() );
336     if ( aWire.IsNull() ) {
337       continue;
338     }
339
340     BRepBuilderAPI_MakeFace aMakeFace( aWire, Standard_True );
341     aMakeFace.Build();
342     if( aMakeFace.IsDone() ) {
343       aBuilder.Add( aCompound, aMakeFace.Face() );
344     }
345   }
346
347   myTopoShape = aCompound;
348   myDisplayMode = AIS_Shaded;
349
350   buildShape();
351   updateShape( theToDisplay, theIsUpdateViewer );
352 }
353
354 void HYDROGUI_Shape::setFace( const TopoDS_Wire& theWire,
355                               const bool         theToDisplay,
356                               const bool         theIsUpdateViewer )
357 {
358   BRepBuilderAPI_MakeFace aFaceBuilder( theWire, Standard_True );
359   aFaceBuilder.Build();
360   if( aFaceBuilder.IsDone() )
361   {
362     TopoDS_Face aFace = aFaceBuilder.Face();
363     setFace( aFace, theToDisplay, theIsUpdateViewer );
364   }
365 }
366
367 void HYDROGUI_Shape::setFace( const TopoDS_Face& theFace,
368                               const bool         theToDisplay,
369                               const bool         theIsUpdateViewer )
370 {
371   myTopoShape = theFace;
372   myDisplayMode = myTextureFileName.isEmpty() ? AIS_Shaded : AIS_Shaded+2;
373   //Note: AIS_Shaded+2 is the same as AIS_ExactHLR
374   //TODO: it would be more suitable to use TexturedShape mode from GEOM_AISShape
375
376   buildShape();
377   updateShape( theToDisplay, theIsUpdateViewer );
378 }
379
380 void HYDROGUI_Shape::setShape( const TopoDS_Shape& theShape,
381                                const bool          theToDisplay,
382                                const bool          theIsUpdateViewer )
383 {
384   myTopoShape = theShape;
385   myDisplayMode = AIS_Shaded;
386
387   buildShape();
388   updateShape( theToDisplay, theIsUpdateViewer );
389 }
390
391 void HYDROGUI_Shape::setFillingColor( const QColor& theColor,
392                                       const bool    theToDisplay,
393                                       const bool    theIsUpdateViewer )
394 {
395   myFillingColor = theColor;
396   updateShape( theToDisplay, theIsUpdateViewer );
397 }
398
399 QColor HYDROGUI_Shape::getFillingColor() const
400 {
401   return myFillingColor;
402 }
403
404 void HYDROGUI_Shape::setBorderColor( const QColor& theColor,
405                                      const bool    theToDisplay,
406                                      const bool    theIsUpdateViewer )
407 {
408   myBorderColor = theColor;
409   updateShape( theToDisplay, theIsUpdateViewer );
410 }
411
412 QColor HYDROGUI_Shape::getBorderColor() const
413 {
414   return myBorderColor;
415 }
416
417 void HYDROGUI_Shape::setHighlightColor( const QColor& theColor )
418 {
419   myHighlightColor = theColor;
420 }
421
422 QColor HYDROGUI_Shape::getHighlightColor() const
423 {
424   return myHighlightColor;
425 }
426
427 void HYDROGUI_Shape::setTextureFileName( const QString& theFileName,
428                                          const bool     theToDisplay,
429                                          const bool     theIsUpdateViewer )
430 {
431   myTextureFileName = theFileName;
432   updateShape( theToDisplay, theIsUpdateViewer );
433 }
434
435 QString HYDROGUI_Shape::getTextureFileName() const
436 {
437   return myTextureFileName;
438 }
439
440 void HYDROGUI_Shape::buildShape()
441 {
442   // Erase previously created shape
443   erase();
444
445   if ( myTopoShape.IsNull() )
446     return;
447
448   QString aTextureFileName = getTextureFileName();
449   bool anIsTexture = !aTextureFileName.isEmpty();
450
451   if ( anIsTexture )
452   {
453     myShape = new AIS_TexturedShape( myTopoShape );
454   }
455   else
456   {
457     myShape = new AIS_Shape( myTopoShape );
458   }
459
460   if ( !myObject.IsNull() )
461     myShape->SetOwner( myObject );
462
463   myShape->SetTransparency( 0 );
464   myShape->SetDisplayMode( (AIS_DisplayMode)myDisplayMode );
465
466   if( anIsTexture )
467   {
468     Handle(AIS_TexturedShape) aTexturedShape = 
469       Handle(AIS_TexturedShape)::DownCast( myShape );
470
471     aTexturedShape->SetTextureFileName( HYDROGUI_Tool::ToAsciiString( aTextureFileName ) );
472     aTexturedShape->SetTextureMapOn();
473     aTexturedShape->DisableTextureModulate();
474   }
475
476     // Init default params for shape
477   const Handle(AIS_Drawer)& anAttributes = myShape->Attributes();
478   if ( !anAttributes.IsNull() )
479   {
480     if ( myDisplayMode == AIS_Shaded )
481     {
482       Handle(Prs3d_IsoAspect) anIsoAspect = anAttributes->UIsoAspect();
483       if ( !anIsoAspect.IsNull() )
484         anIsoAspect->SetNumber( 0 );
485       
486       anIsoAspect = anAttributes->VIsoAspect();
487       if ( !anIsoAspect.IsNull() )
488         anIsoAspect->SetNumber( 0 );
489
490       Handle(Prs3d_ShadingAspect) aShadingAspect = anAttributes->ShadingAspect();
491       if ( !aShadingAspect.IsNull() )
492       {
493         Graphic3d_MaterialAspect aMatAspect;
494         aMatAspect.SetAmbient( 1 );
495         aMatAspect.SetDiffuse( 0 );
496
497         aShadingAspect->Aspect()->SetFrontMaterial( aMatAspect );
498         aShadingAspect->Aspect()->SetBackMaterial( aMatAspect );
499       }
500     }
501     else if ( myDisplayMode == AIS_WireFrame )
502     {
503       anAttributes->SetWireDraw( true );
504     }
505   }
506 }
507
508 void HYDROGUI_Shape::updateShape( const bool theToDisplay,
509                                   const bool theIsUpdateViewer )
510 {
511   if ( myShape.IsNull() )
512     return;
513
514   const Handle(AIS_Drawer)& anAttributes = myShape->Attributes();
515   if ( !anAttributes.IsNull() )
516   {
517     if ( myDisplayMode == AIS_Shaded )
518     {
519       // Coloring face filling
520       Handle(Prs3d_ShadingAspect) aShadingAspect = anAttributes->ShadingAspect();
521       if ( !aShadingAspect.IsNull() )
522       {
523         Quantity_Color aFillingColor( getQuantityColorVal( myFillingColor.red() ), 
524                                       getQuantityColorVal( myFillingColor.green() ),
525                                       getQuantityColorVal( myFillingColor.blue() ),
526                                       Quantity_TOC_RGB );
527
528         aShadingAspect->SetColor( aFillingColor );
529         aShadingAspect->SetTransparency( 1 - getQuantityColorVal( myFillingColor.alpha() ) );
530       }
531     }
532     else if ( myDisplayMode == AIS_WireFrame )
533     {
534     }
535
536     // Coloring borders
537     colorShapeBorder( getActiveColor() );
538   }
539
540   if ( !theToDisplay || !isVisible() || myContext.IsNull() )
541     return;
542   
543   myContext->Display( myShape, theIsUpdateViewer );
544 }
545
546 QColor HYDROGUI_Shape::getActiveColor() const
547 {
548   return isHighlighted() ? myHighlightColor : myBorderColor;
549 }
550
551 double HYDROGUI_Shape::getQuantityColorVal( const int theColorVal )
552 {
553   return theColorVal == 0 ? 0 : ( (double)theColorVal / 255 );
554 }
555
556 void HYDROGUI_Shape::colorShapeBorder( const QColor& theColor )
557 {
558   if ( myShape.IsNull() )
559     return;
560
561   const Handle(AIS_Drawer)& anAttributes = myShape->Attributes();
562   if ( anAttributes.IsNull() )
563     return;
564
565   Quantity_Color aBorderColor( getQuantityColorVal( theColor.red() ), 
566                                getQuantityColorVal( theColor.green() ),
567                                getQuantityColorVal( theColor.blue() ),
568                                Quantity_TOC_RGB );
569   if ( myDisplayMode == AIS_Shaded )
570   {
571     if ( theColor.alpha() == 0 )
572     {
573       anAttributes->SetFaceBoundaryDraw( false );
574     }
575     else
576     {
577       anAttributes->SetFaceBoundaryDraw( true );
578
579       Handle(Prs3d_LineAspect) aBoundaryAspect = anAttributes->FaceBoundaryAspect();
580       if ( !aBoundaryAspect.IsNull() )
581         aBoundaryAspect->SetColor( aBorderColor );
582     }
583   }
584   else if ( myDisplayMode == AIS_WireFrame )
585   {
586     myShape->SetColor( aBorderColor );
587   }
588 }
589
590 QString HYDROGUI_Shape::generateTextureFileName( const Handle(HYDROData_Entity)& theImageObj )
591 {
592   QString aResult;
593   if( !theImageObj.IsNull() )
594   {
595     QString aTempDir = HYDROGUI_Tool::GetTempDir( true );
596
597     int aStudyId = HYDROGUI_Tool::GetActiveStudyId();
598     QString aPrefix = QString( "image_%1" ).arg( aStudyId );
599
600     QString anEntry = HYDROGUI_DataObject::dataObjectEntry( theImageObj, false );
601     anEntry.replace( ':', '_' );
602
603     QString anExtension = "bmp";
604
605     aResult = QString( "%1/%2_%3.%4" ).arg( aTempDir, aPrefix, anEntry, anExtension );
606   }
607   return aResult;
608 }
609
610 void HYDROGUI_Shape::removeTextureFile() const
611 {
612   QFile aFile( getTextureFileName() );
613   if( aFile.exists() )
614     aFile.remove();
615 }
616
617 QImage HYDROGUI_Shape::reduceTexture( const QImage& theImage, const int theSizeLimit )
618 {
619   double aSizeLimit = (double)theSizeLimit;
620   double aWidth = (double)theImage.width();
621   double aHeight = (double)theImage.height();
622   if( aWidth > aSizeLimit || aHeight > aSizeLimit )
623   {
624     if( aWidth > aHeight )
625     {
626       aHeight /= ( aWidth / aSizeLimit );
627       aWidth = aSizeLimit;
628     }
629     else
630     {
631       aWidth /= ( aHeight / aSizeLimit );
632       aHeight = aSizeLimit;
633     }
634   }
635   return theImage.scaled( aWidth, aHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation );
636 }