cgv
Loading...
Searching...
No Matches
color_map_editor.cxx
1#include "color_map_editor.h"
2
3#include <cgv/gui/key_event.h>
4#include <cgv/gui/mouse_event.h>
5#include <cgv/gui/theme_info.h>
6#include <cgv/math/ftransform.h>
8#include <cgv/utils/file.h>
9#include <cgv_gl/gl/gl.h>
10#include <cgv_g2d/msdf_font.h>
11#include <cgv_g2d/msdf_gl_font_renderer.h>
12
13namespace cgv {
14namespace app {
15
16const float color_map_editor::color_point::default_width = 12.0f;
17const float color_map_editor::color_point::default_height = 18.0f;
18
19const float color_map_editor::opacity_point::default_size = 12.0f;
20
21color_map_editor::color_map_editor() {
22
23 set_name("Color Scale Editor");
24 blocks_events(true);
25
26 resolution = (cgv::type::DummyEnum)256;
27 opacity_scale_exponent = 1.0f;
28 supports_opacity = false;
29 use_interpolation = true;
30 use_linear_filtering = true;
31 range = vec2(0.0f, 1.0f);
32
33 layout.padding = padding();
34 layout.total_height = supports_opacity ? 200 : 60;
35
36 set_size(ivec2(600u, layout.total_height));
37
38 mouse_is_on_overlay = false;
39 cursor_position = { 0 };
40 show_value_label = false;
41
42 cmc.color_points.set_constraint(layout.color_handles_rect);
43 cmc.color_points.callback = std::bind(&color_map_editor::handle_color_drag, this, std::placeholders::_1);
44
45 cmc.opacity_points.set_constraint(layout.opacity_editor_rect);
46 cmc.opacity_points.callback = std::bind(&color_map_editor::handle_opacity_drag, this, std::placeholders::_1);
47
48 color_handle_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::arrow);
49 opacity_handle_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::rectangle);
50 line_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::line);
51 polygon_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::polygon);
52}
53
55
56 cgv::g2d::ref_msdf_font_regular(ctx, -1);
57 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, -1);
58
60
61 color_handle_renderer.destruct(ctx);
62 opacity_handle_renderer.destruct(ctx);
63 line_renderer.destruct(ctx);
64 polygon_renderer.destruct(ctx);
65
66 bg_tex.destruct(ctx);
67 preview_tex.destruct(ctx);
68 hist_tex.destruct(ctx);
69}
70
72
73 switch(e.get_action()) {
75 switch(e.get_key()) {
77 cursor_label = "+";
78 post_damage();
79 break;
81 cursor_label = "-";
82 post_damage();
83 break;
84 default:
85 break;
86 }
87 break;
89 switch(e.get_key()) {
92 cursor_label = "";
93 post_damage();
94 break;
95 default:
96 break;
97 }
98 break;
99 default:
100 break;
101 }
102 return false;
103}
104
106
107 switch(e.get_action()) {
109 mouse_is_on_overlay = true;
110 return true;
112 mouse_is_on_overlay = false;
113 post_damage();
114 return true;
117 if(get_context())
118 cursor_position = local_mouse_pos;// ivec2(me.get_x(), get_context()->get_height() - 1 - me.get_y());
119 if(!cursor_label.empty())
120 post_damage();
121 break;
122 }
123
124 bool request_clear_selection = false;
125
127 if(e.get_action() == cgv::gui::MA_PRESS) {
128 request_clear_selection = is_hit(local_mouse_pos);
129
130 switch(e.get_modifiers()) {
132 if(!get_hit_point(local_mouse_pos))
133 add_point(local_mouse_pos);
134 break;
135 case cgv::gui::EM_ALT:
136 {
137 cgv::g2d::draggable* hit_point = get_hit_point(local_mouse_pos);
138 if(hit_point)
139 remove_point(hit_point);
140 break;
141 }
142 default:
143 break;
144 }
145 }
146 }
147
148 if(cmc.color_points.handle(e, get_viewport_size(), get_rectangle()))
149 return true;
150 if(cmc.opacity_points.handle(e, get_viewport_size(), get_rectangle()))
151 return true;
152
153 if(request_clear_selection) {
154 cmc.color_points.clear_selected();
155 cmc.opacity_points.clear_selected();
156 handle_drag_end();
157 }
158
159 return false;
160}
161
163
164 if(m.is(layout.total_height)) {
165 ivec2 size = get_rectangle().size;
166 size.y() = layout.total_height;
167 set_size(size);
168 }
169
170 if(m.is(opacity_scale_exponent)) {
171 opacity_scale_exponent = cgv::math::clamp(opacity_scale_exponent, 1.0f, 5.0f);
172
173 update_point_positions();
174 sort_points();
175 update_geometry();
176 }
177
178 if(m.is(resolution)) {
180 if(cmc.cm)
181 cmc.cm->set_resolution(resolution);
182 update_color_map(false);
183 }
184
185 if(m.is(use_interpolation)) {
187 if(cmc.cm)
188 cmc.cm->enable_interpolation(use_interpolation);
189 update_color_map(false);
190 }
191
192 if(m.is(use_linear_filtering)) {
194 if(cmc.cm) {
195 cgv::render::gl_color_map* gl_cm = cmc.get_gl_color_map();
196 if(gl_cm)
197 gl_cm->enable_linear_filtering(use_linear_filtering);
198 }
199 update_color_map(false);
200 }
201
202 for(unsigned i = 0; i < cmc.color_points.size(); ++i) {
203 if(m.is(cmc.color_points[i].col)) {
204 update_color_map(true);
205 break;
206 }
207 }
208
209 for(unsigned i = 0; i < cmc.opacity_points.size(); ++i) {
210 if(m.is(cmc.opacity_points[i].val[1])) {
211 cmc.opacity_points[i].update_pos(layout, opacity_scale_exponent);
212 update_color_map(true);
213 break;
214 }
215 }
216
217 if(m.is(supports_opacity)) {
218 layout.total_height = supports_opacity ? 200 : 60;
219 on_set(&layout.total_height);
220
221 if(supports_opacity) {
222 layout.total_height = 200;
223 } else {
224 layout.total_height = 60;
225 if(cmc.cm) {
226 cmc.cm->clear_opacity_points();
227 cmc.opacity_points.clear();
228
229 update_point_positions();
230 update_color_map(false);
231 }
232 }
233
234 post_recreate_layout();
236 }
237}
238
240
241 cgv::g2d::ref_msdf_font_regular(ctx, 1);
242 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, 1);
243
244 register_shader("rectangle", cgv::g2d::shaders::rectangle);
245 register_shader("circle", cgv::g2d::shaders::circle);
246 register_shader("histogram", "heightfield1d.glpr");
247 register_shader("background", "color_map_editor_bg.glpr");
248
249 bool success = canvas_overlay::init(ctx);
250
251 success &= color_handle_renderer.init(ctx);
252 success &= opacity_handle_renderer.init(ctx);
253 success &= line_renderer.init(ctx);
254 success &= polygon_renderer.init(ctx);
255
256 init_preview_texture(ctx);
257 update_color_map(false);
258
259 rgb a(0.75f);
260 rgb b(0.9f);
261 std::vector<rgb> bg_data = { a, b, b, a };
262
263 bg_tex.destruct(ctx);
265 bg_tex = cgv::render::texture("flt32[R,G,B]", cgv::render::TF_NEAREST, cgv::render::TF_NEAREST, cgv::render::TW_REPEAT, cgv::render::TW_REPEAT);
266 success &= bg_tex.create(ctx, bg_dv, 0);
267
268 return success;
269}
270
272
273 if(ensure_layout(ctx)) {
274 ivec2 container_size = get_rectangle().size;
275 layout.update(container_size, supports_opacity);
276
277 auto& bg_prog = content_canvas.enable_shader(ctx, "background");
278 float width_factor = static_cast<float>(layout.opacity_editor_rect.w()) / static_cast<float>(layout.opacity_editor_rect.h());
279 bg_style.texcoord_scaling = vec2(5.0f * width_factor, 5.0f);
280 bg_style.apply(ctx, bg_prog);
281 content_canvas.disable_current_shader(ctx);
282
283 update_point_positions();
284 sort_points();
285 update_geometry();
286 cmc.color_points.set_constraint(layout.color_handles_rect);
287 cmc.opacity_points.set_constraint(layout.opacity_editor_rect);
288 }
289}
290
291void color_map_editor::draw_content(cgv::render::context& ctx) {
292
293 begin_content(ctx);
294
295 // draw inner border
296 ivec2 container_size = get_rectangle().size;
297 content_canvas.enable_shader(ctx, "rectangle");
298 content_canvas.set_style(ctx, border_style);
299 content_canvas.draw_shape(ctx, ivec2(layout.padding - 1) + ivec2(0, 10), container_size - 2 * layout.padding + 2 - ivec2(0, 10));
300
301 if(cmc.cm) {
302 // draw color scale texture
303 content_canvas.set_style(ctx, color_map_style);
304 preview_tex.enable(ctx, 0);
305 content_canvas.draw_shape(ctx, layout.color_editor_rect);
306 preview_tex.disable(ctx);
307 //content_canvas.disable_current_shader(ctx);
308
309 if(supports_opacity) {
310 // draw opacity editor checkerboard background
311 auto& bg_prog = content_canvas.enable_shader(ctx, "background");
312 bg_style.apply(ctx, bg_prog);
313 bg_prog.set_uniform(ctx, "scale_exponent", opacity_scale_exponent);
314 bg_tex.enable(ctx, 0);
315 content_canvas.draw_shape(ctx, layout.opacity_editor_rect);
316 bg_tex.disable(ctx);
317 content_canvas.disable_current_shader(ctx);
318
319 // draw histogram
320 if(histogram_type != (cgv::type::DummyEnum)0 && hist_tex.is_created()) {
321 auto& hist_prog = content_canvas.enable_shader(ctx, "histogram");
322 hist_prog.set_uniform(ctx, "max_value", hist_norm_ignore_zero ? hist_max_non_zero : hist_max);
323 hist_prog.set_uniform(ctx, "norm_gamma", hist_norm_gamma);
324 hist_prog.set_uniform(ctx, "sampling_type", cgv::math::clamp(static_cast<unsigned>(histogram_type) - 1, 0u, 2u));
325 hist_style.apply(ctx, hist_prog);
326
327 hist_tex.enable(ctx, 1);
328 content_canvas.draw_shape(ctx, layout.opacity_editor_rect);
329 hist_tex.disable(ctx);
330 content_canvas.disable_current_shader(ctx);
331 }
332
333 preview_tex.enable(ctx, 0);
334 // draw transfer function area polygon
335 polygon_renderer.render(ctx, content_canvas, cgv::render::PT_TRIANGLE_STRIP, cmc.triangles, polygon_style);
336
337 // draw transfer function lines
338 line_renderer.render(ctx, content_canvas, cgv::render::PT_LINE_STRIP, cmc.lines, line_style);
339 preview_tex.disable(ctx);
340
341 // draw separator line
342 content_canvas.enable_shader(ctx, "rectangle");
343 content_canvas.set_style(ctx, border_style);
344 content_canvas.draw_shape(ctx,
345 ivec2(layout.color_editor_rect.x(), layout.color_editor_rect.y1()),
346 ivec2(container_size.x() - 2 * layout.padding, 1)
347 );
348 content_canvas.disable_current_shader(ctx);
349 }
350
351 // draw control points
352 // color handles
353 color_handle_renderer.render(ctx, content_canvas, cgv::render::PT_LINES, cmc.color_handles, color_handle_style);
354
355 // opacity handles
356 if(supports_opacity) {
357 auto& opacity_handle_prog = opacity_handle_renderer.enable_prog(ctx);
358 opacity_handle_prog.set_attribute(ctx, "size", vec2(opacity_point::default_size)); // size is constant for all points
359 opacity_handle_renderer.render(ctx, content_canvas, cgv::render::PT_POINTS, cmc.opacity_handles, opacity_handle_style);
360 }
361 } else {
362 content_canvas.disable_current_shader(ctx);
363 }
364
365 auto& font = cgv::g2d::ref_msdf_font_regular(ctx);
366 auto& font_renderer = cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx);
367 cgv::g2d::msdf_gl_font_renderer::text_render_info text_render_info;
368
369 if(show_value_label) {
370 cgv::g2d::irect rectangle = value_label_rectangle;
371 rectangle.translate(0, 5);
372 rectangle.size += ivec2(10, 6);
373
374 content_canvas.enable_shader(ctx, "rectangle");
375 content_canvas.set_style(ctx, label_box_style);
376 content_canvas.draw_shape(ctx, rectangle);
377 content_canvas.disable_current_shader(ctx);
378
379 text_render_info.alignment = cgv::render::TextAlignment::TA_BOTTOM;
380 font_renderer.render(ctx, content_canvas, font, value_label, value_label_rectangle.position, text_render_info, value_label_style);
381 }
382
383 // draw cursor decorators to show interaction hints
384 if(mouse_is_on_overlay && !cursor_label.empty()) {
385 vec2 position = static_cast<vec2>(cursor_position + ivec2(12, 6));
386 text_render_info.alignment = cgv::render::TextAlignment::TA_NONE;
387 font_renderer.render(ctx, content_canvas, font, cursor_label, vec3(position, 0.0f), text_render_info, cursor_label_style);
388 }
389
390 end_content(ctx);
391}
392
399
401
402 if(begin_tree_node("Settings", layout, false)) {
403 align("\a");
404 std::string height_options = "min=";
405 height_options += supports_opacity ? "80" : "40";
406 height_options += ";max=500;step=10;ticks=true";
407 add_member_control(this, "Height", layout.total_height, "value_slider", height_options);
408 add_member_control(this, "Opacity Scale Exponent", opacity_scale_exponent, "value_slider", "min=1.0;max=5.0;step=0.001;ticks=true");
409 add_member_control(this, "Resolution", resolution, "dropdown", "enums='2=2,4=4,8=8,16=16,32=32,64=64,128=128,256=256,512=512,1024=1024,2048=2048';w=106", " ");
410 add_member_control(this, "Interpolate", use_interpolation, "check", "w=82");
411
412 if(cmc.cm && cmc.cm->has_texture_support()) {
413 add_member_control(this, "Linear Filtering (Texture)", use_linear_filtering, "check");
414 }
415 align("\b");
416 end_tree_node(layout);
417
418 add_decorator("Histogram", "heading", "level=4");
419 add_member_control(this, "Type", histogram_type, "dropdown", "enums='None,Nearest,Linear,Smooth'");
420 add_member_control(this, "Ignore Zero for Normalization", hist_norm_ignore_zero, "check");
421 add_member_control(this, "Gamma", hist_norm_gamma, "value_slider", "min=0.001;max=2;step=0.001;ticks=true");
422 add_member_control(this, "Fill Color", hist_style.fill_color);
423 add_member_control(this, "Border Color", hist_style.border_color);
424 add_member_control(this, "Border Width", hist_style.border_width, "value_slider", "min=0;max=10;step=0.5;ticks=true");
425 }
426
427 if(begin_tree_node("Color Points", cmc.color_points, true)) {
428 align("\a");
429 auto& points = cmc.color_points;
430 for(unsigned i = 0; i < points.size(); ++i) {
431 std::string label_prefix = "";
432 std::string options = "w=48";
433 if(&points[i] == cmc.color_points.get_selected()) {
434 label_prefix = "> ";
435 options += ";label_color=" + highlight_color_hex;
436 }
437
438 add_view(label_prefix + std::to_string(i), points[i].val, "", options, " ");
439 add_member_control(this, "", points[i].col, "", "w=140");
440 }
441 align("\b");
442 end_tree_node(cmc.color_points);
443 }
444
445 if(supports_opacity) {
446 if(begin_tree_node("Opacity Points", cmc.opacity_points, true)) {
447 align("\a");
448 auto& points = cmc.opacity_points;
449 for(unsigned i = 0; i < points.size(); ++i) {
450 std::string label_prefix = "";
451 std::string options = "w=48";
452 if(&points[i] == cmc.opacity_points.get_selected()) {
453 label_prefix = "> ";
454 options += ";label_color=" + highlight_color_hex;
455 }
456
457 add_view(label_prefix + std::to_string(i), points[i].val[0], "", options, " ");
458 add_member_control(this, "", points[i].val[1], "value", "w=140");
459 }
460 align("\b");
461 end_tree_node(cmc.opacity_points);
462 }
463 }
464}
465
466void color_map_editor::set_opacity_support(bool flag) {
467
468 supports_opacity = flag;
469 set_color_map(cmc.cm);
470 on_set(&supports_opacity);
471}
472
473void color_map_editor::set_color_map(cgv::render::color_map* cm) {
474 cmc.reset();
475 cmc.cm = cm;
476
477 if(cmc.cm) {
478 auto& cm = *cmc.cm;
479 auto& cp = cmc.cm->ref_color_points();
480
481 for(size_t i = 0; i < cp.size(); ++i) {
482 color_point p;
483 p.val = cgv::math::clamp(cp[i].first, 0.0f, 1.0f);
484 p.col = cp[i].second;
485 cmc.color_points.add(p);
486 }
487
488 if(supports_opacity) {
489 auto& op = cmc.cm->ref_opacity_points();
490 for(size_t i = 0; i < op.size(); ++i) {
491 opacity_point p;
492 p.val.x() = cgv::math::clamp(op[i].first, 0.0f, 1.0f);
493 p.val.y() = cgv::math::clamp(op[i].second, 0.0f, 1.0f);
494 cmc.opacity_points.add(p);
495 }
496 }
497
498 use_interpolation = cm.is_interpolation_enabled();
499 use_linear_filtering = true;
500
501 cgv::render::gl_color_map* gl_cm = cmc.get_gl_color_map();
502 if(gl_cm)
503 use_linear_filtering = gl_cm->is_linear_filtering_enabled();
504
505 update_point_positions();
506 update_color_map(false);
507
509 }
510}
511
512void color_map_editor::set_histogram_data(const std::vector<unsigned> data) {
513 histogram = data;
514
515 std::vector<float> float_data(histogram.size(), 0.0f);
516 hist_max = 1;
517 hist_max_non_zero = 1;
518 for(size_t i = 0; i < histogram.size(); ++i) {
519 unsigned count = histogram[i];
520 hist_max = std::max(hist_max, count);
521 if(i > 0)
522 hist_max_non_zero = std::max(hist_max_non_zero, count);
523 float_data[i] = static_cast<float>(count);
524 }
525
526 if(auto ctx_ptr = get_context()) {
527 auto& ctx = *ctx_ptr;
528 hist_tex.destruct(ctx);
529
530 cgv::data::data_view dv = cgv::data::data_view(new cgv::data::data_format(unsigned(histogram.size()), cgv::type::info::TI_FLT32, cgv::data::CF_R), float_data.data());
531 hist_tex = cgv::render::texture("flt32[R]");
532 hist_tex.create(ctx, dv, 0);
533
534 post_damage();
535 }
536}
537
538void color_map_editor::set_selected_color(rgb color) {
539
540 auto selected_point = cmc.color_points.get_selected();
541 if(selected_point) {
542 selected_point->col = color;
543 update_color_map(true);
544 update_member(&selected_point->col);
545 post_damage();
546 }
547}
548
549void color_map_editor::init_styles() {
550 auto& theme = cgv::gui::theme_info::instance();
551 handle_color = rgba(theme.text(), 1.0f);
552 highlight_color = rgba(theme.highlight(), 1.0f);
553 highlight_color_hex = theme.highlight_hex();
554
555 // configure style for the border rectangles
556 border_style.fill_color = theme.border();
557 border_style.border_width = 0.0f;
558 border_style.feather_width = 0.0f;
559
560 // configure style for the color scale rectangle
561 color_map_style = border_style;
562 color_map_style.use_texture = true;
563
564 // configure style for background
565 bg_style.use_texture = true;
566 bg_style.feather_width = 0.0f;
567
568 // configure style for histogram
569 hist_style.use_blending = true;
570 hist_style.feather_width = 1.0f;
571 hist_style.feather_origin = 0.0f;
572 hist_style.fill_color = rgba(rgb(0.5f), 0.666f);
573 hist_style.border_color = rgba(rgb(0.0f), 0.666f);
574 hist_style.border_width = 1.0f;
575
576 // configure style for color handles
577 color_handle_style.use_blending = true;
578 color_handle_style.use_fill_color = false;
579 color_handle_style.position_is_center = true;
580 color_handle_style.border_color = theme.border();
581 color_handle_style.border_width = 1.5f;
582 color_handle_style.border_radius = 2.0f;
583 color_handle_style.stem_width = color_point::default_width;
584 color_handle_style.head_width = color_point::default_width;
585
586 label_box_style.position_is_center = true;
587 label_box_style.use_blending = true;
588 label_box_style.fill_color = handle_color;
589 label_box_style.border_color = theme.border();
590 label_box_style.border_width = 1.5f;
591 label_box_style.border_radius = 4.0f;
592
593 // configure style for opacity handles
594 opacity_handle_style.use_blending = true;
595 opacity_handle_style.use_fill_color = false;
596 opacity_handle_style.position_is_center = true;
597 opacity_handle_style.border_color = theme.border();
598 opacity_handle_style.border_width = 1.5f;
599
600 // configure style for the lines and polygon
601 line_style.use_blending = true;
602 line_style.use_fill_color = false;
603 line_style.use_texture = true;
604 line_style.use_texture_alpha = false;
605 line_style.width = 3.0f;
606
607 polygon_style = static_cast<cgv::g2d::shape2d_style>(line_style);
608 polygon_style.use_texture_alpha = true;
609
610 // label style
611 cursor_label_style.fill_color = rgb(0.0f);
612 cursor_label_style.font_size = 16.0f;
613
614 value_label_style.fill_color = theme.group();
615 value_label_style.font_size = 12.0f;
616}
617
618void color_map_editor::setup_preview_texture(cgv::render::context& ctx) {
619
620 if(preview_tex.is_created())
621 preview_tex.destruct(ctx);
622
623 cgv::render::TextureFilter filter = use_linear_filtering ? cgv::render::TF_LINEAR : cgv::render::TF_NEAREST;
624 preview_tex = cgv::render::texture("uint8[R,G,B,A]", filter, filter);
625}
626
627void color_map_editor::init_preview_texture(cgv::render::context& ctx) {
628
629 std::vector<uint8_t> data(resolution * 4 * 2, 0u);
630
631 setup_preview_texture(ctx);
633 preview_tex.create(ctx, tf_dv, 0);
634}
635
636void color_map_editor::add_point(const vec2& pos) {
637
638 if(cmc.cm) {
639 ivec2 test_pos = static_cast<ivec2>(pos);
640
641 if(layout.color_editor_rect.contains(test_pos)) {
642 // color point
643 color_point p;
644 p.position = ivec2(int(pos.x()), layout.color_handles_rect.y());
645 p.update_val(layout);
646 p.col = cmc.cm->interpolate_color(p.val);
647 cmc.color_points.add(p);
648 } else if(supports_opacity && layout.opacity_editor_rect.contains(test_pos)) {
649 // opacity point
650 opacity_point p;
651 p.position = pos;
652 p.update_val(layout, opacity_scale_exponent);
653 cmc.opacity_points.add(p);
654 }
655
656 update_color_map(true);
657 }
658}
659
660void color_map_editor::remove_point(const cgv::g2d::draggable* ptr) {
661
662 int color_point_idx = -1;
663 int opacity_point_idx = -1;
664
665 for(unsigned i = 0; i < cmc.color_points.size(); ++i) {
666 if(&cmc.color_points[i] == ptr) {
667 color_point_idx = i;
668 break;
669 }
670 }
671
672 for(unsigned i = 0; i < cmc.opacity_points.size(); ++i) {
673 if(&cmc.opacity_points[i] == ptr) {
674 opacity_point_idx = i;
675 break;
676 }
677 }
678
679 bool removed = false;
680
681 if(color_point_idx > -1) {
682 if(cmc.color_points.size() > 1) {
683 cmc.color_points.ref_draggables().erase(cmc.color_points.ref_draggables().begin() + color_point_idx);
684 removed = true;
685 }
686 }
687
688 if(opacity_point_idx > -1) {
689 if(cmc.opacity_points.size() > 1) {
690 cmc.opacity_points.ref_draggables().erase(cmc.opacity_points.ref_draggables().begin() + opacity_point_idx);
691 removed = true;
692 }
693 }
694
695 if(removed)
696 update_color_map(true);
697}
698
699cgv::g2d::draggable* color_map_editor::get_hit_point(const vec2& pos) {
700
701 cgv::g2d::draggable* hit = nullptr;
702
703 for(unsigned i = 0; i < cmc.color_points.size(); ++i) {
704 color_point& p = cmc.color_points[i];
705 if(p.contains(pos))
706 hit = &p;
707 }
708
709 for(unsigned i = 0; i < cmc.opacity_points.size(); ++i) {
710 opacity_point& p = cmc.opacity_points[i];
711 if(p.contains(pos))
712 hit = &p;
713 }
714
715 return hit;
716}
717
718void color_map_editor::update_value_label_rectangle(vec2 position, const cgv::g2d::rect& parent_rectangle) {
719
720 value_label_rectangle.position = position;
721
722 if(auto ctx = get_context()) {
723 auto& font = cgv::g2d::ref_msdf_font_regular(*get_context());
724 value_label_rectangle.size = font.compute_render_size(value_label, value_label_style.font_size);
725 }
726
727 float padding = std::ceil(0.5f * value_label_rectangle.w()) + 4.0f;
728 float min_x = layout.color_editor_rect.x() + padding;
729 float max_x = layout.color_editor_rect.x1() - padding;
730 value_label_rectangle.position.x() = cgv::math::clamp(value_label_rectangle.position.x(), min_x, max_x);
731}
732
733void color_map_editor::handle_color_drag(cgv::g2d::DragAction action) {
734
735 switch(action) {
736 case cgv::g2d::DragAction::kDrag:
737 {
738 color_point* dragged_point = cmc.color_points.get_dragged();
739 if(dragged_point) {
740 dragged_point->update_val(layout);
741 update_color_map(true);
742
743 show_value_label = true;
744 value_label = value_to_string(dragged_point->val);
745 update_value_label_rectangle(dragged_point->position, layout.color_editor_rect);
746 value_label_rectangle.position.y() += 25.0f;
747
748 if(dragged_point && on_color_point_select_callback)
749 on_color_point_select_callback(dragged_point->col);
750
751 post_damage();
752 }
753 break;
754 }
755 case cgv::g2d::DragAction::kDragEnd:
756 case cgv::g2d::DragAction::kSelect:
757 handle_drag_end();
758 break;
759 default:
760 break;
761 }
762}
763
764void color_map_editor::handle_opacity_drag(cgv::g2d::DragAction action) {
765
766 switch(action) {
767 case cgv::g2d::DragAction::kDrag:
768 {
769 opacity_point* dragged_point = cmc.opacity_points.get_dragged();
770 if(dragged_point) {
771 dragged_point->update_val(layout, opacity_scale_exponent);
772 update_color_map(true);
773
774 show_value_label = true;
775 std::string x_label = value_to_string(dragged_point->val.x());
776 std::string y_label = cgv::utils::to_string(dragged_point->val.y(), -1, 3u);
777 value_label = x_label + ", " + y_label;
778
779 update_value_label_rectangle(dragged_point->position, layout.opacity_editor_rect);
780 value_label_rectangle.position.y() = std::min(value_label_rectangle.position.y() + 10.0f, static_cast<float>(layout.opacity_editor_rect.y1() - 6));
781
782 post_damage();
783 }
784 break;
785 }
786 case cgv::g2d::DragAction::kDragEnd:
787 case cgv::g2d::DragAction::kSelect:
788 handle_drag_end();
789 break;
790 default:
791 break;
792 }
793}
794
795/*
796void color_map_editor::handle_color_point_drag() {
797
798 auto dragged_point = cmc.color_points.get_dragged();
799 dragged_point->update_val(layout);
800 update_color_map(true);
801
802 show_value_label = true;
803 value_label = value_to_string(dragged_point->val);
804 update_value_label_rectangle(dragged_point->position, layout.color_editor_rect);
805 value_label_rectangle.position.y() += 25.0f;
806
807 if(dragged_point && on_color_point_select_callback)
808 on_color_point_select_callback(dragged_point->col);
809
810 post_damage();
811}
812
813void color_map_editor::handle_opacity_point_drag() {
814
815 auto dragged_point = cmc.opacity_points.get_dragged();
816 dragged_point->update_val(layout, opacity_scale_exponent);
817 update_color_map(true);
818
819 show_value_label = true;
820 std::string x_label = value_to_string(dragged_point->val.x());
821 std::string y_label = cgv::utils::to_string(dragged_point->val.y(), -1, 3u);
822 value_label = x_label + ", " + y_label;
823
824 update_value_label_rectangle(dragged_point->position, layout.opacity_editor_rect);
825 value_label_rectangle.position.y() = std::min(value_label_rectangle.position.y() + 10.0f, static_cast<float>(layout.opacity_editor_rect.y1() - 6));
826
827 post_damage();
828}
829*/
830
831void color_map_editor::handle_drag_end() {
832
833 show_value_label = false;
834 update_geometry();
835
836 auto selected_point = cmc.color_points.get_selected();
837 if(selected_point) {
838 if(on_color_point_select_callback)
839 on_color_point_select_callback(selected_point->col);
840 } else {
841 if(on_color_point_deselect_callback)
842 on_color_point_deselect_callback();
843 }
844
846 post_damage();
847}
848
849std::string color_map_editor::value_to_string(float value) {
850
851 float display_value = cgv::math::lerp(range.x(), range.y(), value);
852
853 float total = range.y() - range.x();
854 if(total < 100.0f) {
855 // show as float
856 return cgv::utils::to_string(display_value, -1, 3u);
857 } else {
858 // show as int
859 int display_value_int = static_cast<int>(round(display_value));
860 return std::to_string(display_value_int);
861 }
862}
863
864void color_map_editor::sort_points() {
865 sort_color_points();
866 if(supports_opacity)
867 sort_opacity_points();
868}
869
870void color_map_editor::sort_color_points() {
871
872 auto& points = cmc.color_points;
873
874 if(points.size() > 1) {
875 int dragged_point_idx = -1;
876 int selected_point_idx = -1;
877
878 const color_point* dragged_point = points.get_dragged();
879 const color_point* selected_point = points.get_selected();
880
881 std::vector<std::pair<color_point, int>> sorted(points.size());
882
883 for(unsigned i = 0; i < points.size(); ++i) {
884 sorted[i].first = points[i];
885 sorted[i].second = i;
886
887 if(dragged_point == &points[i])
888 dragged_point_idx = i;
889 if(selected_point == &points[i])
890 selected_point_idx = i;
891 }
892
893 std::sort(sorted.begin(), sorted.end(),
894 [](const auto& a, const auto& b) -> bool {
895 return a.first.val < b.first.val;
896 }
897 );
898
899 int new_dragged_point_idx = -1;
900 int new_selected_point_idx = -1;
901
902 for(unsigned i = 0; i < sorted.size(); ++i) {
903 points[i] = sorted[i].first;
904 if(dragged_point_idx == sorted[i].second) {
905 new_dragged_point_idx = i;
906 }
907 if(selected_point_idx == sorted[i].second) {
908 new_selected_point_idx = i;
909 }
910 }
911
912 points.set_dragged(new_dragged_point_idx);
913 points.set_selected(new_selected_point_idx);
914 }
915}
916
917void color_map_editor::sort_opacity_points() {
918
919 auto& points = cmc.opacity_points;
920
921 if(points.size() > 1) {
922 int dragged_point_idx = -1;
923 int selected_point_idx = -1;
924
925 const opacity_point* dragged_point = points.get_dragged();
926 const opacity_point* selected_point = points.get_selected();
927
928 std::vector<std::pair<opacity_point, int>> sorted(points.size());
929
930 for(unsigned i = 0; i < points.size(); ++i) {
931 sorted[i].first = points[i];
932 sorted[i].second = i;
933
934 if(dragged_point == &points[i])
935 dragged_point_idx = i;
936 if(selected_point == &points[i])
937 selected_point_idx = i;
938 }
939
940 std::sort(sorted.begin(), sorted.end(),
941 [](const auto& a, const auto& b) -> bool {
942 return a.first.val.x() < b.first.val.x();
943 }
944 );
945
946 int new_dragged_point_idx = -1;
947 int new_selected_point_idx = -1;
948
949 for(unsigned i = 0; i < sorted.size(); ++i) {
950 points[i] = sorted[i].first;
951 if(dragged_point_idx == sorted[i].second) {
952 new_dragged_point_idx = i;
953 }
954 if(selected_point_idx == sorted[i].second) {
955 new_selected_point_idx = i;
956 }
957 }
958
959 points.set_dragged(new_dragged_point_idx);
960 points.set_selected(new_selected_point_idx);
961 }
962}
963
964void color_map_editor::update_point_positions() {
965
966 for(unsigned i = 0; i < cmc.color_points.size(); ++i)
967 cmc.color_points[i].update_pos(layout);
968
969 for(unsigned i = 0; i < cmc.opacity_points.size(); ++i)
970 cmc.opacity_points[i].update_pos(layout, opacity_scale_exponent);
971}
972
973void color_map_editor::update_color_map(bool is_data_change) {
974
976 if(!ctx_ptr || !cmc.cm) return;
977 cgv::render::context& ctx = *ctx_ptr;
978
979 auto& cm = *cmc.cm;
980 auto& color_points = cmc.color_points;
981 auto& opacity_points = cmc.opacity_points;
982
983 sort_points();
984
985 cm.clear();
986
987 for(unsigned i = 0; i < color_points.size(); ++i) {
988 const color_point& p = color_points[i];
989 cm.add_color_point(p.val, p.col);
990 }
991
992 if(supports_opacity) {
993 for(unsigned i = 0; i < opacity_points.size(); ++i) {
994 const opacity_point& p = opacity_points[i];
995 cm.add_opacity_point(p.val.x(), p.val.y());
996 }
997 } else {
998 // add one fully opaque point that will be removed later on
999 cm.add_opacity_point(0.0f, 1.0f);
1000 }
1001
1002 size_t size = static_cast<size_t>(resolution);
1003 std::vector<rgba> cs_data = cm.interpolate(size);
1004
1005 std::vector<uint8_t> data(4 * 2 * size);
1006 for(size_t i = 0; i < size; ++i) {
1007 rgba col = cs_data[i];
1008 uint8_t r = static_cast<uint8_t>(255.0f * col.R());
1009 uint8_t g = static_cast<uint8_t>(255.0f * col.G());
1010 uint8_t b = static_cast<uint8_t>(255.0f * col.B());
1011 uint8_t a = static_cast<uint8_t>(255.0f * col.alpha());
1012
1013 size_t idx = 4 * i;
1014 data[idx + 0] = r;
1015 data[idx + 1] = g;
1016 data[idx + 2] = b;
1017 data[idx + 3] = a;
1018 idx += 4*size;
1019 data[idx + 0] = r;
1020 data[idx + 1] = g;
1021 data[idx + 2] = b;
1022 data[idx + 3] = a;
1023 }
1024
1025 setup_preview_texture(ctx);
1027 preview_tex.create(ctx, dv, 0);
1028
1029 if(!supports_opacity)
1030 cm.clear_opacity_points();
1031
1032 update_geometry();
1033
1034 if(on_change_callback)
1035 on_change_callback();
1036
1037 post_damage();
1038}
1039
1040bool color_map_editor::update_geometry() {
1041
1042 cgv::render::context* ctx_ptr = get_context();
1043 if(!ctx_ptr || !cmc.cm) return false;
1044 cgv::render::context& ctx = *ctx_ptr;
1045
1046 auto& cm = *cmc.cm;
1047 auto& color_points = cmc.color_points;
1048 auto& opacity_points = cmc.opacity_points;
1049 auto& color_handles = cmc.color_handles;
1050 auto& opacity_handles = cmc.opacity_handles;
1051 auto& lines = cmc.lines;
1052 auto& triangles = cmc.triangles;
1053
1054 color_handles.clear();
1055 opacity_handles.clear();
1056 lines.clear();
1057 triangles.clear();
1058
1059 bool success = color_points.size() > 0 && opacity_points.size() > 0;
1060
1061 // create color handles
1062 vec2 pos_offset = vec2(0.0f, 0.5f * color_point::default_height);
1063
1064 for(unsigned i = 0; i < color_points.size(); ++i) {
1065 const auto& p = color_points[i];
1066 vec2 pos = p.center();
1067 rgba col = color_points.get_selected() == &p ? highlight_color : handle_color;
1068 color_handles.add(pos - pos_offset, col);
1069 color_handles.add(pos + pos_offset, col);
1070 }
1071
1072 // create opacity handles
1073 for(unsigned i = 0; i < opacity_points.size(); ++i) {
1074 const auto& p = opacity_points[i];
1075 vec2 pos = p.center();
1076 rgba col = opacity_points.get_selected() == &p ? highlight_color : handle_color;
1077 opacity_handles.add(pos, col);
1078 }
1079
1080 if(opacity_points.size() > 0) {
1081 const auto& pl = opacity_points[0];
1082
1083 vec2 tex_coord(0.0f, 0.5f);
1084
1085 lines.add(vec2(float(layout.opacity_editor_rect.x()), pl.center().y()), tex_coord);
1086
1087 triangles.add(vec2(float(layout.opacity_editor_rect.x()), pl.center().y()), tex_coord);
1088 triangles.add(layout.opacity_editor_rect.position, tex_coord);
1089
1090 for(unsigned i = 0; i < opacity_points.size(); ++i) {
1091 const auto& p = opacity_points[i];
1092 vec2 pos = p.center();
1093
1094 tex_coord.x() = p.val.x();
1095
1096 lines.add(pos, tex_coord);
1097
1098 triangles.add(pos, tex_coord);
1099 triangles.add(vec2(pos.x(), (float)layout.opacity_editor_rect.y()), tex_coord);
1100 }
1101
1102 const auto& pr = opacity_points[opacity_points.size() - 1];
1103 vec2 max_pos = layout.opacity_editor_rect.position + vec2(1.0f, 0.0f) * layout.opacity_editor_rect.size;
1104
1105 tex_coord.x() = 1.0f;
1106
1107 lines.add(vec2(max_pos.x(), pr.center().y()), tex_coord);
1108
1109 triangles.add(vec2(max_pos.x(), pr.center().y()), tex_coord);
1110 triangles.add(max_pos, tex_coord);
1111 } else {
1112 success = false;
1113 }
1114
1115 return success;
1116}
1117
1118}
1119}
More advanced text processing for splitting text into lines or tokens.
bool is_hit(const ivec2 &mouse_pos) const override
Test if the mouse pointer is hovering over this overlay and returns true if this is the case.
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...
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
bool handle_key_event(cgv::gui::key_event &e) override
overload this method to handle key events
bool handle_mouse_event(cgv::gui::mouse_event &e, cgv::ivec2 local_mouse_pos) override
overload this method to handle mouse events; local_mouse_pos is the mouse position in the local coord...
void handle_theme_change(const cgv::gui::theme_info &theme) override
override in your class to handle theme changes
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
void handle_member_change(const cgv::utils::pointer_test &m) override
implement to handle member changes
void create_gui_impl() override
virtual method to implement the derived class gui creation
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
bool blocks_events() const
return whether this overlay blocks events, i.e. does not pass them to the next event handler
Definition overlay.h:127
ivec2 get_viewport_size() const
return the current viewport size
Definition overlay.h:133
void set_size(const ivec2 &size)
set the default size of the overlay before stretch gets applied
Definition overlay.cxx:108
virtual void handle_theme_change(const cgv::gui::theme_info &theme) override
override in your class to handle theme changes
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
unsigned char get_modifiers() const
return the active modifiers as values from EventModifier combined with a logical or-operation
Definition event.cxx:237
class to represent all possible keyboard events with the EID_KEY
Definition key_event.h:23
unsigned short get_key() const
return the key being a capital letter, digit or a value from the Keys enum
Definition key_event.cxx:51
KeyAction get_action() const
return the key event action
Definition key_event.cxx:71
class to represent all possible mouse events with the EID_MOUSE
Definition mouse_event.h:33
MouseAction get_action() const
return the mouse action
unsigned char get_button_state() const
return the button state as values from MouseButton combined with a logical or-operation
cgv::base::base_ptr add_decorator(const std::string &label, const std::string &decorator_type, const std::string &options="", const std::string &align="\n")
add a newly created decorator to the group
Definition provider.cxx:168
void align(const std::string &_align)
send pure alignment information
Definition provider.cxx:36
bool begin_tree_node(const std::string &label, const T &value, bool initial_visibility=false, const std::string &options="", gui_group_ptr ggp=gui_group_ptr())
Begin a sub tree of a tree structured gui.
Definition provider.h:212
virtual void update_member(void *member_ptr)
call this to update all views and controls of a member
Definition provider.cxx:72
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
void end_tree_node(const T &value)
template specialization that allows to specify value reference plus node_instance by using the result...
Definition provider.h:222
virtual void post_recreate_gui()
delayed recreation of gui
Definition provider.cxx:509
data::ref_ptr< view< T > > add_view(const std::string &label, const T &value, const std::string &gui_type="", const std::string &options="", const std::string &align="\n")
use this to add a new view to the gui with a given value type, gui type and init options
Definition provider.h:112
T & y()
second element
Definition fvec.h:159
T & x()
first element
Definition fvec.h:155
static cgv::type::uint32_type size()
return number of elements
Definition fvec.h:179
base class for all drawables, which is independent of the used rendering API.
Definition context.h:621
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:2046
the texture class encapsulates all functionality independent of the rendering api.
Definition texture.h:15
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 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
@ CF_R
undefinded format with no component
@ CF_RGB
color format with two components R and G
@ KA_PRESS
key press action
Definition key_event.h:14
@ KA_RELEASE
key release action
Definition key_event.h:13
@ MB_LEFT_BUTTON
left button
Definition mouse_event.h:26
@ EM_ALT
alt modifier
Definition event.h:43
@ EM_CTRL
ctrl modifier
Definition event.h:44
@ KEY_Left_Ctrl
left ctrl key
Definition shortcut.h:37
@ KEY_Left_Alt
left alt key
Definition shortcut.h:35
@ MA_PRESS
mouse button pressed
Definition mouse_event.h:13
@ MA_LEAVE
mouse leave window action
Definition mouse_event.h:19
@ MA_MOVE
mouse pointer moved
Definition mouse_event.h:16
@ MA_ENTER
mouse enter window action
Definition mouse_event.h:18
@ MA_DRAG
mouse drag action
Definition mouse_event.h:17
@ TA_BOTTOM
center of bottom edge of text bounds
Definition context.h:278
@ TA_NONE
no alignment
Definition context.h:274
TextureFilter
different texture filter
Definition context.h:189
@ TI_FLT32
floating point type stored in 16 bits
Definition type_id.h:28
@ TI_UINT8
signed integer stored in 64 bits
Definition type_id.h:23
DummyEnum
some enum to mark an integral parameter to be of enum type
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
the cgv namespace
Definition print.h:11
cgv::media::color< float, cgv::media::RGB, cgv::media::OPACITY > rgba
declare rgba color type with 32 bit components
Definition color.h:855
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