LCOV - code coverage report
Current view: top level - src/utils/common - RGBColor.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 63.5 % 219 139
Test Date: 2026-07-26 16:30:20 Functions: 72.0 % 25 18

            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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1