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