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