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