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 39242590 : RGBColor::RGBColor(bool valid)
66 39242590 : : myRed(0), myGreen(0), myBlue(0), myAlpha(0), myValid(valid) {}
67 :
68 :
69 30108390 : RGBColor::RGBColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
70 30108390 : : myRed(red), myGreen(green), myBlue(blue), myAlpha(alpha), myValid(true) {}
71 :
72 :
73 : unsigned char
74 44649444 : RGBColor::red() const {
75 44649444 : return myRed;
76 : }
77 :
78 :
79 : unsigned char
80 44649444 : RGBColor::green() const {
81 44649444 : return myGreen;
82 : }
83 :
84 :
85 : unsigned char
86 44649444 : RGBColor::blue() const {
87 44649444 : return myBlue;
88 : }
89 :
90 :
91 : unsigned char
92 80240649 : RGBColor::alpha() const {
93 80240649 : 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 4794 : RGBColor::setValid(const bool value) {
115 4794 : myValid = value;
116 4794 : }
117 :
118 :
119 : bool
120 1124 : RGBColor::isValid() const {
121 1124 : return myValid;
122 : }
123 :
124 :
125 : std::ostream&
126 121556 : operator<<(std::ostream& os, const RGBColor& col) {
127 121556 : if (col == RGBColor::RED) {
128 10393 : return os << "red";
129 : }
130 111163 : if (col == RGBColor::GREEN) {
131 10080 : return os << "green";
132 : }
133 101083 : if (col == RGBColor::BLUE) {
134 632 : return os << "blue";
135 : }
136 100451 : if (col == RGBColor::YELLOW) {
137 61403 : return os << "yellow";
138 : }
139 39048 : if (col == RGBColor::CYAN) {
140 100 : return os << "cyan";
141 : }
142 38948 : if (col == RGBColor::MAGENTA) {
143 184 : return os << "magenta";
144 : }
145 38764 : if (col == RGBColor::ORANGE) {
146 99 : return os << "orange";
147 : }
148 38665 : if (col == RGBColor::WHITE) {
149 1242 : return os << "white";
150 : }
151 37423 : if (col == RGBColor::BLACK) {
152 1841 : return os << "black";
153 : }
154 35582 : if (col == RGBColor::GREY) {
155 32 : return os << "grey";
156 : }
157 35550 : if (col == RGBColor::INVISIBLE) {
158 64 : return os << "invisible";
159 : }
160 35486 : os << static_cast<int>(col.myRed) << ","
161 35486 : << static_cast<int>(col.myGreen) << ","
162 35486 : << static_cast<int>(col.myBlue);
163 35486 : if (col.myAlpha < 255) {
164 832 : os << "," << static_cast<int>(col.myAlpha);
165 : }
166 : return os;
167 : }
168 :
169 :
170 : bool
171 698238 : RGBColor::operator==(const RGBColor& c) const {
172 698238 : return (myRed == c.myRed) && (myGreen == c.myGreen) && (myBlue == c.myBlue) && (myAlpha == c.myAlpha) && (myValid == c.myValid);
173 : }
174 :
175 :
176 : bool
177 2505071 : RGBColor::operator!=(const RGBColor& c) const {
178 2505071 : 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 4406407 : RGBColor::changedBrightness(int change, int toChange) const {
201 4406407 : const unsigned char red = (unsigned char)(MIN2(MAX2(myRed + change, 0), 255));
202 4406407 : const unsigned char blue = (unsigned char)(MIN2(MAX2(myBlue + change, 0), 255));
203 4406407 : const unsigned char green = (unsigned char)(MIN2(MAX2(myGreen + change, 0), 255));
204 4406407 : int changed = ((int)red - (int)myRed) + ((int)blue - (int)myBlue) + ((int)green - (int)myGreen);
205 4406407 : const RGBColor result(red, green, blue, myAlpha);
206 4406407 : if (changed == toChange * change) {
207 2239290 : return result;
208 2167117 : } else if (changed == 0) {
209 0 : return result;
210 : } else {
211 4323971 : const int maxedColors = (red != myRed + change ? 1 : 0) + (blue != myBlue + change ? 1 : 0) + (green != myGreen + change ? 1 : 0);
212 2167117 : if (maxedColors == 3) {
213 60 : return result;
214 : } else {
215 2167057 : const int toChangeNext = 3 - maxedColors;
216 2167057 : 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 94748 : RGBColor::parseColor(std::string coldef) {
240 94748 : coldef = StringUtils::to_lower_case(coldef);
241 94748 : if (coldef == "red") {
242 10631 : return RED;
243 : }
244 84117 : if (coldef == "green") {
245 7614 : return GREEN;
246 : }
247 76503 : if (coldef == "blue") {
248 13128 : return BLUE;
249 : }
250 63375 : if (coldef == "yellow") {
251 12958 : return YELLOW;
252 : }
253 50417 : if (coldef == "cyan") {
254 1526 : return CYAN;
255 : }
256 48891 : if (coldef == "magenta") {
257 837 : return MAGENTA;
258 : }
259 48054 : if (coldef == "orange") {
260 714 : return ORANGE;
261 : }
262 47340 : if (coldef == "white") {
263 459 : return WHITE;
264 : }
265 46881 : if (coldef == "black") {
266 176 : return BLACK;
267 : }
268 46705 : if (coldef == "grey" || coldef == "gray") {
269 773 : return GREY;
270 : }
271 45932 : if (coldef == "invisible") {
272 32 : return INVISIBLE;
273 : }
274 45900 : 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 45900 : if (coldef[0] == '#') {
286 849 : const int coldesc = StringUtils::hexToInt(coldef);
287 849 : if (coldef.length() == 7) {
288 849 : r = static_cast<unsigned char>((coldesc & 0xFF0000) >> 16);
289 849 : g = static_cast<unsigned char>((coldesc & 0x00FF00) >> 8);
290 849 : 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 135153 : std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
301 45051 : if (st.size() == 3 || st.size() == 4) {
302 : try {
303 45038 : r = static_cast<unsigned char>(StringUtils::toInt(st[0]));
304 19319 : g = static_cast<unsigned char>(StringUtils::toInt(st[1]));
305 19246 : b = static_cast<unsigned char>(StringUtils::toInt(st[2]));
306 19221 : if (st.size() == 4) {
307 960 : a = static_cast<unsigned char>(StringUtils::toInt(st[3]));
308 : }
309 19221 : if (r <= 1 && g <= 1 && b <= 1 && (st.size() == 3 || a <= 1)) {
310 6932 : throw NumberFormatException("(color component) " + coldef);
311 : }
312 29283 : } catch (NumberFormatException&) {
313 29283 : r = static_cast<unsigned char>(StringUtils::toDouble(st[0]) * 255. + 0.5);
314 29282 : g = static_cast<unsigned char>(StringUtils::toDouble(st[1]) * 255. + 0.5);
315 29282 : b = static_cast<unsigned char>(StringUtils::toDouble(st[2]) * 255. + 0.5);
316 29282 : if (st.size() == 4) {
317 118 : a = static_cast<unsigned char>(StringUtils::toDouble(st[3]) * 255. + 0.5);
318 : }
319 29283 : }
320 : } else {
321 26 : throw FormatException("Invalid color definition '" + coldef + "'");
322 : }
323 45051 : }
324 45886 : return RGBColor(r, g, b, a);
325 : }
326 :
327 :
328 : RGBColor
329 2972 : 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 5944 : 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 116399 : RGBColor::interpolate(const RGBColor& minColor, const RGBColor& maxColor, double weight) {
356 116399 : if (weight < 0) {
357 : weight = 0;
358 : }
359 116398 : if (weight > 1) {
360 : weight = 1;
361 : }
362 116399 : const unsigned char r = (unsigned char)((int)minColor.myRed + (((int)maxColor.myRed - (int)minColor.myRed) * weight));
363 116399 : const unsigned char g = (unsigned char)((int)minColor.myGreen + (((int)maxColor.myGreen - (int)minColor.myGreen) * weight));
364 116399 : const unsigned char b = (unsigned char)((int)minColor.myBlue + (((int)maxColor.myBlue - (int)minColor.myBlue) * weight));
365 116399 : const unsigned char a = (unsigned char)((int)minColor.myAlpha + (((int)maxColor.myAlpha - (int)minColor.myAlpha) * weight));
366 116399 : 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 : /****************************************************************************/
|