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 70386998 : RGBColor::RGBColor(bool valid)
66 70386998 : : myRed(0), myGreen(0), myBlue(0), myAlpha(0), myValid(valid) {}
67 :
68 :
69 33041132 : RGBColor::RGBColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
70 33041132 : : myRed(red), myGreen(green), myBlue(blue), myAlpha(alpha), myValid(true) {}
71 :
72 :
73 : unsigned char
74 78169756 : RGBColor::red() const {
75 78169756 : return myRed;
76 : }
77 :
78 :
79 : unsigned char
80 78169756 : RGBColor::green() const {
81 78169756 : return myGreen;
82 : }
83 :
84 :
85 : unsigned char
86 78169756 : RGBColor::blue() const {
87 78169756 : return myBlue;
88 : }
89 :
90 :
91 : unsigned char
92 145009609 : RGBColor::alpha() const {
93 145009609 : return myAlpha;
94 : }
95 :
96 :
97 : void
98 184 : RGBColor::set(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
99 184 : myRed = r;
100 184 : myGreen = g;
101 184 : myBlue = b;
102 184 : myAlpha = a;
103 184 : myValid = true;
104 184 : }
105 :
106 :
107 : void
108 805 : RGBColor::setAlpha(unsigned char alpha) {
109 805 : myAlpha = alpha;
110 805 : }
111 :
112 :
113 : void
114 4887 : RGBColor::setValid(const bool value) {
115 4887 : myValid = value;
116 4887 : }
117 :
118 :
119 : bool
120 1200 : RGBColor::isValid() const {
121 1200 : return myValid;
122 : }
123 :
124 :
125 : std::ostream&
126 98464 : operator<<(std::ostream& os, const RGBColor& col) {
127 98464 : if (col == RGBColor::RED) {
128 14377 : return os << "red";
129 : }
130 84087 : if (col == RGBColor::GREEN) {
131 13482 : return os << "green";
132 : }
133 70605 : if (col == RGBColor::BLUE) {
134 755 : return os << "blue";
135 : }
136 69850 : if (col == RGBColor::YELLOW) {
137 54548 : return os << "yellow";
138 : }
139 15302 : if (col == RGBColor::CYAN) {
140 108 : return os << "cyan";
141 : }
142 15194 : if (col == RGBColor::MAGENTA) {
143 187 : return os << "magenta";
144 : }
145 15007 : if (col == RGBColor::ORANGE) {
146 108 : return os << "orange";
147 : }
148 14899 : if (col == RGBColor::WHITE) {
149 1245 : return os << "white";
150 : }
151 13654 : if (col == RGBColor::BLACK) {
152 2081 : return os << "black";
153 : }
154 11573 : if (col == RGBColor::GREY) {
155 35 : return os << "grey";
156 : }
157 11538 : if (col == RGBColor::INVISIBLE) {
158 70 : return os << "invisible";
159 : }
160 11468 : os << static_cast<int>(col.myRed) << ","
161 11468 : << static_cast<int>(col.myGreen) << ","
162 11468 : << static_cast<int>(col.myBlue);
163 11468 : if (col.myAlpha < 255) {
164 910 : os << "," << static_cast<int>(col.myAlpha);
165 : }
166 : return os;
167 : }
168 :
169 :
170 : bool
171 487867 : RGBColor::operator==(const RGBColor& c) const {
172 487867 : return (myRed == c.myRed) && (myGreen == c.myGreen) && (myBlue == c.myBlue) && (myAlpha == c.myAlpha) && (myValid == c.myValid);
173 : }
174 :
175 :
176 : bool
177 4810364 : RGBColor::operator!=(const RGBColor& c) const {
178 4810364 : 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 3812084 : RGBColor::changedBrightness(int change, int toChange) const {
201 3812084 : const unsigned char red = (unsigned char)(MIN2(MAX2(myRed + change, 0), 255));
202 3812084 : const unsigned char blue = (unsigned char)(MIN2(MAX2(myBlue + change, 0), 255));
203 3812084 : const unsigned char green = (unsigned char)(MIN2(MAX2(myGreen + change, 0), 255));
204 3812084 : int changed = ((int)red - (int)myRed) + ((int)blue - (int)myBlue) + ((int)green - (int)myGreen);
205 3812084 : const RGBColor result(red, green, blue, myAlpha);
206 3812084 : if (changed == toChange * change) {
207 1973830 : return result;
208 1838254 : } else if (changed == 0) {
209 0 : return result;
210 : } else {
211 3692381 : const int maxedColors = (red != myRed + change ? 1 : 0) + (blue != myBlue + change ? 1 : 0) + (green != myGreen + change ? 1 : 0);
212 1838254 : if (maxedColors == 3) {
213 79 : return result;
214 : } else {
215 1838175 : const int toChangeNext = 3 - maxedColors;
216 1838175 : 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 92360 : RGBColor::parseColor(std::string coldef) {
240 92360 : coldef = StringUtils::to_lower_case(coldef);
241 92360 : if (coldef == "red") {
242 12323 : return RED;
243 : }
244 80037 : if (coldef == "green") {
245 9469 : return GREEN;
246 : }
247 70568 : if (coldef == "blue") {
248 13467 : return BLUE;
249 : }
250 57101 : if (coldef == "yellow") {
251 13468 : return YELLOW;
252 : }
253 43633 : if (coldef == "cyan") {
254 1641 : return CYAN;
255 : }
256 41992 : if (coldef == "magenta") {
257 907 : return MAGENTA;
258 : }
259 41085 : if (coldef == "orange") {
260 781 : return ORANGE;
261 : }
262 40304 : if (coldef == "white") {
263 466 : return WHITE;
264 : }
265 39838 : if (coldef == "black") {
266 190 : return BLACK;
267 : }
268 39648 : if (coldef == "grey" || coldef == "gray") {
269 846 : return GREY;
270 : }
271 38802 : if (coldef == "invisible") {
272 34 : return INVISIBLE;
273 : }
274 38768 : 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 38768 : if (coldef[0] == '#') {
286 870 : const int coldesc = StringUtils::hexToInt(coldef);
287 870 : if (coldef.length() == 7) {
288 870 : r = static_cast<unsigned char>((coldesc & 0xFF0000) >> 16);
289 870 : g = static_cast<unsigned char>((coldesc & 0x00FF00) >> 8);
290 870 : 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 113694 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
301 37898 : if (st.size() == 3 || st.size() == 4) {
302 : try {
303 37885 : r = static_cast<unsigned char>(StringUtils::toInt(st[0]));
304 20915 : g = static_cast<unsigned char>(StringUtils::toInt(st[1]));
305 20842 : b = static_cast<unsigned char>(StringUtils::toInt(st[2]));
306 20817 : if (st.size() == 4) {
307 1050 : a = static_cast<unsigned char>(StringUtils::toInt(st[3]));
308 : }
309 20817 : if (r <= 1 && g <= 1 && b <= 1 && (st.size() == 3 || a <= 1)) {
310 7504 : throw NumberFormatException("(color component) " + coldef);
311 : }
312 20820 : } catch (NumberFormatException&) {
313 20820 : r = static_cast<unsigned char>(StringUtils::toDouble(st[0]) * 255. + 0.5);
314 20819 : g = static_cast<unsigned char>(StringUtils::toDouble(st[1]) * 255. + 0.5);
315 20819 : b = static_cast<unsigned char>(StringUtils::toDouble(st[2]) * 255. + 0.5);
316 20819 : if (st.size() == 4) {
317 130 : a = static_cast<unsigned char>(StringUtils::toDouble(st[3]) * 255. + 0.5);
318 : }
319 20820 : }
320 : } else {
321 39 : throw FormatException("Invalid color definition '" + coldef + "'");
322 : }
323 37898 : }
324 38754 : 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 125189 : RGBColor::interpolate(const RGBColor& minColor, const RGBColor& maxColor, double weight) {
388 125189 : if (weight < 0) {
389 : weight = 0;
390 : }
391 125188 : if (weight > 1) {
392 : weight = 1;
393 : }
394 125189 : const unsigned char r = (unsigned char)((int)minColor.myRed + (((int)maxColor.myRed - (int)minColor.myRed) * weight));
395 125189 : const unsigned char g = (unsigned char)((int)minColor.myGreen + (((int)maxColor.myGreen - (int)minColor.myGreen) * weight));
396 125189 : const unsigned char b = (unsigned char)((int)minColor.myBlue + (((int)maxColor.myBlue - (int)minColor.myBlue) * weight));
397 125189 : const unsigned char a = (unsigned char)((int)minColor.myAlpha + (((int)maxColor.myAlpha - (int)minColor.myAlpha) * weight));
398 125189 : 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 : /****************************************************************************/
|