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