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