Files
2026-05-06 21:18:20 -04:00

407 lines
9.5 KiB
C++

/**
\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 <stdlib.h>
#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