LCOV - code coverage report
Current view: top level - src/utils/common - RGBColor.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 62.4 % 213 133
Test Date: 2025-12-06 15:35:27 Functions: 70.8 % 24 17

            Line data    Source code
       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     60578130 : RGBColor::RGBColor(bool valid)
      66     60578130 :     : myRed(0), myGreen(0), myBlue(0), myAlpha(0), myValid(valid) {}
      67              : 
      68              : 
      69     31560956 : RGBColor::RGBColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
      70     31560956 :     : myRed(red), myGreen(green), myBlue(blue), myAlpha(alpha), myValid(true) {}
      71              : 
      72              : 
      73              : unsigned char
      74     67223016 : RGBColor::red() const {
      75     67223016 :     return myRed;
      76              : }
      77              : 
      78              : 
      79              : unsigned char
      80     67223016 : RGBColor::green() const {
      81     67223016 :     return myGreen;
      82              : }
      83              : 
      84              : 
      85              : unsigned char
      86     67223016 : RGBColor::blue() const {
      87     67223016 :     return myBlue;
      88              : }
      89              : 
      90              : 
      91              : unsigned char
      92    123584087 : RGBColor::alpha() const {
      93    123584087 :     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         4822 : RGBColor::setValid(const bool value) {
     115         4822 :     myValid = value;
     116         4822 : }
     117              : 
     118              : 
     119              : bool
     120         1125 : RGBColor::isValid() const {
     121         1125 :     return myValid;
     122              : }
     123              : 
     124              : 
     125              : std::ostream&
     126        90049 : operator<<(std::ostream& os, const RGBColor& col) {
     127        90049 :     if (col == RGBColor::RED) {
     128        11251 :         return os << "red";
     129              :     }
     130        78798 :     if (col == RGBColor::GREEN) {
     131        10632 :         return os << "green";
     132              :     }
     133        68166 :     if (col == RGBColor::BLUE) {
     134          763 :         return os << "blue";
     135              :     }
     136        67403 :     if (col == RGBColor::YELLOW) {
     137        52115 :         return os << "yellow";
     138              :     }
     139        15288 :     if (col == RGBColor::CYAN) {
     140          108 :         return os << "cyan";
     141              :     }
     142        15180 :     if (col == RGBColor::MAGENTA) {
     143          187 :         return os << "magenta";
     144              :     }
     145        14993 :     if (col == RGBColor::ORANGE) {
     146          108 :         return os << "orange";
     147              :     }
     148        14885 :     if (col == RGBColor::WHITE) {
     149         1245 :         return os << "white";
     150              :     }
     151        13640 :     if (col == RGBColor::BLACK) {
     152         2081 :         return os << "black";
     153              :     }
     154        11559 :     if (col == RGBColor::GREY) {
     155           35 :         return os << "grey";
     156              :     }
     157        11524 :     if (col == RGBColor::INVISIBLE) {
     158           70 :         return os << "invisible";
     159              :     }
     160        11454 :     os << static_cast<int>(col.myRed) << ","
     161        11454 :        << static_cast<int>(col.myGreen) << ","
     162        11454 :        << static_cast<int>(col.myBlue);
     163        11454 :     if (col.myAlpha < 255) {
     164          910 :         os << "," << static_cast<int>(col.myAlpha);
     165              :     }
     166              :     return os;
     167              : }
     168              : 
     169              : 
     170              : bool
     171       401490 : RGBColor::operator==(const RGBColor& c) const {
     172       401490 :     return (myRed == c.myRed) && (myGreen == c.myGreen) && (myBlue == c.myBlue) && (myAlpha == c.myAlpha) && (myValid == c.myValid);
     173              : }
     174              : 
     175              : 
     176              : bool
     177      4594964 : RGBColor::operator!=(const RGBColor& c) const {
     178      4594964 :     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      3740505 : RGBColor::changedBrightness(int change, int toChange) const {
     201      3740505 :     const unsigned char red = (unsigned char)(MIN2(MAX2(myRed + change, 0), 255));
     202      3740505 :     const unsigned char blue = (unsigned char)(MIN2(MAX2(myBlue + change, 0), 255));
     203      3740505 :     const unsigned char green = (unsigned char)(MIN2(MAX2(myGreen + change, 0), 255));
     204      3740505 :     int changed = ((int)red - (int)myRed) + ((int)blue - (int)myBlue) + ((int)green - (int)myGreen);
     205      3740505 :     const RGBColor result(red, green, blue, myAlpha);
     206      3740505 :     if (changed == toChange * change) {
     207      1932073 :         return result;
     208      1808432 :     } else if (changed == 0) {
     209            0 :         return result;
     210              :     } else {
     211      3636143 :         const int maxedColors = (red != myRed + change ? 1 : 0) + (blue != myBlue + change ? 1 : 0) + (green != myGreen + change ? 1 : 0);
     212      1808432 :         if (maxedColors == 3) {
     213           29 :             return result;
     214              :         } else {
     215      1808403 :             const int toChangeNext = 3 - maxedColors;
     216      1808403 :             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        86927 : RGBColor::parseColor(std::string coldef) {
     240        86927 :     coldef = StringUtils::to_lower_case(coldef);
     241        86927 :     if (coldef == "red") {
     242        11124 :         return RED;
     243              :     }
     244        75803 :     if (coldef == "green") {
     245         8041 :         return GREEN;
     246              :     }
     247        67762 :     if (coldef == "blue") {
     248        13467 :         return BLUE;
     249              :     }
     250        54295 :     if (coldef == "yellow") {
     251        13453 :         return YELLOW;
     252              :     }
     253        40842 :     if (coldef == "cyan") {
     254         1634 :         return CYAN;
     255              :     }
     256        39208 :     if (coldef == "magenta") {
     257          910 :         return MAGENTA;
     258              :     }
     259        38298 :     if (coldef == "orange") {
     260          786 :         return ORANGE;
     261              :     }
     262        37512 :     if (coldef == "white") {
     263          466 :         return WHITE;
     264              :     }
     265        37046 :     if (coldef == "black") {
     266          190 :         return BLACK;
     267              :     }
     268        36856 :     if (coldef == "grey" || coldef == "gray") {
     269          846 :         return GREY;
     270              :     }
     271        36010 :     if (coldef == "invisible") {
     272           34 :         return INVISIBLE;
     273              :     }
     274        35976 :     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        35976 :     if (coldef[0] == '#') {
     286          853 :         const int coldesc = StringUtils::hexToInt(coldef);
     287          853 :         if (coldef.length() == 7) {
     288          853 :             r = static_cast<unsigned char>((coldesc & 0xFF0000) >> 16);
     289          853 :             g = static_cast<unsigned char>((coldesc & 0x00FF00) >> 8);
     290          853 :             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       105369 :         std::vector<std::string> st = StringTokenizer(coldef, ",").getVector();
     301        35123 :         if (st.size() == 3 || st.size() == 4) {
     302              :             try {
     303        35110 :                 r = static_cast<unsigned char>(StringUtils::toInt(st[0]));
     304        21124 :                 g = static_cast<unsigned char>(StringUtils::toInt(st[1]));
     305        21051 :                 b = static_cast<unsigned char>(StringUtils::toInt(st[2]));
     306        21026 :                 if (st.size() == 4) {
     307         1050 :                     a = static_cast<unsigned char>(StringUtils::toInt(st[3]));
     308              :                 }
     309        21026 :                 if (r <= 1 && g <= 1 && b <= 1 && (st.size() == 3 || a <= 1)) {
     310         7366 :                     throw NumberFormatException("(color component) " + coldef);
     311              :                 }
     312        17767 :             } catch (NumberFormatException&) {
     313        17767 :                 r = static_cast<unsigned char>(StringUtils::toDouble(st[0]) * 255. + 0.5);
     314        17766 :                 g = static_cast<unsigned char>(StringUtils::toDouble(st[1]) * 255. + 0.5);
     315        17766 :                 b = static_cast<unsigned char>(StringUtils::toDouble(st[2]) * 255. + 0.5);
     316        17766 :                 if (st.size() == 4) {
     317          130 :                     a = static_cast<unsigned char>(StringUtils::toDouble(st[3]) * 255. + 0.5);
     318              :                 }
     319        17767 :             }
     320              :         } else {
     321           39 :             throw FormatException("Invalid color definition '" + coldef + "'");
     322              :         }
     323        35123 :     }
     324        35962 :     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       124693 : RGBColor::interpolate(const RGBColor& minColor, const RGBColor& maxColor, double weight) {
     388       124693 :     if (weight < 0) {
     389              :         weight = 0;
     390              :     }
     391       124692 :     if (weight > 1) {
     392              :         weight = 1;
     393              :     }
     394       124693 :     const unsigned char r = (unsigned char)((int)minColor.myRed   + (((int)maxColor.myRed   - (int)minColor.myRed)   * weight));
     395       124693 :     const unsigned char g = (unsigned char)((int)minColor.myGreen + (((int)maxColor.myGreen - (int)minColor.myGreen) * weight));
     396       124693 :     const unsigned char b = (unsigned char)((int)minColor.myBlue  + (((int)maxColor.myBlue  - (int)minColor.myBlue)  * weight));
     397       124693 :     const unsigned char a = (unsigned char)((int)minColor.myAlpha + (((int)maxColor.myAlpha - (int)minColor.myAlpha) * weight));
     398       124693 :     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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1