/** \file Color3.cpp Color class. \author Morgan McGuire, http://graphics.cs.williams.edu \created 2001-06-02 \edited 2013-03-29 */ #include "G3D/platform.h" #include #include "G3D/Color3.h" #include "G3D/Vector3.h" #include "G3D/format.h" #include "G3D/BinaryInput.h" #include "G3D/BinaryOutput.h" #include "G3D/Color3unorm8.h" #include "G3D/Any.h" #include "G3D/stringutils.h" namespace G3D { Color3& Color3::operator=(const Any& a) { *this = Color3(a); return *this; } Color3::Color3(const Any& any) { *this = Color3::zero(); any.verifyNameBeginsWith("Color3", "Power3", "Radiance3", "Irradiance3", "Energy3", "Radiosity3", "Biradiance3"); switch (any.type()) { case Any::TABLE: for (Any::AnyTable::Iterator it = any.table().begin(); it.isValid(); ++it) { const std::string& key = toLower(it->key); if (key == "r") { r = it->value; } else if (key == "g") { g = it->value; } else if (key == "b") { b = it->value; } else { any.verify(false, "Illegal key: " + it->key); } } break; case Any::ARRAY: // Intentionally falls through case Any::EMPTY_CONTAINER: { const std::string& name = any.name(); std::string factoryName; size_t i = name.find("::"); if (i != std::string::npos && i > 1) { factoryName = name.substr(i + 2); } if (factoryName == "") { if (any.size() == 1) { r = g = b = any[0]; } else { any.verifySize(3); r = any[0]; g = any[1]; b = any[2]; } } else if (factoryName == "one") { any.verifySize(0); *this = one(); } else if (factoryName == "zero") { any.verifySize(0); *this = zero(); } else if (factoryName == "fromARGB") { *this = Color3::fromARGB((int)any[0].number()); } else if (factoryName == "fromASRGB") { *this = Color3::fromASRGB((int)any[0].number()); } else { any.verify(false, "Expected Color3 constructor"); } } break; default: any.verify(false, "Bad Color3 constructor"); } } Any Color3::toAny() const { Any a(Any::ARRAY, "Color3"); a.append(r, g, b); return a; } Color3 Color3::ansiMap(uint32 i) { static const Color3 map[] = {Color3::black(), Color3::red() * 0.75f, Color3::green() * 0.75f, Color3::yellow() * 0.75f, Color3::blue() * 0.75f, Color3::purple() * 0.75f, Color3::cyan() * 0.75f, Color3::white() * 0.75f, Color3::white() * 0.90f, Color3::red(), Color3::green(), Color3::yellow(), Color3::blue(), Color3::purple(), Color3::cyan(), Color3::white()}; return map[i & 15]; } Color3 Color3::pastelMap(uint32 i) { uint32 x = Crypto::crc32(&i, sizeof(uint32)); // Create fairly bright, saturated colors Vector3 v(((x >> 22) & 1023) / 1023.0f, (((x >> 11) & 2047) / 2047.0f) * 0.5f + 0.25f, ((x & 2047) / 2047.0f) * 0.75f + 0.25f); return Color3::fromHSV(v); } const Color3& Color3::red() { static Color3 c(1.0f, 0.0f, 0.0f); return c; } const Color3& Color3::green() { static Color3 c(0.0f, 1.0f, 0.0f); return c; } const Color3& Color3::blue() { static Color3 c(0.0f, 0.0f, 1.0f); return c; } const Color3& Color3::purple() { static Color3 c(0.7f, 0.0f, 1.0f); return c; } const Color3& Color3::cyan() { static Color3 c(0.0f, 0.7f, 1.0f); return c; } const Color3& Color3::yellow() { static Color3 c(1.0f, 1.0f, 0.0f); return c; } const Color3& Color3::brown() { static Color3 c(0.5f, 0.5f, 0.0f); return c; } const Color3& Color3::orange() { static Color3 c(1.0f, 0.5f, 0.0f); return c; } const Color3& Color3::black() { static Color3 c(0.0f, 0.0f, 0.0f); return c; } const Color3& Color3::zero() { static Color3 c(0.0f, 0.0f, 0.0f); return c; } const Color3& Color3::one() { static Color3 c(1.0f, 1.0f, 1.0f); return c; } const Color3& Color3::gray() { static Color3 c(0.7f, 0.7f, 0.7f); return c; } const Color3& Color3::white() { static Color3 c(1.0f, 1.0f, 1.0f); return c; } bool Color3::isFinite() const { return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b); } Color3::Color3(BinaryInput& bi) { deserialize(bi); } void Color3::deserialize(BinaryInput& bi) { r = bi.readFloat32(); g = bi.readFloat32(); b = bi.readFloat32(); } void Color3::serialize(BinaryOutput& bo) const { bo.writeFloat32(r); bo.writeFloat32(g); bo.writeFloat32(b); } const Color3& Color3::wheelRandom() { static const Color3 colorArray[8] = {Color3::blue(), Color3::red(), Color3::green(), Color3::orange(), Color3::yellow(), Color3::cyan(), Color3::purple(), Color3::brown()}; return colorArray[iRandom(0, 7)]; } size_t Color3::hashCode() const { unsigned int rhash = (*(int*)(void*)(&r)); unsigned int ghash = (*(int*)(void*)(&g)); unsigned int bhash = (*(int*)(void*)(&b)); return rhash + (ghash * 37) + (bhash * 101); } Color3::Color3(const Vector3& v) { r = v.x; g = v.y; b = v.z; } Color3::Color3(const class Color3unorm8& other) : r(other.r), g(other.g), b(other.b) { } Color3 Color3::fromARGB(uint32 x) { return Color3(Color3unorm8::fromARGB(x)); } Color3 Color3::fromASRGB(uint32 x) { return Color3(Color3unorm8::fromARGB(x)).pow(2.2f); } //---------------------------------------------------------------------------- Color3 Color3::random() { return Color3(uniformRandom(), uniformRandom(), uniformRandom()).direction(); } //---------------------------------------------------------------------------- Color3& Color3::operator/= (float fScalar) { if (fScalar != 0.0f) { float fInvScalar = 1.0f / fScalar; r *= fInvScalar; g *= fInvScalar; b *= fInvScalar; } else { r = (float)G3D::finf(); g = (float)G3D::finf(); b = (float)G3D::finf(); } return *this; } //---------------------------------------------------------------------------- float Color3::unitize (float fTolerance) { float fLength = length(); if ( fLength > fTolerance ) { float fInvLength = 1.0f / fLength; r *= fInvLength; g *= fInvLength; b *= fInvLength; } else { fLength = 0.0f; } return fLength; } //---------------------------------------------------------------------------- Color3 Color3::fromHSV(const Vector3& _hsv) { debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f) && (_hsv.y <= 1.0f && _hsv.y >= 0.0f) && ( _hsv.z <= 1.0f && _hsv.z >= 0.0f), "H,S,V must be between [0,1]"); const int i = iMin(5, G3D::iFloor(6.0 * _hsv.x)); const float f = 6.0f * _hsv.x - i; const float m = _hsv.z * (1.0f - (_hsv.y)); const float n = _hsv.z * (1.0f - (_hsv.y * f)); const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f))); switch(i) { case 0: return Color3(_hsv.z, k, m); case 1: return Color3(n, _hsv.z, m); case 2: return Color3(m, _hsv.z, k); case 3: return Color3(m, n, _hsv.z); case 4: return Color3(k, m, _hsv.z); case 5: return Color3(_hsv.z, m, n); default: debugAssertM(false, "fell through switch.."); } return Color3::black(); } Vector3 Color3::toHSV(const Color3& _rgb) { debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f) && (_rgb.g <= 1.0f && _rgb.g >= 0.0f) && (_rgb.b <= 1.0f && _rgb.b >= 0.0f), "R,G,B must be between [0,1]"); Vector3 hsv = Vector3::zero(); hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b); if (G3D::fuzzyEq(hsv.z, 0.0f)) { return hsv; } const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b); hsv.y = (hsv.z - x) / hsv.z; if (G3D::fuzzyEq(hsv.y, 0.0f)) { return hsv; } Vector3 rgbN; rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x); rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x); rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x); if (_rgb.r == hsv.z) { // note from the max we know that it exactly equals one of the three. hsv.x = (_rgb.g == x)? 5.0f + rgbN.z : 1.0f - rgbN.y; } else if (_rgb.g == hsv.z) { hsv.x = (_rgb.b == x)? 1.0f + rgbN.x : 3.0f - rgbN.z; } else { hsv.x = (_rgb.r == x)? 3.0f + rgbN.y : 5.0f - rgbN.x; } hsv.x /= 6.0f; return hsv; } Color3 Color3::jetColorMap(const float& val) { debugAssertM(val <= 1.0f && val >= 0.0f , "value should be in [0,1]"); //truncated triangles where sides have slope 4 Color3 jet; jet.r = G3D::min(4.0f * val - 1.5f,-4.0f * val + 4.5f) ; jet.g = G3D::min(4.0f * val - 0.5f,-4.0f * val + 3.5f) ; jet.b = G3D::min(4.0f * val + 0.5f,-4.0f * val + 2.5f) ; jet.r = G3D::clamp(jet.r, 0.0f, 1.0f); jet.g = G3D::clamp(jet.g, 0.0f, 1.0f); jet.b = G3D::clamp(jet.b, 0.0f, 1.0f); return jet; } std::string Color3::toString() const { return G3D::format("(%g, %g, %g)", r, g, b); } //---------------------------------------------------------------------------- Color3 Color3::rainbowColorMap(float hue) { return fromHSV(Vector3(hue, 1.0f, 1.0f)); } }; // namespace