forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rgba.h
144 lines (113 loc) · 5.09 KB
/
rgba.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#pragma once
#include <algorithm>
#include <optional>
#include "drake/common/drake_copyable.h"
#include "drake/common/drake_throw.h"
#include "drake/common/eigen_types.h"
#include "drake/common/name_value.h"
namespace drake {
namespace geometry {
/** Defines RGBA (red, green, blue, alpha) values on the range [0, 1]. */
class Rgba {
public:
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(Rgba);
/** Default constructor produces fully opaque white. */
Rgba() = default;
/** Constructs with given (r, g, b, a) values.
@pre All values are within the range of [0, 1]. */
Rgba(double r, double g, double b, double a = 1.0) { set(r, g, b, a); }
/** Red. */
double r() const { return value_[0]; }
/** Green. */
double g() const { return value_[1]; }
/** Blue. */
double b() const { return value_[2]; }
/** Alpha. */
double a() const { return value_[3]; }
/** Returns all four elements in order. */
Eigen::Vector4d rgba() const { return value_; }
/** Sets (r, g, b, a) values.
@throws std::exception if any values are outside of the range [0, 1]. */
void set(double r, double g, double b, double a = 1.0) {
set(Eigen::Vector4d{r, g, b, a});
}
/** Sets an (r, g, b, a) from a vector.
@throws std::exception if the vector is not size 3 or 4.
@throws std::exception if any values are outside of the range [0, 1]. */
void set(const Eigen::Ref<const Eigen::VectorXd>& rgba);
/** Updates individual (r, g, b, a) values; any values not provided will
remain unchanged.
@throws std::exception if any values are outside of the range [0, 1]. */
void update(std::optional<double> r = {}, std::optional<double> g = {},
std::optional<double> b = {}, std::optional<double> a = {}) {
set(r.value_or(this->r()), g.value_or(this->g()), b.value_or(this->b()),
a.value_or(this->a()));
}
/** Reports if two %Rgba values are equal within a given absolute `tolerance`.
They are "equal" so long as the difference in no single channel is larger
than the specified `tolerance`. */
bool AlmostEqual(const Rgba& other, double tolerance = 0.0) const {
return std::abs(r() - other.r()) <= tolerance &&
std::abs(g() - other.g()) <= tolerance &&
std::abs(b() - other.b()) <= tolerance &&
std::abs(a() - other.a()) <= tolerance;
}
bool operator==(const Rgba& other) const { return value_ == other.value_; }
bool operator!=(const Rgba& other) const { return !(*this == other); }
/** Computes the element-wise product of two rgba colors. This type of
calculation is frequently used to modulate one color with another (e.g., for
lighting or texturing). */
Rgba operator*(const Rgba& other) const {
return {r() * other.r(), g() * other.g(), b() * other.b(), a() * other.a()};
}
/** Computes a new %Rgba color by multiplying the color channels (rgb) by the
given scalar `scalar`. All resultant channel values saturate at one. The
result has `this` %Rgba's alpha values.
@pre scale >= 0. */
Rgba scale_rgb(double scale) const {
DRAKE_THROW_UNLESS(scale >= 0);
return {std::min(1.0, r() * scale), std::min(1.0, g() * scale),
std::min(1.0, b() * scale), a()};
}
friend std::ostream& operator<<(std::ostream& out, const Rgba& rgba);
/** Passes this object to an Archive.
In YAML, an %Rgba is represented by an array-like list of three or four
numbers. E.g.,
rgba: [0.5, 0.5, 1.0]
or
rgba: [0.5, 0.5, 1.0, 0.5]
such that the first three values are red, green, and blue, respectively. If
no fourth value is provided, alpha is defined a 1.0.
Refer to @ref yaml_serialization "YAML Serialization" for background. */
template <typename Archive>
void Serialize(Archive* a) {
// N.B. This spelling is to enable the following in YAML:
// - rgba: [r, g, b]
// or
// - rgba: [r, g, b, a]
//
// N.B. For the Python binding use of DefAttributesUsingSerialize, this
// must refer to a member field; we cannot use a stack temporary here.
a->Visit(MakeNameValue("rgba", &value_));
// In case the archive modified our data, we need to re-validate it now.
// We want to be sure that value_ remains well-formed even if set() throws,
// so we'll clear it and then reset it.
auto new_value = value_;
value_ = Eigen::Vector4d::Zero();
set(new_value);
}
private:
void ThrowIfOutOfRange() const;
// We'll store the `r, g, b, a` doubles in an Eigen::Vector. Normally we'd
// use an `Eigen::Vector4d` for this purpose, but for compatibility with YAML
// parsing of either a 3-tuple (rgb) or 4-tuple (rgba), we need allow for a
// dynamic number of vector elements, and then carefully write our code to
// ensure that `value_.size()` is always exactly `4` as a runtime invariant.
// We'll also set the template argument `MaxRowsAtCompileTime == 4` so that
// our data is stored inline as part of this object, instead of on the heap.
Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 4, 1> value_ =
// As documented above, default-constructed Rgba is opaque white.
Eigen::Vector4d::Ones();
};
} // namespace geometry
} // namespace drake