cgv
Loading...
Searching...
No Matches
color_scale_legend.cxx
1#include "color_scale_legend.h"
2
3#include <cgv/gui/theme_info.h>
4#include <cgv/math/ftransform.h>
5#include <cgv/utils/algorithm.h>
6#include <cgv/utils/scan.h>
7#include <cgv_g2d/msdf_gl_font_renderer.h>
8
9namespace cgv {
10namespace overlay {
11
12color_scale_legend::color_scale_legend() {
13
14 set_name("Color Map Legend");
15
16 // TODO: Remove padding from layout and use get_content_rect() as a starting point instead.
17 layout.padding = padding();
18 layout.total_size = ivec2(300, 60);
19
20 set_size(layout.total_size);
21
22 tick_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::rectangle);
23
24 label_format.precision = 0;
25 label_format.trailing_zeros = false;
26 label_format.decimal_integers = false;
27 label_format.fixed = true;
28 label_format.grouping = true;
29}
30
32
33 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, -1);
34
36
37 tex.destruct(ctx);
38
39 tick_renderer.destruct(ctx);
40 tick_geometry.destruct(ctx);
41 label_geometry.destruct(ctx);
42}
43
45
46 if(ptr.points_to_member_of(layout.total_size)) {
47 // TODO: minimum width and height depend on other layout parameters
48 layout.total_size.y() = std::max(layout.total_size.y(), 2 * layout.padding + 4 + layout.label_space);
49 set_size(layout.total_size);
50 }
51
52 if(ptr.points_to_one_of(background_visible_, invert_color))
53 init_styles();
54
55 if(ptr.points_to(num_ticks))
56 num_ticks = cgv::math::clamp(num_ticks, 2u, 100u);
57
58 if(ptr.points_to(title)) {
59 layout.title_space = title == "" ? 0 : 12;
60 post_recreate_layout();
61 }
62
63 if(ptr.points_to_one_of(layout.orientation, layout.label_alignment, num_ticks, nice_ticks, auto_precision) || ptr.points_to_member_of(label_format))
64 post_recreate_layout();
65}
66
68
69 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, 1);
70
71 register_shader("rectangle", cgv::g2d::shaders::rectangle);
72 register_shader("grid", cgv::g2d::shaders::grid);
73
74 bool success = canvas_overlay::init(ctx);
75
76 success &= tick_renderer.init(ctx);
77 success &= tick_geometry.init(ctx);
78 success &= label_geometry.init(ctx);
79
80 return success;
81}
82
84
85 if(ensure_layout(ctx)) {
87 if(create_ticks(ctx)) {
88 post_recreate_layout();
89 post_damage();
90 }
91
92 float width_factor = static_cast<float>(layout.color_ramp_rect.w());
93 float height_factor = static_cast<float>(layout.color_ramp_rect.h());
94 background_style.texcoord_scaling = vec2(width_factor, height_factor) / 10.0f;
95 }
96
97 create_texture();
98}
99
100void color_scale_legend::draw_content(cgv::render::context& ctx) {
101
102 begin_content(ctx);
103
104 // draw inner border
105 content_canvas.enable_shader(ctx, "rectangle");
106 content_canvas.set_style(ctx, border_style);
107 content_canvas.draw_shape(ctx, layout.color_ramp_rect.position - 1, layout.color_ramp_rect.size + 2);
108
109 // draw background grid as contrast for transparent color maps or indicator that no color map is set
110 content_canvas.enable_shader(ctx, "grid");
111 content_canvas.set_style(ctx, background_style);
112 content_canvas.draw_shape(ctx, layout.color_ramp_rect);
113
114 if(tex.is_created()) {
115 // draw the color map texture
116 content_canvas.push_modelview_matrix();
117 ivec2 pos = layout.color_ramp_rect.position;
118 ivec2 size = layout.color_ramp_rect.size;
119 float angle = 0.0f;
120
121 if(layout.orientation == Orientation::kVertical) {
122 pos.x() += layout.color_ramp_rect.size.x();
123 std::swap(size.x(), size.y());
124 angle = 90.0f;
125 }
126
127 content_canvas.mul_modelview_matrix(ctx, cgv::math::translate2h(pos));
128 content_canvas.mul_modelview_matrix(ctx, cgv::math::rotate2h(angle));
129
130 // draw color scale texture
131 color_ramp_style.use_texture_alpha = show_opacity;
132
133 if(flip_texture)
134 color_ramp_style.texcoord_scaling.x() *= -1.0f;
135
136 content_canvas.enable_shader(ctx, "rectangle");
137 content_canvas.set_style(ctx, color_ramp_style);
138 tex.enable(ctx, 0);
139 content_canvas.draw_shape(ctx, ivec2(0), size);
140 tex.disable(ctx);
141
142 content_canvas.pop_modelview_matrix(ctx);
143 }
144 content_canvas.disable_current_shader(ctx);
145
146 // draw tick marks
147 tick_renderer.render(ctx, content_canvas, cgv::render::PT_POINTS, tick_geometry, tick_style);
148
149 // draw tick labels
150 auto& font_renderer = cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx);
151 font_renderer.render(ctx, content_canvas, label_geometry, text_style);
152
153 end_content(ctx);
154}
155
157
158 add_member_control(this, "Width", layout.total_size[0], "value_slider", "min=40;max=500;step=1;ticks=true");
159 add_member_control(this, "Height", layout.total_size[1], "value_slider", "min=40;max=500;step=1;ticks=true");
160
161 add_member_control(this, "Background", background_visible_, "check", "w=100", " ");
162 add_member_control(this, "Invert Color", invert_color, "check", "w=88");
163 add_member_control(this, "Show Opacity", show_opacity, "check");
164
165 add_member_control(this, "Orientation", layout.orientation, "dropdown", "enums='Horizontal,Vertical'");
166 add_member_control(this, "Label Alignment", layout.label_alignment, "dropdown", "enums='-,Before,Inside,After'");
167
168 add_member_control(this, "Ticks", num_ticks, "value", "min=2;max=20;step=1");
169 add_member_control(this, "Number Precision", label_format.precision, "value", "w=28;min=0;max=10;step=1", " ");
170 add_member_control(this, "Auto", auto_precision, "check", "w=52", "");
171 add_member_control(this, "Show 0s", label_format.trailing_zeros, "check", "w=74", "");
172 add_member_control(this, "Int", label_format.decimal_integers, "check", "w=40");
173 add_member_control(this, "Fixed", label_format.fixed, "check");
174 add_member_control(this, "Nice", nice_ticks, "check");
175}
176
177void color_scale_legend::set_color_scale(std::shared_ptr<const cgv::media::color_scale> color_scale) {
178 if(this->color_scale != color_scale) {
179 this->color_scale = color_scale;
180 build_time.reset();
181 }
182 post_damage();
183}
184
185void color_scale_legend::set_width(size_t w) {
186 layout.total_size.x() = int(w);
187 on_set(&layout.total_size.x());
188}
189
190void color_scale_legend::set_height(size_t h) {
191 layout.total_size.y() = int(h);
192 on_set(&layout.total_size.y());
193}
194
195void color_scale_legend::set_title(const std::string& t) {
196 title = t;
197 on_set(&title);
198}
199
200void color_scale_legend::set_orientation(Orientation orientation) {
201 layout.orientation = orientation;
202 on_set(&layout.orientation);
203}
204
205void color_scale_legend::set_label_alignment(Alignment alignment) {
206 layout.label_alignment = alignment;
207 on_set(&layout.label_alignment);
208}
209
210void color_scale_legend::set_invert_color(bool flag) {
211 invert_color = flag;
212 on_set(&invert_color);
213}
214
215void color_scale_legend::set_num_ticks(unsigned n) {
216 num_ticks = n;
217 on_set(&num_ticks);
218}
219
220void color_scale_legend::set_label_precision(unsigned p) {
221 label_format.precision = p;
222 on_set(&label_format.precision);
223}
224
225void color_scale_legend::set_label_auto_precision(bool f) {
226 auto_precision = f;
227 on_set(&auto_precision);
228}
229
230void color_scale_legend::set_label_prune_trailing_zeros(bool f) {
231 label_format.trailing_zeros = !f;
232 on_set(&label_format.trailing_zeros);
233}
234
235void color_scale_legend::set_label_integer_mode(bool enabled) {
236 label_format.decimal_integers = enabled;
237 on_set(&label_format.decimal_integers);
238}
239
240void color_scale_legend::set_show_opacity(bool enabled) {
241 show_opacity = enabled;
242 on_set(&show_opacity);
243}
244
245void color_scale_legend::init_styles() {
246 auto& theme = cgv::gui::theme_info::instance();
247 rgb tick_color = theme.text();
248
249 if(invert_color)
250 tick_color = pow(rgb(1.0f) - pow(tick_color, 2.2f), 1.0f / 2.2f);
251
252 // configure style for the border rectangle
253 border_style.feather_width = 0.0f;
254 border_style.fill_color = tick_color;
255 border_style.border_width = 0.0f;
256
257 // configure style for the background rectangle
258 background_style.fill_color = rgb(0.9f);
259 background_style.border_color = rgb(0.75f);
260 background_style.feather_width = 0.0f;
261 background_style.pattern = cgv::g2d::grid2d_style::GridPattern::GP_CHECKER;
262
263 // configure style for the color scale rectangle
264 color_ramp_style = border_style;
265 color_ramp_style.use_texture = true;
266 color_ramp_style.use_texture_alpha = true;
267 color_ramp_style.use_blending = true;
268
269 // configure text style
270 text_style.fill_color = tick_color;
271 text_style.font_size = 12.0f;
272
273 // configure style for tick marks
274 tick_style.position_is_center = true;
275 tick_style.fill_color = tick_color;
276 tick_style.feather_width = 0.0f;
277}
278
279void color_scale_legend::update_layout(const ivec2& parent_size) {
280 ivec2 offset(0, 0);
281 ivec2 size(parent_size);
282
283 switch(layout.label_alignment) {
284 case Alignment::kStart:
285 if(layout.orientation == Orientation::kHorizontal) {
286 offset.x() = layout.x_label_size / 2;
287 offset.y() = layout.title_space;
288 size.x() -= layout.x_label_size;
289 size.y() -= layout.label_space + layout.title_space;
290 } else {
291 offset.x() = layout.x_label_size + 4;
292 offset.y() = 0;
293 size.x() -= layout.x_label_size + 4 + layout.title_space;
294 }
295 break;
296 case Alignment::kEnd:
297 if(layout.orientation == Orientation::kHorizontal) {
298 offset.x() = layout.x_label_size / 2;
299 offset.y() = layout.label_space;
300 size.x() -= layout.x_label_size;
301 size.y() -= layout.label_space + layout.title_space;
302 } else {
303 offset.x() = layout.title_space;
304 size.x() -= layout.x_label_size + 4 + layout.title_space;
305 }
306 break;
307 default: break;
308 }
309
310 layout.color_ramp_rect.position = offset + layout.padding;
311 layout.color_ramp_rect.size = size - 2 * layout.padding;
312}
313
314void color_scale_legend::create_texture() {
315 if(!get_context())
316 return;
317
318 if(color_scale && color_scale->get_modified_time() > build_time.get_modified_time()) {
319 const cgv::render::TextureFilter filter = color_scale->is_discrete() ? cgv::render::TF_NEAREST : cgv::render::TF_LINEAR;
320
321 size_t resolution = 256;
322 std::vector<rgba> colors = color_scale->quantize(resolution);
323 resolution = colors.size();
324
325 std::vector<cgv::rgba8> texture_data;
326 texture_data.reserve(resolution);
327 std::transform(colors.begin(), colors.end(), std::back_inserter(texture_data), [](const cgv::rgba& color) {
328 return cgv::rgba8(color);
329 });
330
332 cgv::data::data_view data_view(&data_format, texture_data.data());
333
334 tex.set_min_filter(filter);
335 tex.set_mag_filter(filter);
336 tex.create(*get_context(), data_view, 0);
337
338 build_time.modified();
339 post_recreate_layout();
340 post_damage();
341 }
342}
343
344bool color_scale_legend::create_ticks(const cgv::render::context& ctx) {
345 label_geometry.clear();
346 tick_geometry.clear();
347
348 if(!color_scale || layout.label_alignment == Alignment::kFree) {
349 layout.x_label_size = 0;
350 return true;
351 }
352
353 const cgv::vec2 domain = color_scale->get_domain();
354
355 std::vector<float> ticks;
356 if(nice_ticks)
357 ticks = color_scale->get_ticks(num_ticks);
358 else
359 cgv::utils::subdivision_sequence(std::back_inserter(ticks), domain[0], domain[1], num_ticks);
360
361 if(ticks.empty()) {
362 layout.x_label_size = 0;
363 return true;
364 }
365
366 ivec2 tick_size(1, 6);
367
368 int axis = 0;
369 int label_offset = 4;
370 Alignment label_alignment = layout.label_alignment;
377 float title_angle = 0.0f;
378
379 if(layout.orientation == Orientation::kVertical) {
380 axis = 1;
381 label_offset = 6;
382
383 std::swap(tick_size.x(), tick_size.y());
384
385 if(label_alignment == Alignment::kStart) label_alignment = Alignment::kEnd;
386 else if(label_alignment == Alignment::kEnd) label_alignment = Alignment::kStart;
387
392 std::swap(title_alignment_1, title_alignment_2);
393
394 title_angle = 90.0f;
395 }
396
397 ivec2 title_position = layout.color_ramp_rect.position;
398 ivec2 tick_start = layout.color_ramp_rect.position;
399
400 cgv::render::TextAlignment title_alignment, text_alignment;
401 title_alignment = title_alignment_1;
402 text_alignment = text_v_end;
403
404 bool inside = false;
405 switch(label_alignment) {
406 case Alignment::kStart:
407 title_position[1 - axis] -= 4;
408 tick_start[1 - axis] += layout.color_ramp_rect.size[1 - axis] + 3;
409 break;
410 case Alignment::kCenter:
411 title_position[axis] += 2;
412 title_position[1 - axis] += layout.color_ramp_rect.size[1 - axis] - (axis ? 3 : 1);
413 tick_start[1 - axis] += 3;
414 inside = true;
415 break;
416 case Alignment::kEnd:
417 title_alignment = title_alignment_2;
418 title_position[1 - axis] += layout.color_ramp_rect.size[1 - axis] + 4;
419 tick_start[1 - axis] -= 3;
420 label_offset = -label_offset;
421 text_alignment = text_v_start;
422 break;
423 default: break;
424 }
425
426 title_alignment = static_cast<cgv::render::TextAlignment>(title_alignment + cgv::render::TextAlignment::TA_LEFT);
427
428 // formatting
429 unsigned last_precision = label_format.precision;
430 if(auto_precision)
431 label_format.precision_from_range(domain[0], domain[1]);
432
433 int length = layout.color_ramp_rect.size[axis] + 2; // +2 for border
434
435 for(float tick : ticks) {
436 float normalized_position = color_scale->normalize_value(tick);
437 if(color_scale->is_reversed())
438 normalized_position = 1.0f - normalized_position;
439 int offset = static_cast<int>(std::round(normalized_position * length));
440 offset = cgv::math::clamp(offset, 0, length - 1);
441
442 ivec2 tick_pos = tick_start;
443 tick_pos[axis] += offset;
444
445 ivec2 label_pos = tick_start;
446 label_pos[1 - axis] += label_offset;
447 label_pos[axis] += offset;
448
449 tick_geometry.add(tick_pos, tick_size);
450 label_geometry.texts.push_back(label_format.convert(tick));
451 label_geometry.positions.push_back(vec3(label_pos, 0.0f));
452 }
453
454 // Restore precision
455 label_format.precision = last_precision;
456
457 label_geometry.alignments.resize(ticks.size(), text_alignment);
458 label_geometry.rotations.resize(ticks.size());
459
460 // Align first and last label inside rectangle if requested.
461 if(inside) {
462 label_geometry.positions.front()[axis] += 3.0f;
463 label_geometry.alignments.front() = static_cast<cgv::render::TextAlignment>(text_alignment | text_h_start);
464 label_geometry.positions.back()[axis] -= 3.0f;
465 label_geometry.alignments.back() = static_cast<cgv::render::TextAlignment>(text_alignment | text_h_end);
466 }
467
468 label_geometry.texts.push_back(title);
469
470 // title:
471 label_geometry.positions.push_back(cgv::vec3(static_cast<cgv::vec2>(title_position), 0.0f));
472 label_geometry.rotations.push_back(cgv::quat(cgv::vec3(0.0f, 0.0f, 1.0f), cgv::math::deg2rad(title_angle)));
473 label_geometry.alignments.push_back(title_alignment);
474
475 label_geometry.create(ctx);
476 const std::vector<cgv::g2d::msdf_text_geometry::text_info>& text_infos = label_geometry.ref_text_infos();
477
478 int x_label_size = 0;
479
480 if(label_geometry.size() > 1) {
481 if(layout.orientation == Orientation::kHorizontal) {
482 x_label_size = static_cast<int>(std::max(text_infos.front().normalized_width, text_infos[text_infos.size() - 2].normalized_width) * text_style.font_size);
483 } else {
484 float max_width = -1.0f;
485 for(size_t i = 0; i < ticks.size(); ++i)
486 max_width = std::max(max_width, text_infos[i].normalized_width);
487 x_label_size = static_cast<int>(max_width * text_style.font_size);
488 }
489 }
490
491 if(layout.x_label_size != x_label_size) {
492 layout.x_label_size = x_label_size;
493 return true;
494 }
495 return false;
496}
497
498} // namespace overlay
499} // namespace cgv
void set_name(const std::string &_name)
set a new parent node
Definition named.cxx:13
A data_format describes a multidimensional data block of data entries.
Definition data_format.h:17
the data view gives access to a data array of one, two, three or four dimensions.
Definition data_view.h:153
This class provides methods to test if a stored pointer points to addresses of given variables or ins...
bool points_to_one_of(const T &ref, const Ts &... refs) const
Return true if the stored pointer points to one of the given objects.
bool points_to(const T &ref) const
Return true if the stored pointer points to the given object.
bool points_to_member_of(const T &ref) const
Return true if the stored pointer points inside the address range of the given object.
data::ref_ptr< control< T > > add_member_control(cgv::base::base *base_ptr, const std::string &label, T &value, const std::string &gui_type="", const std::string &options="", const std::string &align="\n")
add control with callback to cgv::base::on_set method on cgv::gui::control::value_change
Definition provider.h:137
T & y()
return second component
Definition fvec.h:140
T & x()
return first component
Definition fvec.h:136
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
virtual void on_set(void *member_ptr) override
default implementation of that calls handle_member_change and afterwards upates the member in the gui...
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
void init_frame(cgv::render::context &ctx) override
this method is called in one pass over all drawables before the draw method
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
void handle_member_change(cgv::data::informed_ptr ptr) override
implement to handle member changes
void create_gui_impl() override
virtual method to implement the derived class gui creation
void set_size(const ivec2 &size)
set the default size of the overlay before stretch gets applied
Definition overlay.cxx:111
cgv::g2d::irect get_rectangle() const
return the current rectangle area (in screen coordinates) of the overlay taking layout into account
Definition overlay.h:95
void update_layout()
update the layout of the overlay container
Definition overlay.cxx:215
bool background_visible_
whether the background is visible (true by default)
base class for all drawables, which is independent of the used rendering API.
Definition context.h:668
context * get_context() const
access the current context. The context will be available latestly in the init method but not in the ...
Definition drawable.cxx:37
virtual bool is_created() const
return whether component has been created
Definition context.cxx:2207
void set_mag_filter(TextureFilter _mag_filter)
set the magnification filter
Definition texture.cxx:143
bool create(const context &ctx, TextureType _tt=TT_UNDEF, unsigned width=-1, unsigned height=-1, unsigned depth=-1)
create the texture of dimension and resolution specified in the data format base class.
Definition texture.cxx:213
bool disable(const context &ctx)
disable texture and restore state from before last enable call
Definition texture.cxx:774
bool enable(const context &ctx, int tex_unit=-1)
enable this texture in the given texture unit, -1 corresponds to the current unit.
Definition texture.cxx:763
bool destruct(const context &ctx)
destruct the texture and free texture memory and handle
Definition texture.cxx:748
void set_min_filter(TextureFilter _min_filter, float _anisotropy=2.0f)
set the minification filters, if minification is set to TF_ANISOTROP, the second floating point param...
Definition texture.cxx:125
@ CF_RGBA
color format with components R, G and B
TextAlignment
different text alignments
Definition context.h:329
@ TA_TOP
center of top edge of text bounds
Definition context.h:333
@ TA_BOTTOM
center of bottom edge of text bounds
Definition context.h:334
@ TA_RIGHT
center of right edge of text bounds
Definition context.h:332
@ TA_LEFT
center of left edge of text bounds
Definition context.h:331
TextureFilter
different texture filter
Definition context.h:245
@ TI_UINT8
signed integer stored in 64 bits
Definition type_id.h:23
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:233
this header is dependency free
Definition print.h:11
cgv::media::color< float, cgv::media::RGB > rgb
declare rgb color type with 32 bit components
Definition color.h:896
cgv::math::fvec< int32_t, 2 > ivec2
declare type of 2d 32 bit integer vectors
Definition fvec.h:708
cgv::math::fvec< float, 2 > vec2
declare type of 2d single precision floating point vectors
Definition fvec.h:681
cgv::math::fvec< float, 3 > vec3
declare type of 3d single precision floating point vectors
Definition fvec.h:683
Helper functions to process strings.