Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 35677025 : RGBColor::RGBColor(bool valid)
66 35677025 : : myRed(0), myGreen(0), myBlue(0), myAlpha(0), myValid(valid) {}
67 :
68 :
69 26346563 : RGBColor::RGBColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
70 26346563 : : myRed(red), myGreen(green), myBlue(blue), myAlpha(alpha), myValid(true) {}
71 :
72 :
73 : unsigned char
74 39660643 : RGBColor::red() const {
75 39660643 : return myRed;
76 : }
77 :
78 :
79 : unsigned char
80 39660643 : RGBColor::green() const {
81 39660643 : return myGreen;
82 : }
83 :
84 :
85 : unsigned char
86 39660643 : RGBColor::blue() const {
87 39660643 : return myBlue;
88 : }
89 :
90 :
91 : unsigned char
92 71734706 : RGBColor::alpha() const {
93 71734706 : return myAlpha;
94 : }
95 :
96 :
97 : void
98 229 : RGBColor::set(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
99 229 : myRed = r;
100 229 : myGreen = g;
101 229 : myBlue = b;
102 229 : myAlpha = a;
103 229 : myValid = true;
104 229 : }
105 :
106 :
107 : void
108 1000 : RGBColor::setAlpha(unsigned char alpha) {
109 1000 : myAlpha = alpha;
110 1000 : }
111 :
112 :
113 : void
114 6655 : RGBColor::setValid(const bool value) {
115 6655 : myValid = value;
116 6655 : }
117 :
118 :
119 : bool
120 1370 : RGBColor::isValid() const {
121 1370 : return myValid;
122 : }
123 :
124 :
125 : std::ostream&
126 84684 : operator<<(std::ostream& os, const RGBColor& col) {
127 84684 : if (col == RGBColor::RED) {
128 6549 : return os << "red";
129 : }
130 78135 : if (col == RGBColor::GREEN) {
131 5497 : return os << "green";
132 : }
133 72638 : if (col == RGBColor::BLUE) {
134 917 : return os << "blue";
135 : }
136 71721 : if (col == RGBColor::YELLOW) {
137 51695 : return os << "yellow";
138 : }
139 20026 : if (col == RGBColor::CYAN) {
140 97 : return os << "cyan";
141 : }
142 19929 : if (col == RGBColor::MAGENTA) {
143 183 : return os << "magenta";
144 : }
145 19746 : if (col == RGBColor::ORANGE) {
146 128 : return os << "orange";
147 : }
148 19618 : if (col == RGBColor::WHITE) {
149 1638 : return os << "white";
150 : }
151 17980 : if (col == RGBColor::BLACK) {
152 1841 : return os << "black";
153 : }
154 16139 : if (col == RGBColor::GREY) {
155 31 : return os << "grey";
156 : }
157 16108 : if (col == RGBColor::INVISIBLE) {
158 62 : return os << "invisible";
159 : }
160 16046 : os << static_cast<int>(col.myRed) << ","
161 16046 : << static_cast<int>(col.myGreen) << ","
162 16046 : << static_cast<int>(col.myBlue);
163 16046 : if (col.myAlpha < 255) {
164 805 : os << "," << static_cast<int>(col.myAlpha);
165 : }
166 : return os;
167 : }
168 :
169 :
170 : bool
171 436734 : RGBColor::operator==(const RGBColor& c) const {
172 436734 : return (myRed == c.myRed) && (myGreen == c.myGreen) && (myBlue == c.myBlue) && (myAlpha == c.myAlpha) && (myValid == c.myValid);
173 : }
174 :
175 :
176 : bool
177 2160823 : RGBColor::operator!=(const RGBColor& c) const {
178 2160823 : 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 2807661 : RGBColor::changedBrightness(int change, int toChange) const {
201 2807661 : const unsigned char red = (unsigned char)(MIN2(MAX2(myRed + change, 0), 255));
202 2807661 : const unsigned char blue = (unsigned char)(MIN2(MAX2(myBlue + change, 0), 255));
203 2807661 : const unsigned char green = (unsigned char)(MIN2(MAX2(myGreen + change, 0), 255));
204 2807661 : int changed = ((int)red - (int)myRed) + ((int)blue - (int)myBlue) + ((int)green - (int)myGreen);
205 2807661 : const RGBColor result(red, green, blue, myAlpha);
206 2807661 : if (changed == toChange * change) {
207 1433708 : return result;
208 1373953 : } else if (changed == 0) {
209 0 : return result;
210 : } else {
211 2765721 : const int maxedColors = (red != myRed + change ? 1 : 0) + (blue != myBlue + change ? 1 : 0) + (green != myGreen + change ? 1 : 0);
212 1373953 : if (maxedColors == 3) {
213 0 : return result;
214 : } else {
215 1373953 : const int toChangeNext = 3 - maxedColors;
216 1373953 : 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 79721 : RGBColor::parseColor(std::string coldef) {
240 159442 : coldef = StringUtils::to_lower_case(coldef);
241 79721 : if (coldef == "red") {
242 8156 : return RED;
243 : }
244 71565 : if (coldef == "green") {
245 5334 : return GREEN;
246 : }
247 66231 : if (coldef == "blue") {
248 12638 : return BLUE;
249 : }
250 53593 : if (coldef == "yellow") {
251 12258 : return YELLOW;
252 : }
253 41335 : if (coldef == "cyan") {
254 1488 : return CYAN;
255 : }
256 39847 : if (coldef == "magenta") {
257 799 : return MAGENTA;
258 : }
259 39048 : if (coldef == "orange") {
260 700 : return ORANGE;
261 : }
262 38348 : if (coldef == "white") {
263 866 : return WHITE;
264 : }
265 37482 : if (coldef == "black") {
266 180 : return BLACK;
267 : }
268 73870 : if (coldef == "grey" || coldef == "gray") {
269 734 : return GREY;
270 : }
271 36568 : if (coldef == "invisible") {
272 26 : return INVISIBLE;
273 : }
274 36542 : 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 36542 : if (coldef[0] == '#') {
286 1092 : const int coldesc = StringUtils::hexToInt(coldef);
287 1092 : if (coldef.length() == 7) {
288 1092 : r = static_cast<unsigned char>((coldesc & 0xFF0000) >> 16);
289 1092 : g = static_cast<unsigned char>((coldesc & 0x00FF00) >> 8);
290 1092 : 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 106350 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
301 35450 : if (st.size() == 3 || st.size() == 4) {
302 : try {
303 35433 : r = static_cast<unsigned char>(StringUtils::toInt(st[0]));
304 25913 : g = static_cast<unsigned char>(StringUtils::toInt(st[1]));
305 25813 : b = static_cast<unsigned char>(StringUtils::toInt(st[2]));
306 25785 : if (st.size() == 4) {
307 933 : a = static_cast<unsigned char>(StringUtils::toInt(st[3]));
308 : }
309 25785 : if (r <= 1 && g <= 1 && b <= 1 && (st.size() == 3 || a <= 1)) {
310 7310 : throw NumberFormatException("(color component) " + coldef);
311 : }
312 13303 : } catch (NumberFormatException&) {
313 13303 : r = static_cast<unsigned char>(StringUtils::toDouble(st[0]) * 255. + 0.5);
314 13301 : g = static_cast<unsigned char>(StringUtils::toDouble(st[1]) * 255. + 0.5);
315 13301 : b = static_cast<unsigned char>(StringUtils::toDouble(st[2]) * 255. + 0.5);
316 13301 : if (st.size() == 4) {
317 118 : a = static_cast<unsigned char>(StringUtils::toDouble(st[3]) * 255. + 0.5);
318 : }
319 13303 : }
320 : } else {
321 34 : throw FormatException("Invalid color definition '" + coldef + "'");
322 : }
323 35450 : }
324 36523 : return RGBColor(r, g, b, a);
325 : }
326 :
327 :
328 : RGBColor
329 2875 : RGBColor::parseColorReporting(
330 : const std::string& coldef, const std::string& objecttype,
331 : const char* objectid, bool report, bool& ok) {
332 : UNUSED_PARAMETER(report);
333 : try {
334 5750 : return parseColor(coldef);
335 0 : } catch (NumberFormatException&) {
336 0 : } catch (EmptyData&) {
337 0 : }
338 0 : ok = false;
339 0 : std::ostringstream oss;
340 0 : oss << "Attribute 'color' in definition of ";
341 0 : if (objectid == nullptr) {
342 0 : oss << "a ";
343 : }
344 : oss << objecttype;
345 0 : if (objectid != nullptr) {
346 0 : oss << " '" << objectid << "'";
347 : }
348 0 : oss << " is not a valid color.";
349 0 : WRITE_ERROR(oss.str());
350 0 : return RGBColor();
351 0 : }
352 :
353 :
354 : RGBColor
355 112230 : RGBColor::interpolate(const RGBColor& minColor, const RGBColor& maxColor, double weight) {
356 112230 : if (weight < 0) {
357 : weight = 0;
358 : }
359 112230 : if (weight > 1) {
360 : weight = 1;
361 : }
362 112230 : const unsigned char r = (unsigned char)((int)minColor.myRed + (((int)maxColor.myRed - (int)minColor.myRed) * weight));
363 112230 : const unsigned char g = (unsigned char)((int)minColor.myGreen + (((int)maxColor.myGreen - (int)minColor.myGreen) * weight));
364 112230 : const unsigned char b = (unsigned char)((int)minColor.myBlue + (((int)maxColor.myBlue - (int)minColor.myBlue) * weight));
365 112230 : const unsigned char a = (unsigned char)((int)minColor.myAlpha + (((int)maxColor.myAlpha - (int)minColor.myAlpha) * weight));
366 112230 : return RGBColor(r, g, b, a);
367 : }
368 :
369 :
370 : RGBColor
371 0 : RGBColor::fromHSV(double h, double s, double v) {
372 : h = MIN2(MAX2(h, 0.), 360.);
373 : s = MIN2(MAX2(s, 0.), 1.);
374 : v = MIN2(MAX2(v, 0.), 1.);
375 0 : h /= 60.;
376 0 : const int i = int(floor(h));
377 0 : double f = h - i;
378 0 : if (i % 2 == 0) {
379 0 : f = 1. - f;
380 : }
381 0 : const unsigned char m = static_cast<unsigned char>(v * (1 - s) * 255. + 0.5);
382 0 : const unsigned char n = static_cast<unsigned char>(v * (1 - s * f) * 255. + 0.5);
383 0 : const unsigned char vv = static_cast<unsigned char>(v * 255. + 0.5);
384 0 : switch (i) {
385 0 : case 0:
386 : case 6:
387 0 : return RGBColor(vv, n, m, 255);
388 0 : case 1:
389 0 : return RGBColor(n, vv, m, 255);
390 0 : case 2:
391 0 : return RGBColor(m, vv, n, 255);
392 0 : case 3:
393 0 : return RGBColor(m, n, vv, 255);
394 0 : case 4:
395 0 : return RGBColor(n, m, vv, 255);
396 0 : case 5:
397 0 : return RGBColor(vv, m, n, 255);
398 : }
399 0 : return RGBColor(255, 255, 255, 255);
400 : }
401 :
402 : RGBColor
403 0 : RGBColor::randomHue(double s, double v) {
404 0 : return fromHSV(RandHelper::rand(360, &myRNG), s, v);
405 : }
406 :
407 :
408 : /****************************************************************************/
|