cgv
Loading...
Searching...
No Matches
color_map_legend.cxx
1#include "color_map_legend.h"
2
3#include <cgv/gui/theme_info.h>
4#include <cgv/math/ftransform.h>
5#include <cgv/utils/scan.h>
6#include <cgv_g2d/msdf_gl_font_renderer.h>
7
8namespace cgv {
9namespace app {
10
11color_map_legend::color_map_legend() {
12
13 set_name("Color Map Legend");
14
15 // TODO: Remove padding from layout and use get_content_rect() as a starting point instead.
16 layout.padding = padding();
17 layout.total_size = ivec2(300, 60);
18
19 set_size(layout.total_size);
20
21 tick_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::rectangle);
22}
23
25
26 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, -1);
27
29
30 tex.destruct(ctx);
31
32 tick_renderer.destruct(ctx);
33 ticks.destruct(ctx);
34 labels.destruct(ctx);
35}
36
38
39 if(m.member_of(layout.total_size)) {
40 // TODO: minimum width and height depend on other layout parameters
41 layout.total_size.y() = std::max(layout.total_size.y(), 2 * layout.padding + 4 + layout.label_space);
42 set_size(layout.total_size);
43 }
44
45 if(m.one_of(background_visible_, invert_color))
46 init_styles();
47
48 if(m.is(num_ticks))
49 num_ticks = cgv::math::clamp(num_ticks, 2u, 100u);
50
51 if(m.is(title)) {
52 layout.title_space = title == "" ? 0 : 12;
53 post_recreate_layout();
54 }
55
56 if(m.one_of(layout.orientation, layout.label_alignment, value_range, num_ticks) || m.member_of(label_format))
57 post_recreate_layout();
58}
59
61
62 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, 1);
63
64 register_shader("rectangle", cgv::g2d::shaders::rectangle);
65 register_shader("grid", cgv::g2d::shaders::grid);
66
67 bool success = canvas_overlay::init(ctx);
68
69 success &= tick_renderer.init(ctx);
70
71 labels.init(ctx);
72
73 return success;
74}
75
77
78 if(ensure_layout(ctx)) {
79 create_labels(ctx);
80 layout.update(get_rectangle().size);
81 create_ticks();
82
83 float width_factor = static_cast<float>(layout.color_map_rect.w());
84 float height_factor = static_cast<float>(layout.color_map_rect.h());
85 background_style.texcoord_scaling = vec2(width_factor, height_factor) / 10.0f;
86 }
87}
88
89void color_map_legend::draw_content(cgv::render::context& ctx) {
90
91 begin_content(ctx);
92
93 // draw inner border
94 content_canvas.enable_shader(ctx, "rectangle");
95 content_canvas.set_style(ctx, border_style);
96 content_canvas.draw_shape(ctx, layout.color_map_rect.position - 1, layout.color_map_rect.size + 2);
97
98 // draw background grid as contrast for transparent color maps or indicator that no color map is set
99 content_canvas.enable_shader(ctx, "grid");
100 content_canvas.set_style(ctx, background_style);
101 content_canvas.draw_shape(ctx, layout.color_map_rect);
102
103 if(tex.is_created()) {
104 // draw the color map texture
105 content_canvas.push_modelview_matrix();
106 ivec2 pos = layout.color_map_rect.position;
107 ivec2 size = layout.color_map_rect.size;
108 float angle = 0.0f;
109
110 if(layout.orientation == OO_VERTICAL) {
111 pos.x() += layout.color_map_rect.size.x();
112 std::swap(size.x(), size.y());
113 angle = 90.0f;
114 }
115
116 content_canvas.mul_modelview_matrix(ctx, cgv::math::translate2h(pos));
117 content_canvas.mul_modelview_matrix(ctx, cgv::math::rotate2h(angle));
118
119 // draw color scale texture
120 color_map_style.use_texture_alpha = show_opacity;
121
122 color_map_style.texcoord_offset.x() = display_range[0];
123 color_map_style.texcoord_scaling.x() = display_range[1] - display_range[0];
124
125 if(flip_texture)
126 color_map_style.texcoord_scaling.x() *= -1.0f;
127
128 content_canvas.enable_shader(ctx, "rectangle");
129 content_canvas.set_style(ctx, color_map_style);
130 tex.enable(ctx, 0);
131 content_canvas.draw_shape(ctx, ivec2(0), size);
132 tex.disable(ctx);
133
134 content_canvas.pop_modelview_matrix(ctx);
135 }
136 content_canvas.disable_current_shader(ctx);
137
138 // draw tick marks
139 tick_renderer.render(ctx, content_canvas, cgv::render::PT_POINTS, ticks, tick_style);
140
141 // draw tick labels
142 auto& font_renderer = cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx);
143 if(font_renderer.enable(ctx, content_canvas, labels, text_style)) {
144 font_renderer.draw(ctx, content_canvas, labels, 0, static_cast<int>(num_ticks));
145
146 content_canvas.push_modelview_matrix();
147 content_canvas.mul_modelview_matrix(ctx, cgv::math::translate2h(layout.title_position));
148 content_canvas.mul_modelview_matrix(ctx, cgv::math::rotate2h(layout.title_angle));
149
150 font_renderer.draw(ctx, content_canvas, labels, num_ticks, 1);
151
152 content_canvas.pop_modelview_matrix(ctx);
153 font_renderer.disable(ctx, labels);
154 }
155
156 end_content(ctx);
157}
158
160
161 add_member_control(this, "Width", layout.total_size[0], "value_slider", "min=40;max=500;step=1;ticks=true");
162 add_member_control(this, "Height", layout.total_size[1], "value_slider", "min=40;max=500;step=1;ticks=true");
163
164 add_member_control(this, "Background", background_visible_, "check", "w=100", " ");
165 add_member_control(this, "Invert Color", invert_color, "check", "w=88");
166 add_member_control(this, "Show Opacity", show_opacity, "check");
167
168 add_member_control(this, "Orientation", layout.orientation, "dropdown", "enums='Horizontal,Vertical'");
169 add_member_control(this, "Label Alignment", layout.label_alignment, "dropdown", "enums='-,Before,Inside,After'");
170
171 add_member_control(this, "Ticks", num_ticks, "value", "min=2;max=10;step=1");
172 add_member_control(this, "Number Precision", label_format.precision, "value", "w=28;min=0;max=10;step=1", " ");
173 add_member_control(this, "Auto", label_format.auto_precision, "check", "w=52", "");
174 add_member_control(this, "Show 0s", label_format.trailing_zeros, "check", "w=74", "");
175 add_member_control(this, "Int", label_format.integers, "check", "w=40");
176}
177
178void color_map_legend::set_color_map(cgv::render::context& ctx, const cgv::render::color_map& cm) {
179
180 cgv::render::TextureFilter filter = cgv::render::TF_LINEAR;
181 if(cm.has_texture_support()) {
182 const cgv::render::gl_color_map* gl_cm_ptr = dynamic_cast<const cgv::render::gl_color_map*>(&cm);
183 filter = gl_cm_ptr->is_linear_filtering_enabled() ? cgv::render::TF_LINEAR : cgv::render::TF_NEAREST;
184 }
185
186 unsigned resolution = cm.get_resolution();
187 std::vector<rgb> color_data = cm.interpolate_color(static_cast<size_t>(resolution));
188 std::vector<float> opacity_data(static_cast<size_t>(resolution), 1.0f);
189
190 if(!cm.ref_opacity_points().empty())
191 opacity_data = cm.interpolate_opacity(static_cast<size_t>(resolution));
192
193 std::vector<uint8_t> data_8(2 * 4 * color_data.size());
194 for(unsigned i = 0; i < color_data.size(); ++i) {
195 rgba col = color_data[i];
196 data_8[4 * i + 0] = static_cast<uint8_t>(255.0f * col.R());
197 data_8[4 * i + 1] = static_cast<uint8_t>(255.0f * col.G());
198 data_8[4 * i + 2] = static_cast<uint8_t>(255.0f * col.B());
199 data_8[4 * i + 3] = static_cast<uint8_t>(255.0f * opacity_data[i]);
200 }
201
202 std::copy(data_8.begin(), data_8.begin() + 4 * resolution, data_8.begin() + 4 * resolution);
203
205
206 unsigned width = (unsigned)tex.get_width();
207
208 bool replaced = false;
209 if(tex.is_created() && width == resolution && tex.get_nr_components() == 4) {
210 tex.set_min_filter(filter);
211 tex.set_mag_filter(filter);
212 replaced = tex.replace(ctx, 0, 0, dv);
213 }
214
215 if(!replaced) {
216 tex.destruct(ctx);
217 tex = cgv::render::texture("uint8[R,G,B,A]", filter, filter);
218 tex.create(ctx, dv, 0);
219 }
220
221 post_damage();
222}
223
224void color_map_legend::set_width(size_t w) {
225 layout.total_size.x() = int(w);
226 on_set(&layout.total_size.x());
227}
228
229void color_map_legend::set_height(size_t h) {
230 layout.total_size.y() = int(h);
231 on_set(&layout.total_size.y());
232}
233
234void color_map_legend::set_title(const std::string& t) {
235 title = t;
236 on_set(&title);
237}
238
239void color_map_legend::set_orientation(OrientationOption orientation) {
240 layout.orientation = orientation;
241 on_set(&layout.orientation);
242}
243
244void color_map_legend::set_label_alignment(AlignmentOption alignment) {
245 layout.label_alignment = alignment;
246 on_set(&layout.label_alignment);
247}
248
249void color_map_legend::set_range(vec2 r) {
250 flip_texture = r.x() > r.y();
251 if(flip_texture)
252 std::swap(r.x(), r.y());
253
254 value_range = r;
255 on_set(&value_range);
256}
257
258void color_map_legend::set_display_range(vec2 r) {
259 display_range = r;
260 on_set(&display_range);
261}
262
263void color_map_legend::set_invert_color(bool flag) {
264 invert_color = flag;
265 on_set(&invert_color);
266}
267
268void color_map_legend::set_num_ticks(unsigned n) {
269 num_ticks = n;
270 on_set(&num_ticks);
271}
272
273void color_map_legend::set_label_precision(unsigned p) {
274 label_format.precision = p;
275 on_set(&label_format.precision);
276}
277
278void color_map_legend::set_label_auto_precision(bool f) {
279 label_format.auto_precision = f;
280 on_set(&label_format.auto_precision);
281}
282
283void color_map_legend::set_label_prune_trailing_zeros(bool f) {
284 label_format.trailing_zeros = !f;
285 on_set(&label_format.trailing_zeros);
286}
287
288void color_map_legend::set_label_integer_mode(bool enabled) {
289 label_format.integers = enabled;
290 on_set(&label_format.integers);
291}
292
293void color_map_legend::set_show_opacity(bool enabled) {
294 show_opacity = enabled;
295 on_set(&show_opacity);
296}
297
298void color_map_legend::init_styles() {
299 auto& theme = cgv::gui::theme_info::instance();
300 rgb tick_color = theme.text();
301
302 if(invert_color)
303 tick_color = pow(rgb(1.0f) - pow(tick_color, 2.2f), 1.0f / 2.2f);
304
305 // configure style for the border rectangle
306 border_style.feather_width = 0.0f;
307 border_style.fill_color = tick_color;
308 border_style.border_width = 0.0f;
309
310 // configure style for the background rectangle
311 background_style.fill_color = rgb(0.9f);
312 background_style.border_color = rgb(0.75f);
313 background_style.feather_width = 0.0f;
314 background_style.pattern = cgv::g2d::grid2d_style::GridPattern::GP_CHECKER;
315
316 // configure style for the color scale rectangle
317 color_map_style = border_style;
318 color_map_style.use_texture = true;
319 color_map_style.use_texture_alpha = true;
320 color_map_style.use_blending = true;
321
322 // configure text style
323 text_style.fill_color = tick_color;
324 text_style.font_size = 12.0f;
325
326 // configure style for tick marks
327 tick_style.position_is_center = true;
328 tick_style.fill_color = tick_color;
329 tick_style.feather_width = 0.0f;
330}
331
332void color_map_legend::create_labels(const cgv::render::context& ctx) {
333
334 labels.clear();
335
336 if(layout.label_alignment == AO_FREE)
337 return;
338
339 unsigned precision = label_format.precision;
340
341 if(label_format.auto_precision) {
342 precision = 0;
343 const float delta = std::abs(value_range[1] - value_range[0]);
344 const unsigned max_precision = 7;
345
346 if(delta > 5.0f) {
347 precision = 1;
348 } else {
349 float limit = 1.0f;
350 for(unsigned i = 2; i <= max_precision; ++i) {
351 if(delta > limit || i == max_precision) {
352 precision = i;
353 break;
354 }
355 limit /= 2.0f;
356 }
357 }
358 }
359
360 float max_width = -1.0f;
361
362 std::vector<std::string> label_texts;
363
364 for(size_t i = 0; i < num_ticks; ++i) {
365 float fi = static_cast<float>(i);
366 float t = fi / static_cast<float>(num_ticks - 1);
367 float val = cgv::math::lerp(value_range.x(), value_range.y(), t);
368
369 std::string str;
370
371 if(label_format.integers)
372 str = std::to_string(static_cast<int>(round(val)));
373 else {
374 str = cgv::utils::to_string(val, -1, precision, true);
375 if(!label_format.trailing_zeros && str.length() > 1)
376 cgv::utils::rtrim(cgv::utils::rtrim(str, "0"), ".");
377 }
378
379 label_texts.push_back(str);
380 }
381
382 label_texts.push_back(title);
383
384 labels.set_text_array(ctx, label_texts);
385 labels.positions.resize(label_texts.size(), { 0.0f });
386 labels.alignments.resize(label_texts.size(), cgv::render::TextAlignment::TA_NONE);
387 labels.alignments.back() = cgv::render::TextAlignment::TA_BOTTOM_LEFT;
388
389 auto& text_infos = labels.ref_text_infos();
390 for(size_t i = 0; i < static_cast<size_t>(num_ticks); ++i)
391 max_width = std::max(max_width, text_infos[i].normalized_width);
392
393 if(labels.size() > 1) {
394 if(layout.orientation == OO_HORIZONTAL)
395 layout.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);
396 else
397 layout.x_label_size = static_cast<int>(max_width * text_style.font_size);
398 } else {
399 layout.x_label_size = 0;
400 }
401}
402
403void color_map_legend::create_ticks() {
404
405 ticks.clear();
406
407 if(layout.label_alignment == AO_FREE)
408 return;
409
410 ivec2 tick_size(1, 6);
411
412 int axis = 0;
413 int label_offset = 4;
414 AlignmentOption label_alignment = layout.label_alignment;
421
422 layout.title_angle = 0.0f;
423
424 if(layout.orientation == OO_VERTICAL) {
425 axis = 1;
426 label_offset = 6;
427
428 std::swap(tick_size.x(), tick_size.y());
429
430 if(label_alignment == AO_START) label_alignment = AO_END;
431 else if(label_alignment == AO_END) label_alignment = AO_START;
432
437 std::swap(title_alignment_1, title_alignment_2);
438
439 layout.title_angle = 90.0f;
440 }
441
442 ivec2 color_rect_pos = layout.color_map_rect.position;
443 ivec2 color_rect_size = layout.color_map_rect.size;
444
445 int length = color_rect_size[axis];
446 float step = static_cast<float>(length + 1) / static_cast<float>(num_ticks - 1);
447
448 ivec2 title_pos = color_rect_pos;
449 ivec2 tick_start = color_rect_pos;
450
451 cgv::render::TextAlignment title_alignment, text_alignment;
452 title_alignment = title_alignment_1;
453 text_alignment = text_v_end;
454
455 bool inside = false;
456 switch(label_alignment) {
457 case AO_START:
458 title_pos[1 - axis] -= 4;
459 tick_start[1 - axis] += color_rect_size[1 - axis] + 3;
460 break;
461 case AO_CENTER:
462 title_pos[axis] += 2;
463 title_pos[1 - axis] += color_rect_size[1 - axis] - (axis ? 3 : 1);
464 tick_start[1 - axis] += 3;
465 inside = true;
466 break;
467 case AO_END:
468 title_alignment = title_alignment_2;
469 title_pos[1 - axis] += color_rect_size[1 - axis] + 4;
470 tick_start[1 - axis] -= 3;
471 label_offset = -label_offset;
472 text_alignment = text_v_start;
473 break;
474 default: break;
475 }
476
477 title_alignment = static_cast<cgv::render::TextAlignment>(title_alignment + cgv::render::TextAlignment::TA_LEFT);
478
479 for(size_t i = 0; i < num_ticks; ++i) {
480 float fi = static_cast<float>(i);
481 int offset = static_cast<int>(round(fi * step));
482
483 ivec2 tick_pos = tick_start;
484 tick_pos[axis] += offset;
485
486 ivec2 label_pos = tick_start;
487 label_pos[1 - axis] += label_offset;
488 label_pos[axis] += offset;
489
490 cgv::render::TextAlignment alignment = text_alignment;
491
492 if(inside) {
493 if(i == 0) {
494 label_pos[axis] += 3;
495 alignment = static_cast<cgv::render::TextAlignment>(alignment | text_h_start);
496 } else if(i == num_ticks - 1) {
497 label_pos[axis] -= 3;
498 alignment = static_cast<cgv::render::TextAlignment>(alignment | text_h_end);
499 }
500 }
501
502 ticks.add(tick_pos, tick_size);
503
504 labels.positions[i] = vec3(label_pos, 0.0f);
505 labels.alignments[i] = alignment;
506 }
507
508 layout.title_position = title_pos;
509
510 if(labels.alignments.size() > 0)
511 labels.alignments.back() = title_alignment;
512}
513
514}
515}
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
virtual void on_set(void *member_ptr) override
default implementation of that calls handle_member_change and afterwards upates the member in the gui...
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(const cgv::utils::pointer_test &m) override
implement to handle member changes
void init_frame(cgv::render::context &ctx) override
this method is called in one pass over all drawables before the draw method
void create_gui_impl() override
virtual method to implement the derived class gui creation
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
cgv::g2d::irect get_rectangle() const
return the current rectangle area (in screen coordinates) of the overlay taking layout into account
Definition overlay.h:139
void set_size(const ivec2 &size)
set the default size of the overlay before stretch gets applied
Definition overlay.cxx:108
bool background_visible_
whether the background is visible (true by default)
void set_name(const std::string &_name)
set a new parent node
Definition named.cxx:13
unsigned int get_nr_components() const
return the number of components
A data_format describes a multidimensional data block of data entries.
Definition data_format.h:17
size_t get_width() const
return the resolution in the first dimension, or 1 if not defined
the data view gives access to a data array of one, two, three or four dimensions.
Definition data_view.h:153
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()
second element
Definition fvec.h:159
T & x()
first element
Definition fvec.h:155
base class for all drawables, which is independent of the used rendering API.
Definition context.h:621
virtual bool is_created() const
return whether component has been created
Definition context.cxx:2046
the texture class encapsulates all functionality independent of the rendering api.
Definition texture.h:15
void set_mag_filter(TextureFilter _mag_filter)
set the magnification filter
Definition texture.cxx:138
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:208
bool disable(const context &ctx)
disable texture and restore state from before last enable call
Definition texture.cxx:756
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:745
bool destruct(const context &ctx)
destruct the texture and free texture memory and handle
Definition texture.cxx:730
bool replace(const context &ctx, int x, const cgv::data::const_data_view &data, int level=-1, const std::vector< cgv::data::data_view > *palettes=0)
replace a block within a 1d texture with the given data.
Definition texture.cxx:621
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:120
bool member_of(const T &ref) const
Test if the stored pointer points inside the address range of the given object instance.
bool is(const void *ptr) const
Test if the stored pointer points to the given pointer address.
@ CF_RGBA
color format with components R, G and B
TextAlignment
different text alignments
Definition context.h:273
@ TA_TOP
center of top edge of text bounds
Definition context.h:277
@ TA_BOTTOM
center of bottom edge of text bounds
Definition context.h:278
@ TA_NONE
no alignment
Definition context.h:274
@ TA_RIGHT
center of right edge of text bounds
Definition context.h:276
@ TA_BOTTOM_LEFT
bottom left corner of text bounds
Definition context.h:281
@ TA_LEFT
center of left edge of text bounds
Definition context.h:275
TextureFilter
different texture filter
Definition context.h:189
@ TI_UINT8
signed integer stored in 64 bits
Definition type_id.h:23
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
std::string & rtrim(std::string &str, const std::string &chars)
trim white space or other characters from end of string
Definition scan.cxx:703
the cgv namespace
Definition print.h:11
cgv::media::color< float, cgv::media::RGB > rgb
declare rgb color type with 32 bit components
Definition color.h:853
cgv::math::fvec< int32_t, 2 > ivec2
declare type of 2d 32 bit integer vectors
Definition fvec.h:694
cgv::math::fvec< float, 2 > vec2
declare type of 2d single precision floating point vectors
Definition fvec.h:667
cgv::math::fvec< float, 3 > vec3
declare type of 3d single precision floating point vectors
Definition fvec.h:669
Helper functions to process strings.