Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file RGBColor.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Laura Bieker
19 : /// @date Sept 2002
20 : ///
21 : // A RGB-color definition
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <cmath>
26 : #include <cassert>
27 : #include <string>
28 : #include <sstream>
29 : #include <utils/common/RandHelper.h>
30 : #include <utils/common/StringTokenizer.h>
31 : #include <utils/common/ToString.h>
32 : #include <utils/common/StringUtils.h>
33 : #include <utils/common/MsgHandler.h>
34 : #include <utils/common/StdDefs.h>
35 :
36 : #include "RGBColor.h"
37 :
38 :
39 : // ===========================================================================
40 : // static member definitions
41 : // ===========================================================================
42 :
43 : const RGBColor RGBColor::RED = RGBColor(255, 0, 0, 255);
44 : const RGBColor RGBColor::GREEN = RGBColor(0, 255, 0, 255);
45 : const RGBColor RGBColor::BLUE = RGBColor(0, 0, 255, 255);
46 : const RGBColor RGBColor::YELLOW = RGBColor(255, 255, 0, 255);
47 : const RGBColor RGBColor::CYAN = RGBColor(0, 255, 255, 255);
48 : const RGBColor RGBColor::MAGENTA = RGBColor(255, 0, 255, 255);
49 : const RGBColor RGBColor::ORANGE = RGBColor(255, 128, 0, 255);
50 : const RGBColor RGBColor::WHITE = RGBColor(255, 255, 255, 255);
51 : const RGBColor RGBColor::BLACK = RGBColor(0, 0, 0, 255);
52 : const RGBColor RGBColor::GREY = RGBColor(128, 128, 128, 255);
53 : const RGBColor RGBColor::INVISIBLE = RGBColor(0, 0, 0, 0);
54 :
55 : const RGBColor RGBColor::DEFAULT_COLOR = RGBColor::YELLOW;
56 : const std::string RGBColor::DEFAULT_COLOR_STRING = toString(RGBColor::DEFAULT_COLOR);
57 :
58 : // random colors do not affect the simulation. No initialization is necessary
59 : SumoRNG RGBColor::myRNG("color");
60 :
61 : // ===========================================================================
62 : // method definitions
63 : // ===========================================================================
64 :
65 60578130 : RGBColor::RGBColor(bool valid)
66 60578130 : : myRed(0), myGreen(0), myBlue(0), myAlpha(0), myValid(valid) {}
67 :
68 :
69 31560956 : RGBColor::RGBColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
70 31560956 : : myRed(red), myGreen(green), myBlue(blue), myAlpha(alpha), myValid(true) {}
71 :
72 :
73 : unsigned char
74 67223016 : RGBColor::red() const {
75 67223016 : return myRed;
76 : }
77 :
78 :
79 : unsigned char
80 67223016 : RGBColor::green() const {
81 67223016 : return myGreen;
82 : }
83 :
84 :
85 : unsigned char
86 67223016 : RGBColor::blue() const {
87 67223016 : return myBlue;
88 : }
89 :
90 :
91 : unsigned char
92 123584087 : RGBColor::alpha() const {
93 123584087 : return myAlpha;
94 : }
95 :
96 :
97 : void
98 185 : RGBColor::set(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
99 185 : myRed = r;
100 185 : myGreen = g;
101 185 : myBlue = b;
102 185 : myAlpha = a;
103 185 : myValid = true;
104 185 : }
105 :
106 :
107 : void
108 801 : RGBColor::setAlpha(unsigned char alpha) {
109 801 : myAlpha = alpha;
110 801 : }
111 :
112 :
113 : void
114 4822 : RGBColor::setValid(const bool value) {
115 4822 : myValid = value;
116 4822 : }
117 :
118 :
119 : bool
120 1125 : RGBColor::isValid() const {
121 1125 : return myValid;
122 : }
123 :
124 :
125 : std::ostream&
126 90049 : operator<<(std::ostream& os, const RGBColor& col) {
127 90049 : if (col == RGBColor::RED) {
128 11251 : return os << "red";
129 : }
130 78798 : if (col == RGBColor::GREEN) {
131 10632 : return os << "green";
132 : }
133 68166 : if (col == RGBColor::BLUE) {
134 763 : return os << "blue";
135 : }
136 67403 : if (col == RGBColor::YELLOW) {
137 52115 : return os << "yellow";
138 : }
139 15288 : if (col == RGBColor::CYAN) {
140 108 : return os << "cyan";
141 : }
142 15180 : if (col == RGBColor::MAGENTA) {
143 187 : return os << "magenta";
144 : }
145 14993 : if (col == RGBColor::ORANGE) {
146 108 : return os << "orange";
147 : }
148 14885 : if (col == RGBColor::WHITE) {
149 1245 : return os << "white";
150 : }
151 13640 : if (col == RGBColor::BLACK) {
152 2081 : return os << "black";
153 : }
154 11559 : if (col == RGBColor::GREY) {
155 35 : return os << "grey";
156 : }
157 11524 : if (col == RGBColor::INVISIBLE) {
158 70 : return os << "invisible";
159 : }
160 11454 : os << static_cast<int>(col.myRed) << ","
161 11454 : << static_cast<int>(col.myGreen) << ","
162 11454 : << static_cast<int>(col.myBlue);
163 11454 : if (col.myAlpha < 255) {
164 910 : os << "," << static_cast<int>(col.myAlpha);
165 : }
166 : return os;
167 : }
168 :
169 :
170 : bool
171 401490 : RGBColor::operator==(const RGBColor& c) const {
172 401490 : return (myRed == c.myRed) && (myGreen == c.myGreen) && (myBlue == c.myBlue) && (myAlpha == c.myAlpha) && (myValid == c.myValid);
173 : }
174 :
175 :
176 : bool
177 4594964 : RGBColor::operator!=(const RGBColor& c) const {
178 4594964 : return (myRed != c.myRed) || (myGreen != c.myGreen) || (myBlue != c.myBlue) || (myAlpha != c.myAlpha) || (myValid != c.myValid);
179 : }
180 :
181 :
182 : RGBColor
183 0 : RGBColor::invertedColor() const {
184 : // obtain inverse colors
185 0 : const unsigned char r = (unsigned char)(255 - (int)myRed);
186 0 : const unsigned char g = (unsigned char)(255 - (int)myGreen);
187 0 : const unsigned char b = (unsigned char)(255 - (int)myBlue);
188 : // return inverted RBColor
189 0 : return RGBColor(r, g, b, myAlpha);
190 : }
191 :
192 :
193 : SumoRNG*
194 0 : RGBColor::getColorRNG() {
195 0 : return &myRNG;
196 : }
197 :
198 :
199 : RGBColor
200 3740505 : RGBColor::changedBrightness(int change, int toChange) const {
201 3740505 : const unsigned char red = (unsigned char)(MIN2(MAX2(myRed + change, 0), 255));
202 3740505 : const unsigned char blue = (unsigned char)(MIN2(MAX2(myBlue + change, 0), 255));
203 3740505 : const unsigned char green = (unsigned char)(MIN2(MAX2(myGreen + change, 0), 255));
204 3740505 : int changed = ((int)red - (int)myRed) + ((int)blue - (int)myBlue) + ((int)green - (int)myGreen);
205 3740505 : const RGBColor result(red, green, blue, myAlpha);
206 3740505 : if (changed == toChange * change) {
207 1932073 : return result;
208 1808432 : } else if (changed == 0) {
209 0 : return result;
210 : } else {
211 3636143 : const int maxedColors = (red != myRed + change ? 1 : 0) + (blue != myBlue + change ? 1 : 0) + (green != myGreen + change ? 1 : 0);
212 1808432 : if (maxedColors == 3) {
213 29 : return result;
214 : } else {
215 1808403 : const int toChangeNext = 3 - maxedColors;
216 1808403 : return result.changedBrightness((int)((toChange * change - changed) / toChangeNext), toChangeNext);
217 : }
218 : }
219 : }
220 :
221 :
222 : RGBColor
223 0 : RGBColor::changedAlpha(int change) const {
224 0 : int alpha = MIN2(MAX2((int)myAlpha + change, 0), 255);
225 0 : return RGBColor(myRed, myGreen, myBlue, (unsigned char)alpha);
226 : }
227 :
228 :
229 : RGBColor
230 0 : RGBColor::multiply(double factor) const {
231 0 : const unsigned char red = (unsigned char)floor(MIN2(MAX2(myRed * factor, 0.0), 255.0) + 0.5);
232 0 : const unsigned char blue = (unsigned char)floor(MIN2(MAX2(myBlue * factor, 0.0), 255.0) + 0.5);
233 0 : const unsigned char green = (unsigned char)floor(MIN2(MAX2(myGreen * factor, 0.0), 255.0) + 0.5);
234 0 : return RGBColor(red, green, blue, myAlpha);
235 : }
236 :
237 :
238 : RGBColor
239 86927 : RGBColor::parseColor(std::string coldef) {
240 86927 : coldef = StringUtils::to_lower_case(coldef);
241 86927 : if (coldef == "red") {
242 11124 : return RED;
243 : }
244 75803 : if (coldef == "green") {
245 8041 : return GREEN;
246 : }
247 67762 : if (coldef == "blue") {
248 13467 : return BLUE;
249 : }
250 54295 : if (coldef == "yellow") {
251 13453 : return YELLOW;
252 : }
253 40842 : if (coldef == "cyan") {
254 1634 : return CYAN;
255 : }
256 39208 : if (coldef == "magenta") {
257 910 : return MAGENTA;
258 : }
259 38298 : if (coldef == "orange") {
260 786 : return ORANGE;
261 : }
262 37512 : if (coldef == "white") {
263 466 : return WHITE;
264 : }
265 37046 : if (coldef == "black") {
266 190 : return BLACK;
267 : }
268 36856 : if (coldef == "grey" || coldef == "gray") {
269 846 : return GREY;
270 : }
271 36010 : if (coldef == "invisible") {
272 34 : return INVISIBLE;
273 : }
274 35976 : if (coldef == "random") {
275 0 : return fromHSV(RandHelper::rand(360, &myRNG),
276 : // prefer more saturated colors
277 : pow(RandHelper::rand(&myRNG), 0.3),
278 : // prefer brighter colors
279 0 : pow(RandHelper::rand(&myRNG), 0.3));
280 : }
281 : unsigned char r = 0;
282 : unsigned char g = 0;
283 : unsigned char b = 0;
284 : unsigned char a = 255;
285 35976 : if (coldef[0] == '#') {
286 853 : const int coldesc = StringUtils::hexToInt(coldef);
287 853 : if (coldef.length() == 7) {
288 853 : r = static_cast<unsigned char>((coldesc & 0xFF0000) >> 16);
289 853 : g = static_cast<unsigned char>((coldesc & 0x00FF00) >> 8);
290 853 : b = coldesc & 0xFF;
291 0 : } else if (coldef.length() == 9) {
292 0 : r = static_cast<unsigned char>((coldesc & 0xFF000000) >> 24);
293 0 : g = static_cast<unsigned char>((coldesc & 0x00FF0000) >> 16);
294 0 : b = static_cast<unsigned char>((coldesc & 0x0000FF00) >> 8);
295 0 : a = coldesc & 0xFF;
296 : } else {
297 0 : throw EmptyData();
298 : }
299 : } else {
300 105369 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
301 35123 : if (st.size() == 3 || st.size() == 4) {
302 : try {
303 35110 : r = static_cast<unsigned char>(StringUtils::toInt(st[0]));
304 21124 : g = static_cast<unsigned char>(StringUtils::toInt(st[1]));
305 21051 : b = static_cast<unsigned char>(StringUtils::toInt(st[2]));
306 21026 : if (st.size() == 4) {
307 1050 : a = static_cast<unsigned char>(StringUtils::toInt(st[3]));
308 : }
309 21026 : if (r <= 1 && g <= 1 && b <= 1 && (st.size() == 3 || a <= 1)) {
310 7366 : throw NumberFormatException("(color component) " + coldef);
311 : }
312 17767 : } catch (NumberFormatException&) {
313 17767 : r = static_cast<unsigned char>(StringUtils::toDouble(st[0]) * 255. + 0.5);
314 17766 : g = static_cast<unsigned char>(StringUtils::toDouble(st[1]) * 255. + 0.5);
315 17766 : b = static_cast<unsigned char>(StringUtils::toDouble(st[2]) * 255. + 0.5);
316 17766 : if (st.size() == 4) {
317 130 : a = static_cast<unsigned char>(StringUtils::toDouble(st[3]) * 255. + 0.5);
318 : }
319 17767 : }
320 : } else {
321 39 : throw FormatException("Invalid color definition '" + coldef + "'");
322 : }
323 35123 : }
324 35962 : return RGBColor(r, g, b, a);
325 : }
326 :
327 :
328 : bool
329 0 : RGBColor::isColor(std::string coldef) {
330 : // check if is defined using a previous defined color
331 0 : coldef = StringUtils::to_lower_case(coldef);
332 0 : if ((coldef == "red") || (coldef == "green") || (coldef == "blue") ||
333 0 : (coldef == "yellow") || (coldef == "cyan") || (coldef == "magenta") ||
334 0 : (coldef == "orange") || (coldef == "white") || (coldef == "black") ||
335 0 : (coldef == "grey") || (coldef == "gray") || (coldef == "invisible") ||
336 0 : (coldef == "random")) {
337 : return true;
338 : }
339 : // check if is defined using an hexadecimal value
340 0 : if (coldef[0] == '#') {
341 0 : if (StringUtils::isHex(coldef)) {
342 0 : return ((coldef.length() == 7) || (coldef.length() == 9));
343 : } else {
344 : return false;
345 : }
346 : }
347 : // Check definition by tuple of rgb or rgba
348 0 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
349 0 : if (st.size() == 3) {
350 0 : return StringUtils::isDouble(st[0]) && StringUtils::isDouble(st[1]) && StringUtils::isDouble(st[2]);
351 0 : } else if (st.size() == 4) {
352 0 : return StringUtils::isDouble(st[0]) && StringUtils::isDouble(st[1]) &&
353 0 : StringUtils::isDouble(st[2]) && StringUtils::isDouble(st[3]);
354 : } else {
355 : return false;
356 : }
357 0 : }
358 :
359 :
360 : RGBColor
361 3250 : RGBColor::parseColorReporting(
362 : const std::string& coldef, const std::string& objecttype,
363 : const char* objectid, bool report, bool& ok) {
364 : UNUSED_PARAMETER(report);
365 : try {
366 6500 : return parseColor(coldef);
367 0 : } catch (NumberFormatException&) {
368 0 : } catch (EmptyData&) {
369 0 : }
370 0 : ok = false;
371 0 : std::ostringstream oss;
372 0 : oss << "Attribute 'color' in definition of ";
373 0 : if (objectid == nullptr) {
374 0 : oss << "a ";
375 : }
376 : oss << objecttype;
377 0 : if (objectid != nullptr) {
378 0 : oss << " '" << objectid << "'";
379 : }
380 0 : oss << " is not a valid color.";
381 0 : WRITE_ERROR(oss.str());
382 0 : return RGBColor();
383 0 : }
384 :
385 :
386 : RGBColor
387 124693 : RGBColor::interpolate(const RGBColor& minColor, const RGBColor& maxColor, double weight) {
388 124693 : if (weight < 0) {
389 : weight = 0;
390 : }
391 124692 : if (weight > 1) {
392 : weight = 1;
393 : }
394 124693 : const unsigned char r = (unsigned char)((int)minColor.myRed + (((int)maxColor.myRed - (int)minColor.myRed) * weight));
395 124693 : const unsigned char g = (unsigned char)((int)minColor.myGreen + (((int)maxColor.myGreen - (int)minColor.myGreen) * weight));
396 124693 : const unsigned char b = (unsigned char)((int)minColor.myBlue + (((int)maxColor.myBlue - (int)minColor.myBlue) * weight));
397 124693 : const unsigned char a = (unsigned char)((int)minColor.myAlpha + (((int)maxColor.myAlpha - (int)minColor.myAlpha) * weight));
398 124693 : return RGBColor(r, g, b, a);
399 : }
400 :
401 :
402 : RGBColor
403 0 : RGBColor::fromHSV(double h, double s, double v) {
404 : h = MIN2(MAX2(h, 0.), 360.);
405 : s = MIN2(MAX2(s, 0.), 1.);
406 : v = MIN2(MAX2(v, 0.), 1.);
407 0 : h /= 60.;
408 0 : const int i = int(floor(h));
409 0 : double f = h - i;
410 0 : if (i % 2 == 0) {
411 0 : f = 1. - f;
412 : }
413 0 : const unsigned char m = static_cast<unsigned char>(v * (1 - s) * 255. + 0.5);
414 0 : const unsigned char n = static_cast<unsigned char>(v * (1 - s * f) * 255. + 0.5);
415 0 : const unsigned char vv = static_cast<unsigned char>(v * 255. + 0.5);
416 0 : switch (i) {
417 0 : case 0:
418 : case 6:
419 0 : return RGBColor(vv, n, m, 255);
420 0 : case 1:
421 0 : return RGBColor(n, vv, m, 255);
422 0 : case 2:
423 0 : return RGBColor(m, vv, n, 255);
424 0 : case 3:
425 0 : return RGBColor(m, n, vv, 255);
426 0 : case 4:
427 0 : return RGBColor(n, m, vv, 255);
428 0 : case 5:
429 0 : return RGBColor(vv, m, n, 255);
430 : }
431 0 : return RGBColor(255, 255, 255, 255);
432 : }
433 :
434 : RGBColor
435 0 : RGBColor::randomHue(double s, double v) {
436 0 : return fromHSV(RandHelper::rand(360, &myRNG), s, v);
437 : }
438 :
439 :
440 : /****************************************************************************/
|