cgv
Loading...
Searching...
No Matches
transfer_function.cxx
1#include "transfer_function.h"
2
3#include <cgv/media/ticks.h>
4#include <cgv/utils/algorithm.h>
5
6namespace cgv {
7namespace media {
8
9transfer_function::transfer_function(std::initializer_list<color_point_type> colors) {
10 set_color_points(colors);
11}
12
13transfer_function::transfer_function(std::initializer_list<color_point_type> colors, std::initializer_list<opacity_point_type> opacities) {
14 set_color_points(colors);
15 set_opacity_points(opacities);
16}
17
19 color_points_.clear();
20 opacity_points_.clear();
21 update_domain();
22}
23
25 color_points_.clear();
26 update_domain();
27}
28
30 opacity_points_.clear();
31 update_domain();
32}
33
34void transfer_function::set_color_points(const std::vector<color_point_type>& colors) {
35 color_points_ = colors;
36 sort_points_and_update_domain(color_points_);
37 ensure_control_points_cover_domain(color_points_);
38 ensure_control_points_cover_domain(opacity_points_);
39}
40
42 std::vector<float> positions;
43 cgv::utils::subdivision_sequence(std::back_inserter(positions), 0.0f, 1.0f, n);
44 std::vector<cgv::rgb> colors = scheme.quantize(n);
45 set_color_points(cgv::utils::zip(positions.begin(), positions.end(), colors.begin()));
46}
47
48void transfer_function::set_opacity_points(const std::vector<opacity_point_type>& opacities) {
49 opacity_points_ = opacities;
50 std::for_each(opacity_points_.begin(), opacity_points_.end(), [](const opacity_point_type& point) { cgv::math::saturate(point.second); });
51 sort_points_and_update_domain(opacity_points_);
52 ensure_control_points_cover_domain(opacity_points_);
53 ensure_control_points_cover_domain(color_points_);
54}
55
58 color_points_.push_back({ x, color });
59 sort_points_and_update_domain(color_points_);
60 ensure_control_points_cover_domain(opacity_points_);
61 ensure_control_points_cover_domain(color_points_);
62}
63
64void transfer_function::add_opacity_point(float x, float opacity) {
66 opacity_points_.push_back({ x, cgv::math::saturate(opacity) });
67 sort_points_and_update_domain(opacity_points_);
68 ensure_control_points_cover_domain(color_points_);
69 ensure_control_points_cover_domain(opacity_points_);
70}
71
72bool transfer_function::set_color(size_t index, const color_type& color) {
73 if(index < color_points_.size()) {
74 color_points_[index].second = color;
75 modified();
76 return true;
77 }
78 return false;
79}
80
82 auto it = find_point(color_points_, x);
83 if(it != color_points_.end()) {
84 it->second = color;
85 modified();
86 return true;
87 }
88 return false;
89}
90
91bool transfer_function::set_opacity(size_t index, const opacity_type& opacity) {
92 if(index < opacity_points_.size()) {
93 opacity_points_[index].second = opacity;
94 modified();
95 return true;
96 }
97 return false;
98}
99
100bool transfer_function::set_opacity_at(float x, const opacity_type& opacity) {
101 auto it = find_point(opacity_points_, x);
102 if(it != opacity_points_.end()) {
103 it->second = opacity;
104 modified();
105 return true;
106 }
107 return false;
108}
109
111 if(remove_point(color_points_, x)) {
112 modified();
113 return true;
114 }
115 return false;
116}
117
119 if(remove_point(opacity_points_, x)) {
120 modified();
121 return true;
122 }
123 return false;
124}
125
127 const vec2 current_domain = get_domain();
128
129 // Make sure we have points at either end of the new domain.
130 float lower_bound = current_domain[0] < domain[0] ? domain[0] : current_domain[0];
131 float upper_bound = current_domain[1] > domain[1] ? domain[1] : current_domain[1];
132
133 if(!color_points_.empty() && color_points_.front().first != domain[0])
134 add_color_point(domain[0], get_mapped_color(lower_bound));
135 if(!opacity_points_.empty() && opacity_points_.front().first != domain[0])
136 add_opacity_point(domain[0], get_mapped_opacity(lower_bound));
137
138 if(!color_points_.empty() && color_points_.back().first != domain[1])
139 add_color_point(domain[1], get_mapped_color(upper_bound));
140 if(!opacity_points_.empty() && opacity_points_.back().first != domain[1])
141 add_opacity_point(domain[1], get_mapped_opacity(upper_bound));
142
143 // Remove all points that are outside the new domain.
144 const auto is_point_out_of_range = [domain](const auto& point) {
145 return point.first < domain[0] || point.first > domain[1];
146 };
147
148 const auto remove_points_out_of_range = [domain, &is_point_out_of_range](auto& points) {
149 bool done = false;
150 bool removed = false;
151 while(!done) {
152 done = true;
153
154 auto it = std::find_if(points.begin(), points.end(), is_point_out_of_range);
155 if(it != points.end()) {
156 points.erase(it);
157 done = false;
158 removed = true;
159 }
160 }
161 return removed;
162 };
163
164 // Use temporary variables to ensure both functions are called which they otherwise may not due to short-circuit evaluation.
165 bool removed_color_points = remove_points_out_of_range(color_points_);
166 bool removed_opacity_points = remove_points_out_of_range(opacity_points_);
167 if(removed_color_points || removed_opacity_points)
168 modified();
169
170 sort_points_and_update_domain(color_points_);
171}
172
174 const cgv::vec2 current_domain = get_domain();
175
176 if(current_domain == domain)
177 return;
178
179 const auto map_point = [this, current_domain, domain](auto& point) {
180 point.first = map_range_safe(point.first, current_domain[0], current_domain[1], domain[0], domain[1]);
181 };
182
183 if(!color_points_.empty())
184 std::for_each(color_points_.begin(), color_points_.end(), map_point);
185
186 if(!opacity_points_.empty())
187 std::for_each(opacity_points_.begin(), opacity_points_.end(), map_point);
188
190}
191
192float transfer_function::normalize_value(float value) const {
193 const vec2 domain = get_domain();
194 value = cgv::math::clamp(value, domain[0], domain[1]);
195 return map_range_safe(value, domain[0], domain[1], 0.0f, 1.0f);
196}
197
200 float opacity = get_mapped_opacity(value);
201
202 return { color.R(), color.G(), color.B(), opacity };
203}
204
206 if(is_unknown(value) || color_points_.empty())
207 return get_unknown_color();
208 return interpolate(color_points_, value, color_interpolation_);
209}
210
212 if(opacity_points_.empty())
213 return 1.0f;
214 return interpolate(opacity_points_, value, opacity_interpolation_);
215}
216
217std::vector<cgv::rgba> transfer_function::quantize(size_t count) const {
218 const std::vector<cgv::rgb> colors = quantize_color(count);
219 const std::vector<float> opacities = quantize_opacity(count);
220
221 std::vector<cgv::rgba> values;
222 values.reserve(count);
223 std::transform(colors.begin(), colors.end(), opacities.begin(), std::back_inserter(values), [](const cgv::rgba& color, float opacity) {
224 return cgv::rgba(color, opacity);
225 });
226
227 return values;
228}
229
230std::vector<cgv::rgb> transfer_function::quantize_color(size_t count) const {
231 if(color_points_.empty())
232 return std::vector<cgv::rgb>(count, { 0.0f });
233
234 std::vector<cgv::rgb> colors = quantize(color_points_, count, color_interpolation_);
235 if(is_reversed())
236 std::reverse(colors.begin(), colors.end());
237 return colors;
238}
239
240std::vector<float> transfer_function::quantize_opacity(size_t count) const {
241 if(opacity_points_.empty())
242 return std::vector<float>(count, 1.0f);
243 std::vector<float> opacities = quantize(opacity_points_, count, opacity_interpolation_);
244 if(is_reversed())
245 std::reverse(opacities.begin(), opacities.end());
246 return opacities;
247}
248
249std::vector<float> transfer_function::get_ticks(size_t request_count) const {
250 /* This would return one tick for every color or opacity point.
251 std::vector<float> ticks;
252 if(!color_points_.empty())
253 std::transform(color_points_.begin(), color_points_.end(), std::back_inserter(ticks), cgv::utils::get_first<>{});// [] (const color_point_type& point))
254 else
255 std::transform(opacity_points_.begin(), opacity_points_.end(), std::back_inserter(ticks), cgv::utils::get_first<>{});
256 return ticks;
257 */
258 const vec2 domain = get_domain();
259 return compute_ticks(domain[0], domain[1], request_count);
260}
261
263 if(color_interpolation_ != interpolation || opacity_interpolation_ != interpolation) {
264 color_interpolation_ = interpolation;
265 opacity_interpolation_ = interpolation;
266 modified();
267 }
268}
269
271 if(color_interpolation_ != interpolation) {
272 color_interpolation_ = interpolation;
273 modified();
274 }
275}
276
278 if(opacity_interpolation_ != interpolation) {
279 opacity_interpolation_ = interpolation;
280 modified();
281 }
282}
283
284bool transfer_function::update_domain() {
285 cgv::vec2 old_domain = get_domain();
286
287 cgv::vec2 domain = { 0.0f };
288 if(!color_points_.empty()) {
289 domain[0] = color_points_.front().first;
290 domain[1] = color_points_.back().first;
291 }
292 if(!opacity_points_.empty()) {
293 domain[0] = std::min(domain[0], opacity_points_.front().first);
294 domain[1] = std::max(domain[1], opacity_points_.back().first);
295 }
296
297 if(old_domain != domain) {
299 return true;
300 }
301 return false;
302}
303
304} // namespace media
305} // namespace cgv
bool is_unknown(float value) const
Test whether the value is outside the domain according to the mapping options.
float map_range_safe(float value, float in_left, float in_right, float out_left, float out_right) const
Remap a scalar value from an input range to and output range while safely handling empty ranges.
bool is_reversed() const
Get whether the output color ramp is reversed.
Definition color_scale.h:76
cgv::rgba get_unknown_color() const
Get the color returned for scalars outside the domain if clamping is disabled.
Definition color_scale.h:93
cgv::vec2 get_domain() const
Get the input domain of scalars that will be mapped.
Definition color_scale.h:52
void modified()
Update the object's modified time.
virtual void set_domain(cgv::vec2 domain)
Set the input domain of scalars that will be mapped.
This class represents a continuous color scheme using an interpolator to convert continuous scalar va...
std::vector< cgv::rgb > quantize(size_t n) const
Evaluate the color scheme at n uniformly-spaced positions within the range [0,1].
void clear()
Clear all color and opacity control points.
float normalize_value(float value) const override
See color_scale::normalize_value().
float get_mapped_opacity(float value) const override
See color_scale::get_mapped_opacity().
bool remove_opacity_point(float x)
Remove the opacity point at position x if it exists.
InterpolationMode
The interpolation modes supported by the transfer function.
bool set_opacity(size_t index, const opacity_type &opacity)
Set the opacity of the control point at the given index.
void add_color_point(float x, const color_type &color)
Add a color point at position x.
void set_opacity_points(const std::vector< opacity_point_type > &opacities)
Set the opacity function to use the given control points.
void clear_opacity_points()
Clear opacity control points only.
bool remove_color_point(float x)
Remove the color point at position x if it exists.
std::pair< float, opacity_type > opacity_point_type
The used opacity control point type.
void set_color_points(const std::vector< color_point_type > &colors)
Set the color function to use the given control points.
transfer_function()
Construct using default arguments.
bool set_color_at(float x, const color_type &color)
Set the color of the control point at the given position.
std::vector< float > get_ticks(size_t request_count) const override
See color_scale::get_ticks().
bool set_opacity_at(float x, const opacity_type &opacity)
Set the opacity of the control point at the given position.
bool set_color(size_t index, const color_type &color)
Set the color of the control point at the given index.
void add_opacity_point(float x, float opacity)
Add an opacity point at position x.
std::vector< cgv::rgb > quantize_color(size_t count) const
Quantize the color function only.
void set_interpolation(InterpolationMode interpolation)
Set the interpolation mode of the color and opacity function.
void set_color_interpolation(InterpolationMode interpolation)
Set the interpolation mode of the color function.
cgv::rgba map_value(float value) const override
See color_scale::map_value().
void set_color_points_from_scheme(const cgv::media::continuous_color_scheme &scheme, size_t n)
Set the color function to use n uniformly sampled points from the given color scheme.
std::vector< cgv::rgba > quantize(size_t count) const override
See color_scale::quantize().
void rescale(cgv::vec2 domain)
Rescale the color and opactiy functions to the new domain.
std::vector< float > quantize_opacity(size_t count) const
Quantize the opacity function only.
void set_domain(cgv::vec2 domain) override
Set the domain.
void set_opacity_interpolation(InterpolationMode interpolation)
Set the interpolation mode of the opacity function.
cgv::rgb get_mapped_color(float value) const override
See color_scale::get_mapped_color().
void clear_color_points()
Clear color control points only.
float opacity_type
The used opacity type.
OutputIt zip(const InputIt1 first1, const InputIt1 last1, const InputIt2 first2, OutputIt d_first)
Zip two sequences together to form a single sequene of pairs and store the results in an output range...
Definition algorithm.h:199
void subdivision_sequence(OutputIt output_first, ParamT start, ParamT stop, size_t n)
Generate a sequence of n uniformly-spaced values in [start,stop] and store the result in an output ra...
Definition algorithm.h:232
this header is dependency free
Definition print.h:11
T B() const
convert color to RGB and return B component
Definition color.h:418
T R() const
convert color to RGB and return R component
Definition color.h:414
T G() const
convert color to RGB and return G component
Definition color.h:416