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