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