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