Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 70208672 : RGBColor::RGBColor(bool valid) :
66 70208672 : myValid(valid) {}
67 :
68 :
69 34666856 : RGBColor::RGBColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha, bool random) :
70 34666856 : myRed(red),
71 34666856 : myGreen(green),
72 34666856 : myBlue(blue),
73 34666856 : myAlpha(alpha),
74 34666856 : myRandom(random) {}
75 :
76 :
77 : unsigned char
78 78020828 : RGBColor::red() const {
79 78020828 : return myRed;
80 : }
81 :
82 :
83 : unsigned char
84 78020828 : RGBColor::green() const {
85 78020828 : return myGreen;
86 : }
87 :
88 :
89 : unsigned char
90 78020828 : RGBColor::blue() const {
91 78020828 : return myBlue;
92 : }
93 :
94 :
95 : unsigned char
96 145627616 : RGBColor::alpha() const {
97 145627616 : return myAlpha;
98 : }
99 :
100 :
101 : void
102 185 : RGBColor::set(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
103 185 : myRed = r;
104 185 : myGreen = g;
105 185 : myBlue = b;
106 185 : myAlpha = a;
107 185 : myValid = true;
108 185 : }
109 :
110 :
111 : void
112 742 : RGBColor::setAlpha(unsigned char alpha) {
113 742 : myAlpha = alpha;
114 742 : }
115 :
116 :
117 : void
118 5230 : RGBColor::setValid(const bool value) {
119 5230 : myValid = value;
120 5230 : }
121 :
122 :
123 : bool
124 1208 : RGBColor::isValid() const {
125 1208 : return myValid;
126 : }
127 :
128 :
129 : bool
130 7913 : RGBColor::isRandom() const {
131 7913 : return myRandom;
132 : }
133 :
134 :
135 : std::ostream&
136 102854 : operator<<(std::ostream& os, const RGBColor& col) {
137 102854 : if (col == RGBColor::RED) {
138 14579 : return os << "red";
139 : }
140 88275 : if (col == RGBColor::GREEN) {
141 13480 : return os << "green";
142 : }
143 74795 : if (col == RGBColor::BLUE) {
144 757 : return os << "blue";
145 : }
146 74038 : if (col == RGBColor::YELLOW) {
147 58372 : return os << "yellow";
148 : }
149 15666 : if (col == RGBColor::CYAN) {
150 108 : return os << "cyan";
151 : }
152 15558 : if (col == RGBColor::MAGENTA) {
153 187 : return os << "magenta";
154 : }
155 15371 : if (col == RGBColor::ORANGE) {
156 108 : return os << "orange";
157 : }
158 15263 : if (col == RGBColor::WHITE) {
159 1245 : return os << "white";
160 : }
161 14018 : if (col == RGBColor::BLACK) {
162 2077 : return os << "black";
163 : }
164 11941 : if (col == RGBColor::GREY) {
165 35 : return os << "grey";
166 : }
167 11906 : if (col == RGBColor::INVISIBLE) {
168 70 : return os << "invisible";
169 : }
170 11836 : os << static_cast<int>(col.myRed) << ","
171 11836 : << static_cast<int>(col.myGreen) << ","
172 11836 : << static_cast<int>(col.myBlue);
173 11836 : if (col.myAlpha < 255) {
174 910 : os << "," << static_cast<int>(col.myAlpha);
175 : }
176 : return os;
177 : }
178 :
179 :
180 : bool
181 507874 : RGBColor::operator==(const RGBColor& c) const {
182 507874 : return (myRed == c.myRed) && (myGreen == c.myGreen) && (myBlue == c.myBlue) && (myAlpha == c.myAlpha) && (myValid == c.myValid);
183 : }
184 :
185 :
186 : bool
187 4862840 : RGBColor::operator!=(const RGBColor& c) const {
188 4862840 : return (myRed != c.myRed) || (myGreen != c.myGreen) || (myBlue != c.myBlue) || (myAlpha != c.myAlpha) || (myValid != c.myValid);
189 : }
190 :
191 :
192 : RGBColor
193 0 : RGBColor::invertedColor() const {
194 : // obtain inverse colors
195 0 : const unsigned char r = (unsigned char)(255 - (int)myRed);
196 0 : const unsigned char g = (unsigned char)(255 - (int)myGreen);
197 0 : const unsigned char b = (unsigned char)(255 - (int)myBlue);
198 : // return inverted RBColor
199 0 : return RGBColor(r, g, b, myAlpha);
200 : }
201 :
202 :
203 : SumoRNG*
204 0 : RGBColor::getColorRNG() {
205 0 : return &myRNG;
206 : }
207 :
208 :
209 : RGBColor
210 3899841 : RGBColor::changedBrightness(int change, int toChange) const {
211 3899841 : const unsigned char red = (unsigned char)(MIN2(MAX2(myRed + change, 0), 255));
212 3899841 : const unsigned char blue = (unsigned char)(MIN2(MAX2(myBlue + change, 0), 255));
213 3899841 : const unsigned char green = (unsigned char)(MIN2(MAX2(myGreen + change, 0), 255));
214 3899841 : int changed = ((int)red - (int)myRed) + ((int)blue - (int)myBlue) + ((int)green - (int)myGreen);
215 3899841 : const RGBColor result(red, green, blue, myAlpha);
216 3899841 : if (changed == toChange * change) {
217 2057663 : return result;
218 1842178 : } else if (changed == 0) {
219 0 : return result;
220 : } else {
221 3699539 : const int maxedColors = (red != myRed + change ? 1 : 0) + (blue != myBlue + change ? 1 : 0) + (green != myGreen + change ? 1 : 0);
222 1842178 : if (maxedColors == 3) {
223 62 : return result;
224 : } else {
225 1842116 : const int toChangeNext = 3 - maxedColors;
226 1842116 : return result.changedBrightness((int)((toChange * change - changed) / toChangeNext), toChangeNext);
227 : }
228 : }
229 : }
230 :
231 :
232 : RGBColor
233 0 : RGBColor::changedAlpha(int change) const {
234 0 : int alpha = MIN2(MAX2((int)myAlpha + change, 0), 255);
235 0 : return RGBColor(myRed, myGreen, myBlue, (unsigned char)alpha);
236 : }
237 :
238 :
239 : RGBColor
240 0 : RGBColor::multiply(double factor) const {
241 0 : const unsigned char red = (unsigned char)floor(MIN2(MAX2(myRed * factor, 0.0), 255.0) + 0.5);
242 0 : const unsigned char blue = (unsigned char)floor(MIN2(MAX2(myBlue * factor, 0.0), 255.0) + 0.5);
243 0 : const unsigned char green = (unsigned char)floor(MIN2(MAX2(myGreen * factor, 0.0), 255.0) + 0.5);
244 0 : return RGBColor(red, green, blue, myAlpha);
245 : }
246 :
247 :
248 : RGBColor
249 92935 : RGBColor::parseColor(std::string coldef) {
250 92935 : coldef = StringUtils::to_lower_case(coldef);
251 92935 : if (coldef == "red") {
252 12613 : return RED;
253 : }
254 80322 : if (coldef == "green") {
255 9467 : return GREEN;
256 : }
257 70855 : if (coldef == "blue") {
258 13518 : return BLUE;
259 : }
260 57337 : if (coldef == "yellow") {
261 13481 : return YELLOW;
262 : }
263 43856 : if (coldef == "cyan") {
264 1640 : return CYAN;
265 : }
266 42216 : if (coldef == "magenta") {
267 907 : return MAGENTA;
268 : }
269 41309 : if (coldef == "orange") {
270 781 : return ORANGE;
271 : }
272 40528 : if (coldef == "white") {
273 467 : return WHITE;
274 : }
275 40061 : if (coldef == "black") {
276 190 : return BLACK;
277 : }
278 39871 : if (coldef == "grey" || coldef == "gray") {
279 846 : return GREY;
280 : }
281 39025 : if (coldef == "invisible") {
282 34 : return INVISIBLE;
283 : }
284 38991 : if (coldef == "random") {
285 0 : return fromHSV(RandHelper::rand(360, &myRNG),
286 : // prefer more saturated colors
287 : pow(RandHelper::rand(&myRNG), 0.3),
288 : // prefer brighter colors
289 0 : pow(RandHelper::rand(&myRNG), 0.3), true);
290 : }
291 : unsigned char r = 0;
292 : unsigned char g = 0;
293 : unsigned char b = 0;
294 : unsigned char a = 255;
295 38991 : if (coldef[0] == '#') {
296 884 : const int coldesc = StringUtils::hexToInt(coldef);
297 884 : if (coldef.length() == 7) {
298 884 : r = static_cast<unsigned char>((coldesc & 0xFF0000) >> 16);
299 884 : g = static_cast<unsigned char>((coldesc & 0x00FF00) >> 8);
300 884 : b = coldesc & 0xFF;
301 0 : } else if (coldef.length() == 9) {
302 0 : r = static_cast<unsigned char>((coldesc & 0xFF000000) >> 24);
303 0 : g = static_cast<unsigned char>((coldesc & 0x00FF0000) >> 16);
304 0 : b = static_cast<unsigned char>((coldesc & 0x0000FF00) >> 8);
305 0 : a = coldesc & 0xFF;
306 : } else {
307 0 : throw EmptyData();
308 : }
309 : } else {
310 114321 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
311 38107 : if (st.size() == 3 || st.size() == 4) {
312 : try {
313 38094 : r = static_cast<unsigned char>(StringUtils::toInt(st[0]));
314 20941 : g = static_cast<unsigned char>(StringUtils::toInt(st[1]));
315 20868 : b = static_cast<unsigned char>(StringUtils::toInt(st[2]));
316 20843 : if (st.size() == 4) {
317 1050 : a = static_cast<unsigned char>(StringUtils::toInt(st[3]));
318 : }
319 20843 : if (r <= 1 && g <= 1 && b <= 1 && (st.size() == 3 || a <= 1)) {
320 7556 : throw NumberFormatException("(color component) " + coldef);
321 : }
322 21029 : } catch (NumberFormatException&) {
323 21029 : r = static_cast<unsigned char>(StringUtils::toDouble(st[0]) * 255. + 0.5);
324 21028 : g = static_cast<unsigned char>(StringUtils::toDouble(st[1]) * 255. + 0.5);
325 21028 : b = static_cast<unsigned char>(StringUtils::toDouble(st[2]) * 255. + 0.5);
326 21028 : if (st.size() == 4) {
327 130 : a = static_cast<unsigned char>(StringUtils::toDouble(st[3]) * 255. + 0.5);
328 : }
329 21029 : }
330 : } else {
331 39 : throw FormatException("Invalid color definition '" + coldef + "'");
332 : }
333 38107 : }
334 38977 : return RGBColor(r, g, b, a);
335 : }
336 :
337 :
338 : bool
339 0 : RGBColor::isColor(std::string coldef) {
340 : // check if is defined using a previous defined color
341 0 : coldef = StringUtils::to_lower_case(coldef);
342 0 : if ((coldef == "red") || (coldef == "green") || (coldef == "blue") ||
343 0 : (coldef == "yellow") || (coldef == "cyan") || (coldef == "magenta") ||
344 0 : (coldef == "orange") || (coldef == "white") || (coldef == "black") ||
345 0 : (coldef == "grey") || (coldef == "gray") || (coldef == "invisible") ||
346 0 : (coldef == "random")) {
347 : return true;
348 : }
349 : // check if is defined using an hexadecimal value
350 0 : if (coldef[0] == '#') {
351 0 : if (StringUtils::isHex(coldef)) {
352 0 : return ((coldef.length() == 7) || (coldef.length() == 9));
353 : } else {
354 : return false;
355 : }
356 : }
357 : // Check definition by tuple of rgb or rgba
358 0 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
359 0 : if (st.size() == 3) {
360 0 : return StringUtils::isDouble(st[0]) && StringUtils::isDouble(st[1]) && StringUtils::isDouble(st[2]);
361 0 : } else if (st.size() == 4) {
362 0 : return StringUtils::isDouble(st[0]) && StringUtils::isDouble(st[1]) &&
363 0 : StringUtils::isDouble(st[2]) && StringUtils::isDouble(st[3]);
364 : } else {
365 : return false;
366 : }
367 0 : }
368 :
369 :
370 : RGBColor
371 3250 : RGBColor::parseColorReporting(
372 : const std::string& coldef, const std::string& objecttype,
373 : const char* objectid, bool report, bool& ok) {
374 : UNUSED_PARAMETER(report);
375 : try {
376 6500 : return parseColor(coldef);
377 0 : } catch (NumberFormatException&) {
378 0 : } catch (EmptyData&) {
379 0 : }
380 0 : ok = false;
381 0 : std::ostringstream oss;
382 0 : oss << "Attribute 'color' in definition of ";
383 0 : if (objectid == nullptr) {
384 0 : oss << "a ";
385 : }
386 : oss << objecttype;
387 0 : if (objectid != nullptr) {
388 0 : oss << " '" << objectid << "'";
389 : }
390 0 : oss << " is not a valid color.";
391 0 : WRITE_ERROR(oss.str());
392 0 : return RGBColor();
393 0 : }
394 :
395 :
396 : RGBColor
397 123812 : RGBColor::interpolate(const RGBColor& minColor, const RGBColor& maxColor, double weight) {
398 123812 : if (weight < 0) {
399 : weight = 0;
400 : }
401 123811 : if (weight > 1) {
402 : weight = 1;
403 : }
404 123812 : const unsigned char r = (unsigned char)((int)minColor.myRed + (((int)maxColor.myRed - (int)minColor.myRed) * weight));
405 123812 : const unsigned char g = (unsigned char)((int)minColor.myGreen + (((int)maxColor.myGreen - (int)minColor.myGreen) * weight));
406 123812 : const unsigned char b = (unsigned char)((int)minColor.myBlue + (((int)maxColor.myBlue - (int)minColor.myBlue) * weight));
407 123812 : const unsigned char a = (unsigned char)((int)minColor.myAlpha + (((int)maxColor.myAlpha - (int)minColor.myAlpha) * weight));
408 123812 : return RGBColor(r, g, b, a);
409 : }
410 :
411 :
412 : RGBColor
413 0 : RGBColor::fromHSV(double h, double s, double v, bool random) {
414 : h = MIN2(MAX2(h, 0.), 360.);
415 : s = MIN2(MAX2(s, 0.), 1.);
416 : v = MIN2(MAX2(v, 0.), 1.);
417 0 : h /= 60.;
418 0 : const int i = int(floor(h));
419 0 : double f = h - i;
420 0 : if (i % 2 == 0) {
421 0 : f = 1. - f;
422 : }
423 0 : const unsigned char m = static_cast<unsigned char>(v * (1 - s) * 255. + 0.5);
424 0 : const unsigned char n = static_cast<unsigned char>(v * (1 - s * f) * 255. + 0.5);
425 0 : const unsigned char vv = static_cast<unsigned char>(v * 255. + 0.5);
426 0 : switch (i) {
427 0 : case 0:
428 : case 6:
429 0 : return RGBColor(vv, n, m, 255, random);
430 0 : case 1:
431 0 : return RGBColor(n, vv, m, 255, random);
432 0 : case 2:
433 0 : return RGBColor(m, vv, n, 255, random);
434 0 : case 3:
435 0 : return RGBColor(m, n, vv, 255, random);
436 0 : case 4:
437 0 : return RGBColor(n, m, vv, 255, random);
438 0 : case 5:
439 0 : return RGBColor(vv, m, n, 255, random);
440 : }
441 0 : return RGBColor(255, 255, 255, 255, random);
442 : }
443 :
444 :
445 : RGBColor
446 0 : RGBColor::randomHue(double s, double v, bool random) {
447 0 : return fromHSV(RandHelper::rand(360, &myRNG), s, v, random);
448 : }
449 :
450 :
451 : /****************************************************************************/
|