0031909: Visualization, AIS_Trihedron - replace maps with arrays
[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_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   //! Return the index of discrete interval for specified value.
83   //! Note that when value lies exactly on the border between two intervals,
84   //! determining which interval to return is undefined operation;
85   //! Current implementation returns the following interval in this case.
86   //! @param theValue [in] value to map
87   //! @param theMin   [in] values range, lower value
88   //! @param theMax   [in] values range, upper value
89   //! @param theNbIntervals [in] number of discrete intervals
90   //! @return index of interval within [1, theNbIntervals] range
91   static Standard_Integer colorDiscreteInterval (Standard_Real theValue,
92                                                  Standard_Real theMin,
93                                                  Standard_Real theMax,
94                                                  Standard_Integer theNbIntervals)
95   {
96     if (Abs (theMax - theMin) <= Precision::Approximation())
97     {
98       return 1;
99     }
100
101     Standard_Integer anInterval = 1 + (Standard_Integer )Floor (Standard_Real (theNbIntervals) * (theValue - theMin) / (theMax - theMin));
102     // map the very upper value (theValue==theMax) to the largest color interval
103     anInterval = Min (anInterval, theNbIntervals);
104     return anInterval;
105   }
106 }
107
108 //=======================================================================
109 //function : AIS_ColorScale
110 //purpose  :
111 //=======================================================================
112 AIS_ColorScale::AIS_ColorScale()
113 : myMin (0.0),
114   myMax (1.0),
115   myColorHlsMin (230.0, 1.0, 1.0),
116   myColorHlsMax (0.0,   1.0, 1.0),
117   myFormat ("%.4g"),
118   myNbIntervals (10),
119   myColorType (Aspect_TOCSD_AUTO),
120   myLabelType (Aspect_TOCSD_AUTO),
121   myIsLabelAtBorder (Standard_True),
122   myIsReversed (Standard_False),
123   myIsLogarithmic (Standard_False),
124   myIsSmooth (Standard_False),
125   myLabelPos (Aspect_TOCSP_RIGHT),
126   myTitlePos (Aspect_TOCSP_LEFT),
127   myXPos (0),
128   myYPos (0),
129   myBreadth (0),
130   myHeight  (0),
131   mySpacing (5),
132   myTextHeight (20)
133 {
134   SetDisplayMode (0);
135   myDrawer->SetupOwnShadingAspect();
136   myDrawer->ShadingAspect()->Aspect()->SetShadingModel (Graphic3d_TOSM_UNLIT);
137   myDrawer->ShadingAspect()->Aspect()->SetAlphaMode (Graphic3d_AlphaMode_Opaque);
138   myDrawer->ShadingAspect()->Aspect()->SetInteriorColor (Quantity_NOC_WHITE);
139 }
140
141 //=======================================================================
142 //function : GetLabel
143 //purpose  :
144 //=======================================================================
145 TCollection_ExtendedString AIS_ColorScale::GetLabel (const Standard_Integer theIndex) const
146 {
147   if (myLabelType == Aspect_TOCSD_USER)
148   {
149     if (theIndex >= myLabels.Lower()
150      || theIndex <= myLabels.Upper())
151     {
152       return myLabels.Value(theIndex);
153     }
154     return TCollection_ExtendedString();
155   }
156
157   // value to be shown depends on label position
158   const Standard_Real aVal = myIsLabelAtBorder
159                            ? GetIntervalValue (theIndex - 1)
160                            : (0.5 * (GetIntervalValue (theIndex - 1) + GetIntervalValue (theIndex)));
161
162   char aBuf[1024];
163   sprintf (aBuf, myFormat.ToCString(), aVal);
164   return TCollection_ExtendedString (aBuf);
165 }
166
167 //=======================================================================
168 //function : GetIntervalColor
169 //purpose  :
170 //=======================================================================
171 Quantity_Color AIS_ColorScale::GetIntervalColor (const Standard_Integer theIndex) const
172 {
173   if (myColorType== Aspect_TOCSD_USER)
174   {
175     if (theIndex <= 0 || theIndex > myColors.Length())
176     {
177       return Quantity_Color();
178     }
179     return myColors.Value (theIndex);
180   }
181
182   return colorFromValue (theIndex - 1, 0, myNbIntervals - 1);
183 }
184
185 //=======================================================================
186 //function : GetLabels
187 //purpose  :
188 //=======================================================================
189 void AIS_ColorScale::GetLabels (TColStd_SequenceOfExtendedString& theLabels) const
190 {
191   theLabels.Clear();
192   for (TColStd_SequenceOfExtendedString::Iterator aLabIter (myLabels); aLabIter.More(); aLabIter.Next())
193   {
194     theLabels.Append (aLabIter.Value());
195   }
196 }
197
198 //=======================================================================
199 //function : GetColors
200 //purpose  :
201 //=======================================================================
202 void AIS_ColorScale::GetColors (Aspect_SequenceOfColor& theColors) const
203 {
204   theColors.Clear();
205   for (Aspect_SequenceOfColor::Iterator aColorIter (myColors); aColorIter.More(); aColorIter.Next())
206   {
207     theColors.Append (aColorIter.Value());
208   }
209 }
210
211 //=======================================================================
212 //function : SetRange
213 //purpose  :
214 //=======================================================================
215 void AIS_ColorScale::SetRange (const Standard_Real theMin, const Standard_Real theMax)
216 {
217   myMin = Min (theMin, theMax);
218   myMax = Max (theMin, theMax);
219 }
220
221 //=======================================================================
222 //function : SetNumberOfIntervals
223 //purpose  :
224 //=======================================================================
225 void AIS_ColorScale::SetNumberOfIntervals (const Standard_Integer theNum)
226 {
227   if (theNum < 1)
228   {
229     return;
230   }
231
232   myNbIntervals = theNum;
233 }
234
235 //=======================================================================
236 //function : SetLabel
237 //purpose  :
238 //=======================================================================
239 void AIS_ColorScale::SetLabel (const TCollection_ExtendedString& theLabel,
240                                const Standard_Integer theIndex)
241 {
242   const Standard_Integer aLabIndex = (theIndex <= 0 ? myLabels.Length() + 1 : theIndex);
243   while (myLabels.Length() < aLabIndex)
244   {
245     myLabels.Append (TCollection_ExtendedString());
246   }
247   myLabels.SetValue (aLabIndex, theLabel);
248 }
249
250 //=======================================================================
251 //function : SetIntervalColor
252 //purpose  :
253 //=======================================================================
254 void AIS_ColorScale::SetIntervalColor (const Quantity_Color&  theColor,
255                                        const Standard_Integer theIndex)
256 {
257   const Standard_Integer aColorIndex = (theIndex <= 0 ? myColors.Length() + 1 : theIndex);
258   while (myColors.Length() < aColorIndex)
259   {
260     myColors.Append (Quantity_Color());
261   }
262   myColors.SetValue (aColorIndex, theColor);
263 }
264
265 //=======================================================================
266 //function : SetLabels
267 //purpose  :
268 //=======================================================================
269 void AIS_ColorScale::SetLabels (const TColStd_SequenceOfExtendedString& theSeq)
270 {
271   myLabels.Clear();
272   for (TColStd_SequenceOfExtendedString::Iterator aLabIter (theSeq); aLabIter.More(); aLabIter.Next())
273   {
274     myLabels.Append (aLabIter.Value());
275   }
276 }
277
278 //=======================================================================
279 //function : SetColors
280 //purpose  :
281 //=======================================================================
282 void AIS_ColorScale::SetColors (const Aspect_SequenceOfColor& theSeq)
283 {
284   myColors.Clear();
285   for (Aspect_SequenceOfColor::Iterator aColorIter (theSeq); aColorIter.More(); aColorIter.Next())
286   {
287     myColors.Append (aColorIter.Value());
288   }
289 }
290
291 //=======================================================================
292 //function : MakeUniformColors
293 //purpose  :
294 //=======================================================================
295 Aspect_SequenceOfColor AIS_ColorScale::MakeUniformColors (Standard_Integer theNbColors, 
296                                                           Standard_Real theLightness,
297                                                           Standard_Real theHueFrom,
298                                                           Standard_Real theHueTo)
299 {
300   Aspect_SequenceOfColor aResult;
301
302   // adjust range to be within (0, 360], with sign according to theHueFrom and theHueTo 
303   Standard_Real aHueRange = std::fmod (theHueTo - theHueFrom, 360.);
304   const Standard_Real aHueEps = Precision::Angular() * 180. / M_PI;
305   if (Abs (aHueRange) <= aHueEps)
306   {
307     aHueRange = (aHueRange < 0 ? -360. : 360.);
308   }
309
310   // treat limit cases
311   if (theNbColors < 1)
312   {
313     return aResult;
314   }
315   if (theNbColors == 1)
316   {
317     Standard_Real aHue = std::fmod (theHueFrom, 360.);
318     if (aHue < 0.)
319     {
320       aHue += 360.;
321     }
322     Quantity_Color aColor (theLightness, 130., aHue, Quantity_TOC_CIELch);
323     aResult.Append (aColor);
324     return aResult;
325   }
326
327   // discretize the range with 1 degree step
328   const int NBCOLORS = 2 + (int)Abs (aHueRange / 1.);
329   Standard_Real aHueStep = aHueRange / (NBCOLORS - 1);
330   NCollection_Array1<Quantity_Color> aGrid (0, NBCOLORS - 1);
331   for (Standard_Integer i = 0; i < NBCOLORS; i++)
332   {
333     Standard_Real aHue = std::fmod (theHueFrom + i * aHueStep, 360.);
334     if (aHue < 0.)
335     {
336       aHue += 360.;
337     }
338     aGrid(i).SetValues (theLightness, 130., aHue, Quantity_TOC_CIELch);
339   }
340
341   // and compute distances between each two colors in a grid
342   TColStd_Array1OfReal aMetric (0, NBCOLORS - 1);
343   Standard_Real aLength = 0.;
344   for (Standard_Integer i = 0, j = NBCOLORS - 1; i < NBCOLORS; j = i++)
345   {
346     aLength += (aMetric(i) = aGrid(i).DeltaE2000 (aGrid(j)));
347   }
348
349   // determine desired step by distance;
350   // normally we aim to distribute colors from start to end
351   // of the range, but if distance between first and last points of the range
352   // is less than that step (e.g. range is full 360 deg),
353   // then distribute by the whole 360 deg scope to ensure that first
354   // and last colors are sufficiently distanced
355   Standard_Real aDStep = (aLength - aMetric.First()) / (theNbColors - 1);
356   if (aMetric.First() < aDStep)
357   {
358     aDStep = aLength / theNbColors;
359   }
360
361   // generate sequence
362   aResult.Append(aGrid(0));
363   Standard_Real aParam = 0., aPrev = 0., aTarget = aDStep;
364   for (int i = 1; i < NBCOLORS; i++)
365   {
366     aParam = aPrev + aMetric(i);
367     while (aTarget <= aParam)
368     {
369       float aCoefPrev = float((aParam - aTarget) / (aParam - aPrev));
370       float aCoefCurr = float((aTarget - aPrev) / (aParam - aPrev));
371       Quantity_Color aColor (aGrid(i).Rgb() * aCoefCurr + aGrid(i-1).Rgb() * aCoefPrev);
372       aResult.Append (aColor);
373       aTarget += aDStep;
374     }
375     aPrev = aParam;
376   }
377   if (aResult.Length() < theNbColors)
378   {
379     aResult.Append (aGrid.Last());
380   }
381   Standard_ASSERT_VOID (aResult.Length() == theNbColors, "Failed to generate requested nb of colors");
382   return aResult;
383 }
384
385 //=======================================================================
386 //function : SizeHint
387 //purpose  :
388 //=======================================================================
389 void AIS_ColorScale::SizeHint (Standard_Integer& theWidth, Standard_Integer& theHeight) const
390 {
391   const Standard_Integer aTextHeight = TextHeight ("");
392   const Standard_Integer aColorWidth = 20;
393   Standard_Integer aTextWidth = 0;
394   if (myLabelPos != Aspect_TOCSP_NONE)
395   {
396     for (Standard_Integer aLabIter = (myIsLabelAtBorder ? 0 : 1); aLabIter <= myNbIntervals; ++aLabIter)
397     {
398       aTextWidth = Max (aTextWidth, TextWidth (GetLabel (aLabIter)));
399     }
400   }
401
402   const Standard_Integer aScaleWidth  = aColorWidth + aTextWidth + (aTextWidth ? 3 : 2) * mySpacing;
403   const Standard_Integer aScaleHeight = (Standard_Integer)(1.5 * (myNbIntervals + (myIsLabelAtBorder ? 2 : 1)) * aTextHeight);
404
405   Standard_Integer aTitleWidth  = 0;
406   Standard_Integer aTitleHeight = 0;
407   if (!myTitle.IsEmpty())
408   {
409     aTitleHeight = TextHeight (myTitle) + mySpacing;
410     aTitleWidth =  TextWidth  (myTitle) + mySpacing * 2;
411   }
412
413   theWidth  = Max (aTitleWidth, aScaleWidth);
414   theHeight = aScaleHeight + aTitleHeight;
415 }
416
417 //=======================================================================
418 //function : GetIntervalValue
419 //purpose  :
420 //=======================================================================
421 Standard_Real AIS_ColorScale::GetIntervalValue (const Standard_Integer theIndex) const
422 {
423   if (myNbIntervals <= 0)
424   {
425     return 0.0;
426   }
427
428   if (IsLogarithmic())
429   {
430     Standard_Real aMin     = myMin > 0 ? myMin : 1.0;
431     Standard_Real aDivisor = std::pow (myMax / aMin, 1.0 / myNbIntervals);
432     return aMin * std::pow (aDivisor,theIndex);
433   }
434
435   Standard_Real aNum = 0;
436   if (myNbIntervals > 0)
437   {
438     aNum = GetMin() + theIndex * (Abs (GetMax() - GetMin()) / myNbIntervals);
439   }
440   return aNum;
441 }
442
443 //=======================================================================
444 //function : colorFromValue
445 //purpose  :
446 //=======================================================================
447 Quantity_Color AIS_ColorScale::colorFromValue (const Standard_Real theValue,
448                                                const Standard_Real theMin,
449                                                const Standard_Real theMax) const
450 {
451   return colorFromValueEx (theValue, theMin, theMax, myColorHlsMin, myColorHlsMax);
452 }
453
454 //=======================================================================
455 //function : FindColor
456 //purpose  :
457 //=======================================================================
458 Standard_Boolean AIS_ColorScale::FindColor (const Standard_Real theValue,
459                                             Quantity_Color& theColor) const
460 {
461   if (theValue < myMin || theValue > myMax || myMax < myMin)
462   {
463     theColor = Quantity_Color();
464     return Standard_False;
465   }
466
467   if (myColorType == Aspect_TOCSD_USER)
468   {
469     const Standard_Integer anInterval = colorDiscreteInterval (theValue, myMin, myMax, myNbIntervals);
470     if (anInterval < myColors.Lower() || anInterval > myColors.Upper())
471     {
472       theColor = Quantity_Color();
473       return Standard_False;
474     }
475
476     theColor = myColors.Value (anInterval);
477     return Standard_True;
478   }
479
480   return FindColor (theValue, myMin, myMax, myNbIntervals, theColor);
481 }
482
483 //=======================================================================
484 //function : FindColor
485 //purpose  :
486 //=======================================================================
487 Standard_Boolean AIS_ColorScale::FindColor (const Standard_Real theValue,
488                                             const Standard_Real theMin,
489                                             const Standard_Real theMax,
490                                             const Standard_Integer theColorsCount,
491                                             const Graphic3d_Vec3d& theColorHlsMin,
492                                             const Graphic3d_Vec3d& theColorHlsMax,
493                                             Quantity_Color& theColor)
494 {
495   if (theValue < theMin || theValue > theMax || theMax < theMin)
496   {
497     return Standard_False;
498   }
499
500   const Standard_Integer anInterval = colorDiscreteInterval (theValue, theMin, theMax, theColorsCount);
501   theColor = colorFromValueEx (anInterval - 1, 0, theColorsCount - 1, theColorHlsMin, theColorHlsMax);
502   return Standard_True;
503 }
504
505 //=======================================================================
506 //function : computeMaxLabelWidth
507 //purpose  :
508 //=======================================================================
509 Standard_Integer AIS_ColorScale::computeMaxLabelWidth (const TColStd_SequenceOfExtendedString& theLabels) const
510 {
511   Standard_Integer aWidthMax = 0;
512   for (TColStd_SequenceOfExtendedString::Iterator aLabIter (theLabels); aLabIter.More(); aLabIter.Next())
513   {
514     if (!aLabIter.Value().IsEmpty())
515     {
516       aWidthMax = Max (aWidthMax, TextWidth (aLabIter.Value()));
517     }
518   }
519   return aWidthMax;
520 }
521
522 //=======================================================================
523 //function : updateTextAspect
524 //purpose  :
525 //=======================================================================
526 void AIS_ColorScale::updateTextAspect()
527 {
528   // update text aspect
529   const Quantity_Color aFgColor (hasOwnColor ? myDrawer->Color() : Quantity_NOC_WHITE);
530   if (!myDrawer->HasOwnTextAspect())
531   {
532     myDrawer->SetTextAspect (new Prs3d_TextAspect());
533     *myDrawer->TextAspect()->Aspect() = *myDrawer->Link()->TextAspect()->Aspect();
534   }
535
536   const Handle(Prs3d_TextAspect)& anAspect = myDrawer->TextAspect();
537   anAspect->SetColor  (aFgColor);
538   anAspect->SetHeight (myTextHeight);
539   anAspect->SetHorizontalJustification (Graphic3d_HTA_LEFT);
540   anAspect->SetVerticalJustification (Graphic3d_VTA_BOTTOM);
541   anAspect->Aspect()->SetTextZoomable (Standard_True);
542 }
543
544 //=======================================================================
545 //function : Compute
546 //purpose  :
547 //=======================================================================
548 void AIS_ColorScale::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
549                               const Handle(Prs3d_Presentation)& thePrs,
550                               const Standard_Integer theMode)
551 {
552   if (theMode != 0)
553   {
554     return;
555   }
556
557   // update text aspect
558   updateTextAspect();
559
560   const Standard_Integer aTitleOffset = !myTitle.IsEmpty() ? (myTextHeight + mySpacing) : 0;
561
562   const Standard_Integer aBarYOffset = myTextHeight / 2 + 2 * mySpacing; // a half-label offset
563   const Standard_Integer aBarBottom  = myYPos + aBarYOffset;
564   const Standard_Integer aBarTop     = myYPos + myHeight - aTitleOffset - aBarYOffset;
565   const Standard_Integer aBarHeight  = aBarTop - aBarBottom;
566
567   TColStd_SequenceOfExtendedString aLabels;
568   if (myLabelType == Aspect_TOCSD_USER)
569   {
570     aLabels = myLabels;
571   }
572   else
573   {
574     const Standard_Integer aNbLabels = myIsLabelAtBorder ? myNbIntervals + 1 : myNbIntervals;
575     for (Standard_Integer aLabIter = 1; aLabIter <= aNbLabels; ++aLabIter)
576     {
577       if (myIsReversed)
578       {
579         aLabels.Prepend (GetLabel (aLabIter));
580       }
581       else
582       {
583         aLabels.Append (GetLabel (aLabIter));
584       }
585     }
586   }
587
588   const Standard_Integer aTextWidth = myLabelPos != Aspect_TOCSP_NONE ? computeMaxLabelWidth (aLabels) : 0;
589   Standard_Integer aColorBreadth = Max (5, Min (20, myBreadth - aTextWidth - 3 * mySpacing));
590   if (myLabelPos == Aspect_TOCSP_CENTER
591    || myLabelPos == Aspect_TOCSP_NONE)
592   {
593     aColorBreadth += aTextWidth;
594   }
595
596   // draw title
597   Handle(Graphic3d_Group) aLabelsGroup;
598   if (!myTitle.IsEmpty()
599    || !aLabels.IsEmpty())
600   {
601     aLabelsGroup = thePrs->NewGroup();
602     aLabelsGroup->SetGroupPrimitivesAspect (myDrawer->TextAspect()->Aspect());
603   }
604   if (!myTitle.IsEmpty())
605   {
606     drawText (aLabelsGroup, myTitle,
607               myXPos + mySpacing,
608               aBarTop + aBarYOffset,
609               Graphic3d_VTA_BOTTOM);
610   }
611
612   // draw colors
613   drawColorBar (thePrs, aBarBottom, aBarHeight, aTextWidth, aColorBreadth);
614
615   // draw Labels
616   drawLabels (aLabelsGroup, aLabels, aBarBottom, aBarHeight, aTextWidth, aColorBreadth);
617 }
618
619 //=======================================================================
620 //function : drawColorBar
621 //purpose  :
622 //=======================================================================
623 void AIS_ColorScale::drawColorBar (const Handle(Prs3d_Presentation)& thePrs,
624                                    const Standard_Integer theBarBottom,
625                                    const Standard_Integer theBarHeight,
626                                    const Standard_Integer theMaxLabelWidth,
627                                    const Standard_Integer theColorBreadth)
628 {
629   const Standard_Real aStepY = Standard_Real(theBarHeight) / Standard_Real(myNbIntervals);
630   if (aStepY <= 0.0)
631   {
632     return;
633   }
634
635   // Draw colors
636   const Standard_Integer anXLeft = myLabelPos == Aspect_TOCSP_LEFT
637                                  ? myXPos + mySpacing + theMaxLabelWidth + (theMaxLabelWidth != 0 ? 1 : 0) * mySpacing
638                                  : myXPos + mySpacing;
639
640   Aspect_SequenceOfColor aColors;
641   for (Standard_Integer anIntervalIter = 1; anIntervalIter <= myNbIntervals; ++anIntervalIter)
642   {
643     if (myIsReversed)
644     {
645       aColors.Prepend (GetIntervalColor (anIntervalIter));
646     }
647     else
648     {
649       aColors.Append (GetIntervalColor (anIntervalIter));
650     }
651   }
652
653   Handle(Graphic3d_ArrayOfTriangles) aTriangles;
654   if (myIsSmooth
655    && myColorType == Aspect_TOCSD_USER)
656   {
657     // Smooth custom intervals, so that the color in the center of interval is equal to specified one
658     // (thus the halves of first and last intervals have solid color)
659     aTriangles = new Graphic3d_ArrayOfTriangles ((aColors.Length() + 1) * 4,     // quads
660                                                  (aColors.Length() + 1) * 2 * 3, // quads as triangles
661                                                  false, true);                   // per-vertex colors
662     Quantity_Color aColor1 (aColors.Value (1)), aColor2;
663     Standard_Integer       aSizeY        = Standard_Integer(aStepY / 2);
664     const Standard_Integer anYBottom     = theBarBottom + aSizeY;
665     Standard_Integer       anYBottomIter = anYBottom;
666     addColoredQuad (aTriangles,
667                     anXLeft, theBarBottom,
668                     theColorBreadth, aSizeY,
669                     aColor1, aColor1);
670     for (Standard_Integer aColorIter = 0; aColorIter < myNbIntervals - 1; ++aColorIter)
671     {
672       aColor1 = aColors.Value (aColorIter + 1);
673       aColor2 = aColors.Value (aColorIter + 2);
674       aSizeY  = anYBottom + Standard_Integer((aColorIter + 1) * aStepY) - anYBottomIter;
675       addColoredQuad (aTriangles,
676                       anXLeft, anYBottomIter,
677                       theColorBreadth, aSizeY,
678                       aColor1, aColor2);
679       anYBottomIter += aSizeY;
680     }
681     aColor2 = aColors.Value (myNbIntervals);
682     aSizeY  = theBarBottom + theBarHeight - anYBottomIter;
683     addColoredQuad (aTriangles,
684                     anXLeft, anYBottomIter,
685                     theColorBreadth, aSizeY,
686                     aColor2, aColor2);
687   }
688   else if (myIsSmooth)
689   {
690     // smooth transition between standard colors - without solid color regions at the beginning and end of full color range
691     const Quantity_Color aColorsFixed[5] =
692     {
693       colorFromValue (0, 0, 4),
694       colorFromValue (1, 0, 4),
695       colorFromValue (2, 0, 4),
696       colorFromValue (3, 0, 4),
697       colorFromValue (4, 0, 4)
698     };
699     aTriangles = new Graphic3d_ArrayOfTriangles (4 * 4,        // quads
700                                                  4 * 2 * 3,    // quads as triangles
701                                                  false, true); // per-vertex colors
702     Standard_Integer anYBottomIter = theBarBottom;
703     addColoredQuad (aTriangles,
704                     anXLeft, theBarBottom,
705                     theColorBreadth, theBarHeight / 4,
706                     aColorsFixed[0], aColorsFixed[1]);
707     anYBottomIter += theBarHeight / 4;
708     addColoredQuad (aTriangles,
709                     anXLeft, anYBottomIter,
710                     theColorBreadth, theBarHeight / 4,
711                     aColorsFixed[1], aColorsFixed[2]);
712     anYBottomIter += theBarHeight / 4;
713     addColoredQuad (aTriangles,
714                     anXLeft, anYBottomIter,
715                     theColorBreadth, theBarHeight / 4,
716                     aColorsFixed[2], aColorsFixed[3]);
717     anYBottomIter += theBarHeight / 4;
718     const Standard_Integer aLastSizeY  = theBarBottom + theBarHeight - anYBottomIter;
719     addColoredQuad (aTriangles,
720                     anXLeft, anYBottomIter,
721                     theColorBreadth, aLastSizeY,
722                     aColorsFixed[3], aColorsFixed[4]);
723   }
724   else
725   {
726     // no color smoothing
727     aTriangles = new Graphic3d_ArrayOfTriangles (aColors.Length() * 4,     // quads
728                                                  aColors.Length() * 2 * 3, // quads as triangles
729                                                  false, true);             // per-vertex colors
730     Standard_Integer anYBottomIter = theBarBottom;
731     for (Standard_Integer aColorIter = 0; aColorIter < myNbIntervals; ++aColorIter)
732     {
733       const Quantity_Color&  aColor = aColors.Value (aColorIter + 1);
734       const Standard_Integer aSizeY = theBarBottom + Standard_Integer((aColorIter + 1) * aStepY) - anYBottomIter;
735       addColoredQuad (aTriangles,
736                       anXLeft, anYBottomIter,
737                       theColorBreadth, aSizeY,
738                       aColor, aColor);
739       anYBottomIter += aSizeY;
740     }
741   }
742
743   Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
744   aGroup->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
745   aGroup->AddPrimitiveArray (aTriangles);
746
747   const Quantity_Color aFgColor (hasOwnColor ? myDrawer->Color() : Quantity_NOC_WHITE);
748   drawFrame (thePrs,
749              anXLeft - 1, theBarBottom - 1,
750              theColorBreadth + 2,
751              theBarHeight + 2,
752              aFgColor);
753 }
754
755 //=======================================================================
756 //function : drawLabels
757 //purpose  :
758 //=======================================================================
759 void AIS_ColorScale::drawLabels (const Handle(Graphic3d_Group)& theGroup,
760                                  const TColStd_SequenceOfExtendedString& theLabels,
761                                  const Standard_Integer theBarBottom,
762                                  const Standard_Integer theBarHeight,
763                                  const Standard_Integer theMaxLabelWidth,
764                                  const Standard_Integer theColorBreadth)
765 {
766   if (myLabelPos == Aspect_TOCSP_NONE
767    || theLabels.IsEmpty())
768   {
769     return;
770   }
771
772   const Standard_Integer aNbLabels    = theLabels.Size();
773   const Standard_Integer aNbIntervals = myIsLabelAtBorder ? aNbLabels - 1 : aNbLabels;
774   const Standard_Real    aStepY       = Standard_Real(theBarHeight) / Standard_Real(aNbIntervals);
775   if (aStepY <= 0.0)
776   {
777     return;
778   }
779
780   Standard_Integer aFilter = 0;
781   {
782     const Standard_Integer aTitleHeight = !myTitle.IsEmpty() ? (myTextHeight + 2 * mySpacing) : mySpacing;
783     const Standard_Integer aSpc         = myHeight - aTitleHeight - ((Min (aNbLabels, 2) + Abs (aNbLabels - aNbIntervals - 1)) * myTextHeight);
784     if (aSpc <= 0)
785     {
786       return;
787     }
788
789     const Standard_Real aVal = Standard_Real(aNbLabels) * myTextHeight / aSpc;
790     Standard_Real anIPart = 0.0;
791     Standard_Real anFPart = std::modf (aVal, &anIPart);
792     aFilter = (Standard_Integer )anIPart + (anFPart != 0 ? 1 : 0);
793   }
794   if (aFilter <= 0)
795   {
796     return;
797   }
798
799   Standard_Integer anXLeft = myXPos + mySpacing;
800   const Standard_Integer anAscent = 0;
801   switch (myLabelPos)
802   {
803     case Aspect_TOCSP_NONE:
804     case Aspect_TOCSP_LEFT:
805     {
806       break;
807     }
808     case Aspect_TOCSP_CENTER:
809     {
810       anXLeft += (theColorBreadth - theMaxLabelWidth) / 2;
811       break;
812     }
813     case Aspect_TOCSP_RIGHT:
814     {
815       anXLeft += theColorBreadth + mySpacing;
816       break;
817     }
818   }
819
820   Standard_Integer i1 = 0;
821   Standard_Integer i2 = aNbLabels - 1;
822   Standard_Integer aLast1 = i1;
823   Standard_Integer aLast2 = i2;
824   const Standard_Integer anYBottom = myIsLabelAtBorder
825                                    ? theBarBottom
826                                    : theBarBottom + Standard_Integer(aStepY / 2);
827   while (i2 - i1 >= aFilter || ( i2 == 0 && i1 == 0 ))
828   {
829     Standard_Integer aPos1 = i1;
830     Standard_Integer aPos2 = aNbLabels - 1 - i2;
831     if (aFilter && !(aPos1 % aFilter))
832     {
833       drawText (theGroup, theLabels.Value (i1 + 1),
834                 anXLeft, anYBottom + Standard_Integer(i1 * aStepY + anAscent),
835                 Graphic3d_VTA_CENTER);
836       aLast1 = i1;
837     }
838     if (aFilter && !(aPos2 % aFilter))
839     {
840       drawText (theGroup, theLabels.Value (i2 + 1),
841                 anXLeft, anYBottom + Standard_Integer(i2 * aStepY + anAscent),
842                 Graphic3d_VTA_CENTER);
843       aLast2 = i2;
844     }
845     i1++;
846     i2--;
847   }
848   Standard_Integer aPos = i1;
849   Standard_Integer i0   = -1;
850   while (aPos <= i2 && i0 == -1)
851   {
852     if (aFilter && !(aPos % aFilter)
853       && Abs (aPos - aLast1) >= aFilter
854       && Abs (aPos - aLast2) >= aFilter)
855     {
856       i0 = aPos;
857     }
858     aPos++;
859   }
860
861   if (i0 != -1)
862   {
863     drawText (theGroup, theLabels.Value (i0 + 1),
864               anXLeft, anYBottom + Standard_Integer(i0 * aStepY + anAscent),
865               Graphic3d_VTA_CENTER);
866   }
867 }
868
869 //=======================================================================
870 //function : drawFrame
871 //purpose  :
872 //=======================================================================
873 void AIS_ColorScale::drawFrame (const Handle(Prs3d_Presentation)& thePrs,
874                                 const Standard_Integer theX, const Standard_Integer theY,
875                                 const Standard_Integer theWidth, const Standard_Integer theHeight,
876                                 const Quantity_Color& theColor)
877 {
878   Handle(Graphic3d_ArrayOfPolylines) aPrim = new Graphic3d_ArrayOfPolylines(5);
879   aPrim->AddVertex (theX,            theY, 0.0);
880   aPrim->AddVertex (theX + theWidth, theY, 0.0);
881   aPrim->AddVertex (theX + theWidth, theY + theHeight, 0.0);
882   aPrim->AddVertex (theX,            theY + theHeight, 0.0);
883   aPrim->AddVertex (theX,            theY, 0.0);
884
885   Handle(Graphic3d_AspectLine3d) anAspect = new Graphic3d_AspectLine3d (theColor, Aspect_TOL_SOLID, 1.0);
886   Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
887   aGroup->SetGroupPrimitivesAspect (anAspect);
888   aGroup->AddPrimitiveArray (aPrim);
889 }
890
891 //=======================================================================
892 //function : drawText
893 //purpose  :
894 //=======================================================================
895 void AIS_ColorScale::drawText (const Handle(Graphic3d_Group)& theGroup,
896                                const TCollection_ExtendedString& theText,
897                                const Standard_Integer theX, const Standard_Integer theY,
898                                const Graphic3d_VerticalTextAlignment theVertAlignment)
899 {
900   const Handle(Prs3d_TextAspect)& anAspect = myDrawer->TextAspect();
901
902   Handle(Graphic3d_Text) aText = new Graphic3d_Text ((Standard_ShortReal)anAspect->Height());
903   aText->SetText (theText.ToExtString());
904   aText->SetOrientation (gp_Ax2 (gp_Pnt (theX, theY, 0.0), gp::DZ()));
905   aText->SetOwnAnchorPoint (Standard_False);
906   aText->SetVerticalAlignment (theVertAlignment);
907
908   theGroup->AddText (aText);
909 }
910
911 //=======================================================================
912 //function : TextWidth
913 //purpose  :
914 //=======================================================================
915 Standard_Integer AIS_ColorScale::TextWidth (const TCollection_ExtendedString& theText) const
916 {
917   Standard_Integer aWidth, anAscent, aDescent;
918   TextSize (theText, myTextHeight, aWidth, anAscent, aDescent);
919   return aWidth;
920 }
921
922 //=======================================================================
923 //function : TextHeight
924 //purpose  :
925 //=======================================================================
926 Standard_Integer AIS_ColorScale::TextHeight (const TCollection_ExtendedString& theText) const
927 {
928   Standard_Integer aWidth, anAscent, aDescent;
929   TextSize (theText, myTextHeight, aWidth, anAscent, aDescent);
930   return anAscent + aDescent;
931 }
932
933 //=======================================================================
934 //function : TextSize
935 //purpose  :
936 //=======================================================================
937 void AIS_ColorScale::TextSize (const TCollection_ExtendedString& theText,
938                                const Standard_Integer theHeight,
939                                Standard_Integer& theWidth,
940                                Standard_Integer& theAscent,
941                                Standard_Integer& theDescent) const
942 {
943   if (!HasInteractiveContext())
944   {
945     return;
946   }
947
948   Standard_ShortReal aWidth   = 10.0f;
949   Standard_ShortReal anAscent = 1.0f;
950   Standard_ShortReal aDescent = 1.0f;
951   const TCollection_AsciiString aText (theText);
952
953   const Handle(V3d_Viewer)&      aViewer = GetContext()->CurrentViewer();
954   const Handle(Graphic3d_CView)& aView   = aViewer->ActiveViewIterator().Value()->View();
955   aViewer->Driver()->TextSize (aView, aText.ToCString(), (Standard_ShortReal)theHeight, aWidth, anAscent, aDescent);
956   theWidth   = (Standard_Integer)aWidth;
957   theAscent  = (Standard_Integer)anAscent;
958   theDescent = (Standard_Integer)aDescent;
959 }