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