0029076: Visualization - implement element shrinking Shader
[occt.git] / src / AIS / AIS_ColorScale.cxx
1 // Created on: 2015-02-03
2 // Copyright (c) 2015 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <AIS_ColorScale.hxx>
16
17 #include <AIS_InteractiveContext.hxx>
18 #include <Aspect_TypeOfColorScaleData.hxx>
19 #include <Aspect_TypeOfColorScalePosition.hxx>
20 #include <Aspect_Window.hxx>
21 #include <Geom_Line.hxx>
22 #include <GeomAdaptor_Curve.hxx>
23 #include <Graphic3d_ArrayOfPolygons.hxx>
24 #include <Graphic3d_ArrayOfPolylines.hxx>
25 #include <Graphic3d_AspectFillArea3d.hxx>
26 #include <Graphic3d_AspectText3d.hxx>
27 #include <Graphic3d_GraphicDriver.hxx>
28 #include <Graphic3d_ArrayOfTriangles.hxx>
29 #include <Prs3d_LineAspect.hxx>
30 #include <Prs3d_Root.hxx>
31 #include <Prs3d_ShadingAspect.hxx>
32 #include <Prs3d_Text.hxx>
33 #include <Prs3d_TextAspect.hxx>
34 #include <SelectMgr_EntityOwner.hxx>
35 #include <SelectMgr_Selection.hxx>
36 #include <Select3D_SensitiveBox.hxx>
37 #include <Select3D_SensitiveSegment.hxx>
38 #include <StdPrs_Curve.hxx>
39 #include <V3d_Viewer.hxx>
40 #include <V3d_View.hxx>
41
42 IMPLEMENT_STANDARD_RTTIEXT(AIS_ColorScale, AIS_InteractiveObject)
43
44 namespace
45 {
46   //! Method to add colored quad into array of triangles.
47   static void addColoredQuad (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
48                               const Standard_Integer theXLeft, const Standard_Integer theYBottom,
49                               const Standard_Integer theSizeX, const Standard_Integer theSizeY,
50                               const Quantity_Color& theColorBottom,
51                               const Quantity_Color& theColorTop)
52   {
53     const Standard_Integer aVertIndex = theTris->VertexNumber() + 1;
54     theTris->AddVertex (gp_Pnt (theXLeft,            theYBottom,            0.0), theColorBottom);
55     theTris->AddVertex (gp_Pnt (theXLeft + theSizeX, theYBottom,            0.0), theColorBottom);
56     theTris->AddVertex (gp_Pnt (theXLeft,            theYBottom + theSizeY, 0.0), theColorTop);
57     theTris->AddVertex (gp_Pnt (theXLeft + theSizeX, theYBottom + theSizeY, 0.0), theColorTop);
58     theTris->AddEdges (aVertIndex,     aVertIndex + 1, aVertIndex + 2);
59     theTris->AddEdges (aVertIndex + 1, aVertIndex + 2, aVertIndex + 3);
60   }
61
62   //! Compute hue angle from specified value.
63   static Quantity_Color colorFromValueEx (const Standard_Real theValue,
64                                           const Standard_Real theMin,
65                                           const Standard_Real theMax,
66                                           const Graphic3d_Vec3d& theHlsMin,
67                                           const Graphic3d_Vec3d& theHlsMax)
68   {
69     const Standard_Real aValueDelta = theMax - theMin;
70     Standard_Real aValue = 0.0;
71     if (aValueDelta != 0.0)
72     {
73       aValue = (theValue - theMin) / aValueDelta;
74     }
75
76     Standard_Real aHue        = NCollection_Lerp<Standard_Real>::Interpolate (theHlsMin[0], theHlsMax[0], aValue);
77     Standard_Real aLightness  = NCollection_Lerp<Standard_Real>::Interpolate (theHlsMin[1], theHlsMax[1], aValue);
78     Standard_Real aSaturation = NCollection_Lerp<Standard_Real>::Interpolate (theHlsMin[2], theHlsMax[2], aValue);
79     return Quantity_Color (AIS_ColorScale::hueToValidRange (aHue), aLightness, aSaturation, Quantity_TOC_HLS);
80   }
81 }
82
83 //=======================================================================
84 //function : AIS_ColorScale
85 //purpose  :
86 //=======================================================================
87 AIS_ColorScale::AIS_ColorScale()
88 : myMin (0.0),
89   myMax (1.0),
90   myColorHlsMin (230.0, 1.0, 1.0),
91   myColorHlsMax (0.0,   1.0, 1.0),
92   myFormat ("%.4g"),
93   myNbIntervals (10),
94   myColorType (Aspect_TOCSD_AUTO),
95   myLabelType (Aspect_TOCSD_AUTO),
96   myIsLabelAtBorder (Standard_True),
97   myIsReversed (Standard_False),
98   myIsLogarithmic (Standard_False),
99   myIsSmooth (Standard_False),
100   myLabelPos (Aspect_TOCSP_RIGHT),
101   myTitlePos (Aspect_TOCSP_LEFT),
102   myXPos (0),
103   myYPos (0),
104   myBreadth (0),
105   myHeight  (0),
106   mySpacing (5),
107   myTextHeight (20)
108 {
109   SetDisplayMode (0);
110   myDrawer->SetupOwnShadingAspect();
111   myDrawer->ShadingAspect()->Aspect()->SetShadingModel (Graphic3d_TOSM_UNLIT);
112   myDrawer->ShadingAspect()->Aspect()->SetAlphaMode (Graphic3d_AlphaMode_Opaque);
113   myDrawer->ShadingAspect()->Aspect()->SetInteriorColor (Quantity_NOC_WHITE);
114 }
115
116 //=======================================================================
117 //function : GetLabel
118 //purpose  :
119 //=======================================================================
120 TCollection_ExtendedString AIS_ColorScale::GetLabel (const Standard_Integer theIndex) const
121 {
122   if (myLabelType == Aspect_TOCSD_USER)
123   {
124     if (theIndex >= myLabels.Lower()
125      || theIndex <= myLabels.Upper())
126     {
127       return myLabels.Value(theIndex);
128     }
129     return TCollection_ExtendedString();
130   }
131
132   // value to be shown depends on label position
133   const Standard_Real aVal = myIsLabelAtBorder
134                            ? GetIntervalValue (theIndex - 1)
135                            : (0.5 * (GetIntervalValue (theIndex - 1) + GetIntervalValue (theIndex)));
136
137   char aBuf[1024];
138   sprintf (aBuf, myFormat.ToCString(), aVal);
139   return TCollection_ExtendedString (aBuf);
140 }
141
142 //=======================================================================
143 //function : GetIntervalColor
144 //purpose  :
145 //=======================================================================
146 Quantity_Color AIS_ColorScale::GetIntervalColor (const Standard_Integer theIndex) const
147 {
148   if (myColorType== Aspect_TOCSD_USER)
149   {
150     if (theIndex <= 0 || theIndex > myColors.Length())
151     {
152       return Quantity_Color();
153     }
154     return myColors.Value (theIndex);
155   }
156
157   return colorFromValue (theIndex - 1, 0, myNbIntervals - 1);
158 }
159
160 //=======================================================================
161 //function : GetLabels
162 //purpose  :
163 //=======================================================================
164 void AIS_ColorScale::GetLabels (TColStd_SequenceOfExtendedString& theLabels) const
165 {
166   theLabels.Clear();
167   for (TColStd_SequenceOfExtendedString::Iterator aLabIter (myLabels); aLabIter.More(); aLabIter.Next())
168   {
169     theLabels.Append (aLabIter.Value());
170   }
171 }
172
173 //=======================================================================
174 //function : GetColors
175 //purpose  :
176 //=======================================================================
177 void AIS_ColorScale::GetColors (Aspect_SequenceOfColor& theColors) const
178 {
179   theColors.Clear();
180   for (Aspect_SequenceOfColor::Iterator aColorIter (myColors); aColorIter.More(); aColorIter.Next())
181   {
182     theColors.Append (aColorIter.Value());
183   }
184 }
185
186 //=======================================================================
187 //function : SetRange
188 //purpose  :
189 //=======================================================================
190 void AIS_ColorScale::SetRange (const Standard_Real theMin, const Standard_Real theMax)
191 {
192   myMin = Min (theMin, theMax);
193   myMax = Max (theMin, theMax);
194 }
195
196 //=======================================================================
197 //function : SetNumberOfIntervals
198 //purpose  :
199 //=======================================================================
200 void AIS_ColorScale::SetNumberOfIntervals (const Standard_Integer theNum)
201 {
202   if (theNum < 1)
203   {
204     return;
205   }
206
207   myNbIntervals = theNum;
208 }
209
210 //=======================================================================
211 //function : SetLabel
212 //purpose  :
213 //=======================================================================
214 void AIS_ColorScale::SetLabel (const TCollection_ExtendedString& theLabel,
215                                const Standard_Integer theIndex)
216 {
217   const Standard_Integer aLabIndex = (theIndex <= 0 ? myLabels.Length() + 1 : theIndex);
218   while (myLabels.Length() < aLabIndex)
219   {
220     myLabels.Append (TCollection_ExtendedString());
221   }
222   myLabels.SetValue (aLabIndex, theLabel);
223 }
224
225 //=======================================================================
226 //function : SetIntervalColor
227 //purpose  :
228 //=======================================================================
229 void AIS_ColorScale::SetIntervalColor (const Quantity_Color&  theColor,
230                                        const Standard_Integer theIndex)
231 {
232   const Standard_Integer aColorIndex = (theIndex <= 0 ? myColors.Length() + 1 : theIndex);
233   while (myColors.Length() < aColorIndex)
234   {
235     myColors.Append (Quantity_Color());
236   }
237   myColors.SetValue (aColorIndex, theColor);
238 }
239
240 //=======================================================================
241 //function : SetLabels
242 //purpose  :
243 //=======================================================================
244 void AIS_ColorScale::SetLabels (const TColStd_SequenceOfExtendedString& theSeq)
245 {
246   myLabels.Clear();
247   for (TColStd_SequenceOfExtendedString::Iterator aLabIter (theSeq); aLabIter.More(); aLabIter.Next())
248   {
249     myLabels.Append (aLabIter.Value());
250   }
251 }
252
253 //=======================================================================
254 //function : SetColors
255 //purpose  :
256 //=======================================================================
257 void AIS_ColorScale::SetColors (const Aspect_SequenceOfColor& theSeq)
258 {
259   myColors.Clear();
260   for (Aspect_SequenceOfColor::Iterator aColorIter (theSeq); aColorIter.More(); aColorIter.Next())
261   {
262     myColors.Append (aColorIter.Value());
263   }
264 }
265
266 //=======================================================================
267 //function : SizeHint
268 //purpose  :
269 //=======================================================================
270 void AIS_ColorScale::SizeHint (Standard_Integer& theWidth, Standard_Integer& theHeight) const
271 {
272   const Standard_Integer aTextHeight = TextHeight ("");
273   const Standard_Integer aColorWidth = 20;
274   Standard_Integer aTextWidth = 0;
275   if (myLabelPos != Aspect_TOCSP_NONE)
276   {
277     for (Standard_Integer aLabIter = (myIsLabelAtBorder ? 0 : 1); aLabIter <= myNbIntervals; ++aLabIter)
278     {
279       aTextWidth = Max (aTextWidth, TextWidth (GetLabel (aLabIter)));
280     }
281   }
282
283   const Standard_Integer aScaleWidth  = aColorWidth + aTextWidth + (aTextWidth ? 3 : 2) * mySpacing;
284   const Standard_Integer aScaleHeight = (Standard_Integer)(1.5 * (myNbIntervals + (myIsLabelAtBorder ? 2 : 1)) * aTextHeight);
285
286   Standard_Integer aTitleWidth  = 0;
287   Standard_Integer aTitleHeight = 0;
288   if (!myTitle.IsEmpty())
289   {
290     aTitleHeight = TextHeight (myTitle) + mySpacing;
291     aTitleWidth =  TextWidth  (myTitle) + mySpacing * 2;
292   }
293
294   theWidth  = Max (aTitleWidth, aScaleWidth);
295   theHeight = aScaleHeight + aTitleHeight;
296 }
297
298 //=======================================================================
299 //function : GetIntervalValue
300 //purpose  :
301 //=======================================================================
302 Standard_Real AIS_ColorScale::GetIntervalValue (const Standard_Integer theIndex) const
303 {
304   if (myNbIntervals <= 0)
305   {
306     return 0.0;
307   }
308
309   if (IsLogarithmic())
310   {
311     Standard_Real aMin     = myMin > 0 ? myMin : 1.0;
312     Standard_Real aDivisor = std::pow (myMax / aMin, 1.0 / myNbIntervals);
313     return aMin * std::pow (aDivisor,theIndex);
314   }
315
316   Standard_Real aNum = 0;
317   if (myNbIntervals > 0)
318   {
319     aNum = GetMin() + theIndex * (Abs (GetMax() - GetMin()) / myNbIntervals);
320   }
321   return aNum;
322 }
323
324 //=======================================================================
325 //function : colorFromValue
326 //purpose  :
327 //=======================================================================
328 Quantity_Color AIS_ColorScale::colorFromValue (const Standard_Real theValue,
329                                                const Standard_Real theMin,
330                                                const Standard_Real theMax) const
331 {
332   return colorFromValueEx (theValue, theMin, theMax, myColorHlsMin, myColorHlsMax);
333 }
334
335 //=======================================================================
336 //function : FindColor
337 //purpose  :
338 //=======================================================================
339 Standard_Boolean AIS_ColorScale::FindColor (const Standard_Real theValue,
340                                             Quantity_Color& theColor) const
341 {
342   if (theValue < myMin || theValue > myMax || myMax < myMin)
343   {
344     theColor = Quantity_Color();
345     return Standard_False;
346   }
347
348   if (myColorType == Aspect_TOCSD_USER)
349   {
350     Standard_Integer anIndex = 0;
351     if (Abs (myMax - myMin) > Precision::Approximation())
352     {
353       anIndex = (theValue - myMin < Precision::Confusion()) 
354         ? 1
355         : Standard_Integer (Ceiling (( theValue - myMin ) / ( (myMax - myMin) / myNbIntervals)));
356     }
357
358     if (anIndex <= 0 || anIndex > myColors.Length())
359     {
360       theColor = Quantity_Color();
361       return Standard_False;
362     }
363
364     theColor = myColors.Value (anIndex);
365     return Standard_True;
366   }
367
368   return FindColor (theValue, myMin, myMax, myNbIntervals, theColor);
369 }
370
371 //=======================================================================
372 //function : FindColor
373 //purpose  :
374 //=======================================================================
375 Standard_Boolean AIS_ColorScale::FindColor (const Standard_Real theValue,
376                                             const Standard_Real theMin,
377                                             const Standard_Real theMax,
378                                             const Standard_Integer theColorsCount,
379                                             const Graphic3d_Vec3d& theColorHlsMin,
380                                             const Graphic3d_Vec3d& theColorHlsMax,
381                                             Quantity_Color& theColor)
382 {
383   if (theValue < theMin || theValue > theMax || theMax < theMin)
384   {
385     return Standard_False;
386   }
387
388   Standard_Real anInterval = 0.0;
389   if (Abs (theMax - theMin) > Precision::Approximation())
390   {
391     anInterval = Floor (Standard_Real (theColorsCount) * (theValue - theMin) / (theMax - theMin));
392   }
393
394   theColor = colorFromValueEx (anInterval, 0, theColorsCount - 1, theColorHlsMin, theColorHlsMax);
395   return Standard_True;
396 }
397
398 //=======================================================================
399 //function : computeMaxLabelWidth
400 //purpose  :
401 //=======================================================================
402 Standard_Integer AIS_ColorScale::computeMaxLabelWidth (const TColStd_SequenceOfExtendedString& theLabels) const
403 {
404   {
405     Handle(V3d_Viewer) aViewer = GetContext()->CurrentViewer();
406     aViewer->InitActiveViews(); // for AIS_ColorScale::TextSize()
407   }
408
409   Standard_Integer aWidthMax = 0;
410   for (TColStd_SequenceOfExtendedString::Iterator aLabIter (theLabels); aLabIter.More(); aLabIter.Next())
411   {
412     if (!aLabIter.Value().IsEmpty())
413     {
414       aWidthMax = Max (aWidthMax, TextWidth (aLabIter.Value()));
415     }
416   }
417   return aWidthMax;
418 }
419
420 //=======================================================================
421 //function : updateTextAspect
422 //purpose  :
423 //=======================================================================
424 void AIS_ColorScale::updateTextAspect()
425 {
426   // update text aspect
427   const Quantity_Color aFgColor (hasOwnColor ? myDrawer->Color() : Quantity_NOC_WHITE);
428   if (!myDrawer->HasOwnTextAspect())
429   {
430     myDrawer->SetTextAspect (new Prs3d_TextAspect());
431     *myDrawer->TextAspect()->Aspect() = *myDrawer->Link()->TextAspect()->Aspect();
432   }
433
434   const Handle(Prs3d_TextAspect)& anAspect = myDrawer->TextAspect();
435   anAspect->SetColor  (aFgColor);
436   anAspect->SetHeight (myTextHeight);
437   anAspect->SetHorizontalJustification (Graphic3d_HTA_LEFT);
438   anAspect->SetVerticalJustification (Graphic3d_VTA_BOTTOM);
439   anAspect->Aspect()->SetTextZoomable (Standard_True);
440 }
441
442 //=======================================================================
443 //function : Compute
444 //purpose  :
445 //=======================================================================
446 void AIS_ColorScale::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
447                               const Handle(Prs3d_Presentation)& thePrs,
448                               const Standard_Integer theMode)
449 {
450   if (theMode != 0)
451   {
452     return;
453   }
454
455   // update text aspect
456   updateTextAspect();
457
458   const Standard_Integer aTitleOffset = !myTitle.IsEmpty() ? (myTextHeight + mySpacing) : 0;
459
460   const Standard_Integer aBarYOffset = myTextHeight / 2 + 2 * mySpacing; // a half-label offset
461   const Standard_Integer aBarBottom  = myYPos + aBarYOffset;
462   const Standard_Integer aBarTop     = myYPos + myHeight - aTitleOffset - aBarYOffset;
463   const Standard_Integer aBarHeight  = aBarTop - aBarBottom;
464
465   TColStd_SequenceOfExtendedString aLabels;
466   if (myLabelType == Aspect_TOCSD_USER)
467   {
468     aLabels = myLabels;
469   }
470   else
471   {
472     const Standard_Integer aNbLabels = myIsLabelAtBorder ? myNbIntervals + 1 : myNbIntervals;
473     for (Standard_Integer aLabIter = 1; aLabIter <= aNbLabels; ++aLabIter)
474     {
475       if (myIsReversed)
476       {
477         aLabels.Prepend (GetLabel (aLabIter));
478       }
479       else
480       {
481         aLabels.Append (GetLabel (aLabIter));
482       }
483     }
484   }
485
486   const Standard_Integer aTextWidth = myLabelPos != Aspect_TOCSP_NONE ? computeMaxLabelWidth (aLabels) : 0;
487   Standard_Integer aColorBreadth = Max (5, Min (20, myBreadth - aTextWidth - 3 * mySpacing));
488   if (myLabelPos == Aspect_TOCSP_CENTER
489    || myLabelPos == Aspect_TOCSP_NONE)
490   {
491     aColorBreadth += aTextWidth;
492   }
493
494   // draw title
495   Handle(Graphic3d_Group) aLabelsGroup;
496   if (!myTitle.IsEmpty()
497    || !aLabels.IsEmpty())
498   {
499     aLabelsGroup = thePrs->NewGroup();
500     aLabelsGroup->SetGroupPrimitivesAspect (myDrawer->TextAspect()->Aspect());
501   }
502   if (!myTitle.IsEmpty())
503   {
504     drawText (aLabelsGroup, myTitle,
505               myXPos + mySpacing,
506               aBarTop + aBarYOffset,
507               Graphic3d_VTA_BOTTOM);
508   }
509
510   // draw colors
511   drawColorBar (thePrs, aBarBottom, aBarHeight, aTextWidth, aColorBreadth);
512
513   // draw Labels
514   drawLabels (aLabelsGroup, aLabels, aBarBottom, aBarHeight, aTextWidth, aColorBreadth);
515 }
516
517 //=======================================================================
518 //function : drawColorBar
519 //purpose  :
520 //=======================================================================
521 void AIS_ColorScale::drawColorBar (const Handle(Prs3d_Presentation)& thePrs,
522                                    const Standard_Integer theBarBottom,
523                                    const Standard_Integer theBarHeight,
524                                    const Standard_Integer theMaxLabelWidth,
525                                    const Standard_Integer theColorBreadth)
526 {
527   const Standard_Real aStepY = Standard_Real(theBarHeight) / Standard_Real(myNbIntervals);
528   if (aStepY <= 0.0)
529   {
530     return;
531   }
532
533   // Draw colors
534   const Standard_Integer anXLeft = myLabelPos == Aspect_TOCSP_LEFT
535                                  ? myXPos + mySpacing + theMaxLabelWidth + (theMaxLabelWidth != 0 ? 1 : 0) * mySpacing
536                                  : myXPos + mySpacing;
537
538   Aspect_SequenceOfColor aColors;
539   for (Standard_Integer anIntervalIter = 1; anIntervalIter <= myNbIntervals; ++anIntervalIter)
540   {
541     if (myIsReversed)
542     {
543       aColors.Prepend (GetIntervalColor (anIntervalIter));
544     }
545     else
546     {
547       aColors.Append (GetIntervalColor (anIntervalIter));
548     }
549   }
550
551   Handle(Graphic3d_ArrayOfTriangles) aTriangles;
552   if (myIsSmooth
553    && myColorType == Aspect_TOCSD_USER)
554   {
555     // Smooth custom intervals, so that the color in the center of interval is equal to specified one
556     // (thus the halves of first and last intervals have solid color)
557     aTriangles = new Graphic3d_ArrayOfTriangles ((aColors.Length() + 1) * 4,     // quads
558                                                  (aColors.Length() + 1) * 2 * 3, // quads as triangles
559                                                  false, true);                   // per-vertex colors
560     Quantity_Color aColor1 (aColors.Value (1)), aColor2;
561     Standard_Integer       aSizeY        = Standard_Integer(aStepY / 2);
562     const Standard_Integer anYBottom     = theBarBottom + aSizeY;
563     Standard_Integer       anYBottomIter = anYBottom;
564     addColoredQuad (aTriangles,
565                     anXLeft, theBarBottom,
566                     theColorBreadth, aSizeY,
567                     aColor1, aColor1);
568     for (Standard_Integer aColorIter = 0; aColorIter < myNbIntervals - 1; ++aColorIter)
569     {
570       aColor1 = aColors.Value (aColorIter + 1);
571       aColor2 = aColors.Value (aColorIter + 2);
572       aSizeY  = anYBottom + Standard_Integer((aColorIter + 1) * aStepY) - anYBottomIter;
573       addColoredQuad (aTriangles,
574                       anXLeft, anYBottomIter,
575                       theColorBreadth, aSizeY,
576                       aColor1, aColor2);
577       anYBottomIter += aSizeY;
578     }
579     aColor2 = aColors.Value (myNbIntervals);
580     aSizeY  = theBarBottom + theBarHeight - anYBottomIter;
581     addColoredQuad (aTriangles,
582                     anXLeft, anYBottomIter,
583                     theColorBreadth, aSizeY,
584                     aColor2, aColor2);
585   }
586   else if (myIsSmooth)
587   {
588     // smooth transition between standard colors - without solid color regions at the beginning and end of full color range
589     const Quantity_Color aColorsFixed[5] =
590     {
591       colorFromValue (0, 0, 4),
592       colorFromValue (1, 0, 4),
593       colorFromValue (2, 0, 4),
594       colorFromValue (3, 0, 4),
595       colorFromValue (4, 0, 4)
596     };
597     aTriangles = new Graphic3d_ArrayOfTriangles (4 * 4,        // quads
598                                                  4 * 2 * 3,    // quads as triangles
599                                                  false, true); // per-vertex colors
600     Standard_Integer anYBottomIter = theBarBottom;
601     addColoredQuad (aTriangles,
602                     anXLeft, theBarBottom,
603                     theColorBreadth, theBarHeight / 4,
604                     aColorsFixed[0], aColorsFixed[1]);
605     anYBottomIter += theBarHeight / 4;
606     addColoredQuad (aTriangles,
607                     anXLeft, anYBottomIter,
608                     theColorBreadth, theBarHeight / 4,
609                     aColorsFixed[1], aColorsFixed[2]);
610     anYBottomIter += theBarHeight / 4;
611     addColoredQuad (aTriangles,
612                     anXLeft, anYBottomIter,
613                     theColorBreadth, theBarHeight / 4,
614                     aColorsFixed[2], aColorsFixed[3]);
615     anYBottomIter += theBarHeight / 4;
616     const Standard_Integer aLastSizeY  = theBarBottom + theBarHeight - anYBottomIter;
617     addColoredQuad (aTriangles,
618                     anXLeft, anYBottomIter,
619                     theColorBreadth, aLastSizeY,
620                     aColorsFixed[3], aColorsFixed[4]);
621   }
622   else
623   {
624     // no color smoothing
625     aTriangles = new Graphic3d_ArrayOfTriangles (aColors.Length() * 4,     // quads
626                                                  aColors.Length() * 2 * 3, // quads as triangles
627                                                  false, true);             // per-vertex colors
628     Standard_Integer anYBottomIter = theBarBottom;
629     for (Standard_Integer aColorIter = 0; aColorIter < myNbIntervals; ++aColorIter)
630     {
631       const Quantity_Color&  aColor = aColors.Value (aColorIter + 1);
632       const Standard_Integer aSizeY = theBarBottom + Standard_Integer((aColorIter + 1) * aStepY) - anYBottomIter;
633       addColoredQuad (aTriangles,
634                       anXLeft, anYBottomIter,
635                       theColorBreadth, aSizeY,
636                       aColor, aColor);
637       anYBottomIter += aSizeY;
638     }
639   }
640
641   Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
642   aGroup->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
643   aGroup->AddPrimitiveArray (aTriangles);
644
645   const Quantity_Color aFgColor (hasOwnColor ? myDrawer->Color() : Quantity_NOC_WHITE);
646   drawFrame (thePrs,
647              anXLeft - 1, theBarBottom - 1,
648              theColorBreadth + 2,
649              theBarHeight + 2,
650              aFgColor);
651 }
652
653 //=======================================================================
654 //function : drawLabels
655 //purpose  :
656 //=======================================================================
657 void AIS_ColorScale::drawLabels (const Handle(Graphic3d_Group)& theGroup,
658                                  const TColStd_SequenceOfExtendedString& theLabels,
659                                  const Standard_Integer theBarBottom,
660                                  const Standard_Integer theBarHeight,
661                                  const Standard_Integer theMaxLabelWidth,
662                                  const Standard_Integer theColorBreadth)
663 {
664   if (myLabelPos == Aspect_TOCSP_NONE
665    || theLabels.IsEmpty())
666   {
667     return;
668   }
669
670   const Standard_Integer aNbLabels    = theLabels.Size();
671   const Standard_Integer aNbIntervals = myIsLabelAtBorder ? aNbLabels - 1 : aNbLabels;
672   const Standard_Real    aStepY       = Standard_Real(theBarHeight) / Standard_Real(aNbIntervals);
673   if (aStepY <= 0.0)
674   {
675     return;
676   }
677
678   Standard_Integer aFilter = 0;
679   {
680     const Standard_Integer aTitleHeight = !myTitle.IsEmpty() ? (myTextHeight + 2 * mySpacing) : mySpacing;
681     const Standard_Integer aSpc         = myHeight - aTitleHeight - ((Min (aNbLabels, 2) + Abs (aNbLabels - aNbIntervals - 1)) * myTextHeight);
682     if (aSpc <= 0)
683     {
684       return;
685     }
686
687     const Standard_Real aVal = Standard_Real(aNbLabels) * myTextHeight / aSpc;
688     Standard_Real anIPart = 0.0;
689     Standard_Real anFPart = std::modf (aVal, &anIPart);
690     aFilter = (Standard_Integer )anIPart + (anFPart != 0 ? 1 : 0);
691   }
692   if (aFilter <= 0)
693   {
694     return;
695   }
696
697   Standard_Integer anXLeft = myXPos + mySpacing;
698   const Standard_Integer anAscent = 0;
699   switch (myLabelPos)
700   {
701     case Aspect_TOCSP_NONE:
702     case Aspect_TOCSP_LEFT:
703     {
704       break;
705     }
706     case Aspect_TOCSP_CENTER:
707     {
708       anXLeft += (theColorBreadth - theMaxLabelWidth) / 2;
709       break;
710     }
711     case Aspect_TOCSP_RIGHT:
712     {
713       anXLeft += theColorBreadth + mySpacing;
714       break;
715     }
716   }
717
718   Standard_Integer i1 = 0;
719   Standard_Integer i2 = aNbLabels - 1;
720   Standard_Integer aLast1 = i1;
721   Standard_Integer aLast2 = i2;
722   const Standard_Integer anYBottom = myIsLabelAtBorder
723                                    ? theBarBottom
724                                    : theBarBottom + Standard_Integer(aStepY / 2);
725   while (i2 - i1 >= aFilter || ( i2 == 0 && i1 == 0 ))
726   {
727     Standard_Integer aPos1 = i1;
728     Standard_Integer aPos2 = aNbLabels - 1 - i2;
729     if (aFilter && !(aPos1 % aFilter))
730     {
731       drawText (theGroup, theLabels.Value (i1 + 1),
732                 anXLeft, anYBottom + Standard_Integer(i1 * aStepY + anAscent),
733                 Graphic3d_VTA_CENTER);
734       aLast1 = i1;
735     }
736     if (aFilter && !(aPos2 % aFilter))
737     {
738       drawText (theGroup, theLabels.Value (i2 + 1),
739                 anXLeft, anYBottom + Standard_Integer(i2 * aStepY + anAscent),
740                 Graphic3d_VTA_CENTER);
741       aLast2 = i2;
742     }
743     i1++;
744     i2--;
745   }
746   Standard_Integer aPos = i1;
747   Standard_Integer i0   = -1;
748   while (aPos <= i2 && i0 == -1)
749   {
750     if (aFilter && !(aPos % aFilter)
751       && Abs (aPos - aLast1) >= aFilter
752       && Abs (aPos - aLast2) >= aFilter)
753     {
754       i0 = aPos;
755     }
756     aPos++;
757   }
758
759   if (i0 != -1)
760   {
761     drawText (theGroup, theLabels.Value (i0 + 1),
762               anXLeft, anYBottom + Standard_Integer(i0 * aStepY + anAscent),
763               Graphic3d_VTA_CENTER);
764   }
765 }
766
767 //=======================================================================
768 //function : drawFrame
769 //purpose  :
770 //=======================================================================
771 void AIS_ColorScale::drawFrame (const Handle(Prs3d_Presentation)& thePrs,
772                                 const Standard_Integer theX, const Standard_Integer theY,
773                                 const Standard_Integer theWidth, const Standard_Integer theHeight,
774                                 const Quantity_Color& theColor)
775 {
776   Handle(Graphic3d_ArrayOfPolylines) aPrim = new Graphic3d_ArrayOfPolylines(5);
777   aPrim->AddVertex (theX,            theY, 0.0);
778   aPrim->AddVertex (theX + theWidth, theY, 0.0);
779   aPrim->AddVertex (theX + theWidth, theY + theHeight, 0.0);
780   aPrim->AddVertex (theX,            theY + theHeight, 0.0);
781   aPrim->AddVertex (theX,            theY, 0.0);
782
783   Handle(Graphic3d_AspectLine3d) anAspect = new Graphic3d_AspectLine3d (theColor, Aspect_TOL_SOLID, 1.0);
784   Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
785   aGroup->SetGroupPrimitivesAspect (anAspect);
786   aGroup->AddPrimitiveArray (aPrim);
787 }
788
789 //=======================================================================
790 //function : drawText
791 //purpose  :
792 //=======================================================================
793 void AIS_ColorScale::drawText (const Handle(Graphic3d_Group)& theGroup,
794                                const TCollection_ExtendedString& theText,
795                                const Standard_Integer theX, const Standard_Integer theY,
796                                const Graphic3d_VerticalTextAlignment theVertAlignment)
797 {
798   const Handle(Prs3d_TextAspect)& anAspect = myDrawer->TextAspect();
799   theGroup->Text (theText,
800                   gp_Ax2 (gp_Pnt (theX, theY, 0.0), gp::DZ()),
801                   anAspect->Height(),
802                   anAspect->Angle(),
803                   anAspect->Orientation(),
804                   Graphic3d_HTA_LEFT,
805                   theVertAlignment,
806                   Standard_True,
807                   Standard_False); // has own anchor
808
809 }
810
811 //=======================================================================
812 //function : TextWidth
813 //purpose  :
814 //=======================================================================
815 Standard_Integer AIS_ColorScale::TextWidth (const TCollection_ExtendedString& theText) const
816 {
817   Standard_Integer aWidth, anAscent, aDescent;
818   TextSize (theText, myTextHeight, aWidth, anAscent, aDescent);
819   return aWidth;
820 }
821
822 //=======================================================================
823 //function : TextHeight
824 //purpose  :
825 //=======================================================================
826 Standard_Integer AIS_ColorScale::TextHeight (const TCollection_ExtendedString& theText) const
827 {
828   Standard_Integer aWidth, anAscent, aDescent;
829   TextSize (theText, myTextHeight, aWidth, anAscent, aDescent);
830   return anAscent + aDescent;
831 }
832
833 //=======================================================================
834 //function : TextSize
835 //purpose  :
836 //=======================================================================
837 void AIS_ColorScale::TextSize (const TCollection_ExtendedString& theText,
838                                const Standard_Integer theHeight,
839                                Standard_Integer& theWidth,
840                                Standard_Integer& theAscent,
841                                Standard_Integer& theDescent) const
842 {
843   if (!HasInteractiveContext())
844   {
845     return;
846   }
847
848   Standard_ShortReal aWidth   = 10.0f;
849   Standard_ShortReal anAscent = 1.0f;
850   Standard_ShortReal aDescent = 1.0f;
851   const TCollection_AsciiString aText (theText);
852
853   const Handle(V3d_Viewer)&      aViewer = GetContext()->CurrentViewer();
854   const Handle(Graphic3d_CView)& aView   = aViewer->ActiveViewIterator().Value()->View();
855   aViewer->Driver()->TextSize (aView, aText.ToCString(), (Standard_ShortReal)theHeight, aWidth, anAscent, aDescent);
856   theWidth   = (Standard_Integer)aWidth;
857   theAscent  = (Standard_Integer)anAscent;
858   theDescent = (Standard_Integer)aDescent;
859 }