cgv
Loading...
Searching...
No Matches
color_selector.cxx
1#include "color_selector.h"
2
3#include <cgv/gui/mouse_event.h>
4#include <cgv/gui/theme_info.h>
5#include <cgv/math/interpolate.h>
6#include <cgv_g2d/msdf_gl_font_renderer.h>
7#include <cgv_gl/gl/gl.h>
8
9namespace cgv {
10namespace overlay {
11
12color_selector::color_selector() {
13
14 set_name("Color Selector");
15 blocks_events(true);
16
17 layout.padding = padding();
18
19 set_size(ivec2(layout.size));
20
21 selector_handles.callback = std::bind(&color_selector::handle_selector_drag, this, std::placeholders::_1);
22 selector_handles.use_individual_constraints = true;
23}
24
26
27 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, -1);
28
30
31 color_tex.destruct(ctx);
32 hue_tex.destruct(ctx);
33 text_geometry.destruct(ctx);
34}
35
37
38 if (e.get_button_state() & cgv::gui::MB_LEFT_BUTTON) {
39 if (e.get_action() == cgv::gui::MA_PRESS) {
40 int hit_index = -1;
41 cgv::g2d::irect hit_rect;
42
43 if(layout.color_rect.contains(local_mouse_pos)) {
44 hit_index = 0;
45 hit_rect = layout.color_rect;
46 }
47
48 if(layout.hue_rect.contains(local_mouse_pos)) {
49 hit_index = 1;
50 hit_rect = layout.hue_rect;
51 }
52
53 if(layout.opacity_rect.contains(local_mouse_pos)) {
54 hit_index = 2;
55 hit_rect = layout.opacity_rect;
56 }
57
58 if(hit_index > -1 && hit_index < 4) {
59 vec2 hit_rect_realtive_pos = static_cast<vec2>(local_mouse_pos - hit_rect.position);
60 vec2 val = hit_rect_realtive_pos / static_cast<vec2>(hit_rect.size);
61 if(hit_index > 0)
62 val.x() = 0.0f;
63 selector_handles[hit_index].val = val;
64 selector_handles[hit_index].update_pos();
65
66 update_color();
67
68 if(hit_index == 1 && get_context())
69 create_color_texture(*get_context());
70
71 post_damage();
72 }
73 }
74 }
75
76 if(selector_handles.handle(e, get_viewport_size(), get_rectangle()))
77 return true;
78
79 return false;
80}
81
83
84 if(ptr.points_to(rgb_color))
85 set_rgb_color(rgb_color);
86
87 if(ptr.points_to(rgba_color))
88 set_rgba_color(rgba_color);
89
90 if(ptr.points_to(layout.size))
91 set_size(ivec2(layout.size));
92}
93
95
96 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, 1);
97
98 register_shader("rectangle", cgv::g2d::shaders::rectangle);
99 register_shader("circle", cgv::g2d::shaders::circle);
100 register_shader("grid", cgv::g2d::shaders::grid);
101
102 bool success = canvas_overlay::init(ctx);
103
104 success &= text_geometry.init(ctx);
105
106 text_geometry.texts = { "R", "0", "G", "0", "B", "0", "A", "0" };
107 if(success) {
108 text_geometry.create(ctx);
109
110 for(size_t i = 0; i < 4; ++i) {
111 text_geometry.alignments.push_back(cgv::render::TA_LEFT);
112 text_geometry.alignments.push_back(cgv::render::TA_RIGHT);
113 }
114
115 text_geometry.positions = std::vector<vec3>(8, { 0.0f });
116 }
117
118 // saturation and value handle
120 sh.size = vec2(16.0f);
121 selector_handles.add(sh);
122
123 // hue handle
124 sh.is_rectangular = true;
125 sh.size = vec2(20.0f, 10.0f);
126 sh.position_is_center = false;
127 sh.constraint_reference = cgv::g2d::ConstraintReference::kMinPoint;
128 selector_handles.add(sh);
129
130 // opacity handle
131 selector_handles.add(sh);
132
133 // set constraints
134 selector_handles[0].constraint = &layout.color_rect;
135 selector_handles[1].constraint = &layout.hue_constraint;
136 selector_handles[2].constraint = &layout.opacity_constraint;
137
138 create_hue_texture(ctx);
139 create_color_texture(ctx);
140
141 if(has_opacity)
142 set_color(rgba(0.0f, 0.0f, 0.0f, 1.0f), true, true);
143 else
144 set_color(rgb(0.0f), false, true);
145
146 return success;
147}
148
150
151 if(ensure_layout(ctx)) {
153
154 for(size_t i = 0; i < 3; ++i)
155 selector_handles[i].update_pos();
156
157 int w = layout.opacity_rect.w();
158 int h = layout.opacity_rect.h();
159 opacity_bg_style.texcoord_scaling = vec2(1.0f, static_cast<float>(h) / static_cast<float>(w));
160
161 ivec2 text_position = ivec2(layout.preview_rect.b().x() + 10, layout.preview_rect.center().y());
162 for(size_t i = 0; i < text_geometry.texts.size(); ++i) {
163 text_geometry.positions[i] = vec3(text_position, 0.0f);
164 text_position.x() += i & 1 ? 15 : 40;
165 }
166 }
167}
168
170
171 begin_content(ctx);
172
173 // draw inner border
174 content_canvas.enable_shader(ctx, "rectangle");
175 content_canvas.set_style(ctx, border_style);
176
177 auto& theme = cgv::gui::theme_info::instance();
178 rgba border_color = rgba(theme.border(), 1.0f);
179 rgba text_background_color = rgba(theme.text_background(), 1.0f);
180 content_canvas.draw_shape(ctx, layout.border_rect, border_color);
181 content_canvas.draw_shape(ctx, layout.preview_rect, rgb_color);
182
183 cgv::g2d::irect text_bg = layout.preview_rect;
184 text_bg.size.x() = 48;
185 int n_labels = has_opacity ? 4 : 3;
186 for(size_t i = 0; i < n_labels; ++i) {
187 text_bg.position.x() = static_cast<int>(text_geometry.positions[2 * i].x() - 4.0f);
188 content_canvas.draw_shape(ctx, text_bg, text_background_color);
189 }
190
191 content_canvas.set_style(ctx, color_texture_style);
192 color_tex.enable(ctx, 0);
193 content_canvas.draw_shape(ctx, layout.color_rect);
194 color_tex.disable(ctx);
195
196 content_canvas.set_style(ctx, hue_texture_style);
197 hue_tex.enable(ctx, 0);
198 content_canvas.draw_shape(ctx, layout.hue_rect);
199 hue_tex.disable(ctx);
200
201 if(has_opacity) {
202 content_canvas.enable_shader(ctx, "grid");
203 content_canvas.set_style(ctx, opacity_bg_style);
204 content_canvas.draw_shape(ctx, layout.opacity_rect);
205 }
206
207 glEnable(GL_SCISSOR_TEST);
208 glScissor(layout.color_rect.x(), layout.color_rect.y(), layout.color_rect.w(), layout.color_rect.h());
209
210 auto& sh = selector_handles;
211 content_canvas.enable_shader(ctx, "circle");
212 content_canvas.set_style(ctx, color_handle_style);
213 glScissor(layout.color_rect.x(), layout.color_rect.y(), layout.color_rect.w(), layout.color_rect.h());
214 content_canvas.draw_shape(ctx, sh[0].position + 0.5f, sh[0].size);
215
216 content_canvas.enable_shader(ctx, "rectangle");
217 content_canvas.set_style(ctx, hue_handle_style);
218 glScissor(layout.hue_rect.x(), layout.hue_rect.y(), layout.hue_rect.w(), layout.hue_rect.h());
219 content_canvas.draw_shape(ctx, sh[1]);
220
221 if(has_opacity) {
222 glScissor(layout.opacity_rect.x(), layout.opacity_rect.y(), layout.opacity_rect.w(), layout.opacity_rect.h());
223
224 const auto& r = layout.opacity_rect;
225 content_canvas.enable_shader(ctx, "rectangle");
226 opacity_color_style.fill_color = rgba(rgb_color, 1.0f);
227 opacity_color_style.feather_width = static_cast<float>(r.h());
228 content_canvas.set_style(ctx, opacity_color_style);
229 content_canvas.draw_shape(ctx, ivec2(r.x(), r.y1() - 1), ivec2(r.w(), 1));
230
231 content_canvas.set_style(ctx, hue_handle_style);
232 content_canvas.draw_shape(ctx, sh[2]);
233 }
234
235 content_canvas.disable_current_shader(ctx);
236
237 glDisable(GL_SCISSOR_TEST);
238
239 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx).render(ctx, content_canvas, text_geometry, text_style, 0, 2 * n_labels);
240
241 end_content(ctx);
242}
243
245
246 if(begin_tree_node("Settings", layout, false)) {
247 align("\a");
248 add_member_control(this, "Size", layout.size, "value_slider", "min=120;max=1000;step=1;ticks=true");
249 align("\b");
250 end_tree_node(layout);
251 }
252
253 if(has_opacity)
254 add_member_control(this, "Color", rgba_color);
255 else
256 add_member_control(this, "Color", rgb_color);
257}
258
259void color_selector::set_rgb_color(rgb color) {
260 set_color(rgba(color, 1.0f), false);
261}
262
263void color_selector::set_rgba_color(rgba color) {
264 set_color(color, true);
265}
266
267void color_selector::update_layout(const ivec2& parent_size) {
268
269 auto& l = layout;
270 const int slider_width = 20;
271
272 l.border_rect.position = ivec2(l.padding);
273 l.border_rect.size = ivec2(parent_size - 2 * l.padding);
274 l.border_rect.position.y() += 23;
275 l.border_rect.size.y() -= 23;
276
277 cgv::g2d::irect content_rect = l.border_rect;
278 content_rect.translate(1, 1);
279 content_rect.size -= 2;
280
281 int mult = has_opacity ? 2 : 1;
282
283 l.hue_rect.position = ivec2(content_rect.x1() - mult* slider_width - (mult-1), content_rect.y());
284 l.hue_rect.size = ivec2(slider_width, content_rect.h());
285
286 if(has_opacity) {
287 l.opacity_rect = l.hue_rect;
288 l.opacity_rect.translate(slider_width + 1, 0);
289 }
290
291 l.color_rect = content_rect;
292 l.color_rect.w() -= 21 * mult;
293
294 l.preview_rect.position = ivec2(l.padding);
295 l.preview_rect.size = ivec2(20, 20);
296
297 l.hue_constraint = l.hue_rect;
298 l.hue_constraint.translate(0, -5);
299 l.hue_constraint.size.x() = 0;
300
301 l.opacity_constraint = l.opacity_rect;
302 l.opacity_constraint.translate(0, -5);
303 l.opacity_constraint.size.x() = 0;
304
305}
306
307void color_selector::init_styles() {
308 auto& theme = cgv::gui::theme_info::instance();
309
310 // configure style for the border rectangles
311 border_style.border_width = 0.0f;
312 border_style.use_fill_color = false;
313 border_style.feather_width = 0.0f;
314
315 opacity_bg_style.feather_width = 0.0f;
316 opacity_bg_style.fill_color = rgba(rgb(0.75f), 1.0f);
317 opacity_bg_style.border_color = rgba(rgb(0.9f), 1.0f);
318 opacity_bg_style.pattern = cgv::g2d::grid2d_style::GP_CHECKER;
319 opacity_bg_style.scale = 0.5f;
320
321 opacity_color_style.use_blending = true;
322 opacity_color_style.feather_origin = 1.0f;
323
324 // configure style for the color and hue texture rectangles
325 color_texture_style = border_style;
326 color_texture_style.use_texture = true;
327
328 hue_texture_style = color_texture_style;
329 color_texture_style.texcoord_scaling = vec2(0.5f);
330 color_texture_style.texcoord_offset = vec2(0.25f);
331
332 // configure style for color handle
333 color_handle_style.use_blending = true;
334 color_handle_style.use_fill_color = true;
335 color_handle_style.position_is_center = true;
336 color_handle_style.border_color = rgba(rgb(1.0f), 0.75f);
337 color_handle_style.border_width = 1.0f;
338 color_handle_style.fill_color = rgba(rgb(0.0f), 1.0f);
339 color_handle_style.ring_width = 4.0f;
340
341 // configure style for hue handle
342 hue_handle_style = color_handle_style;
343 hue_handle_style.position_is_center = false;
344
345 // configure text style
346 text_style.fill_color = theme.text();
347 text_style.font_size = 14.0f;
348}
349
350bool color_selector::create_hue_texture(cgv::render::context& ctx) {
351
352 const size_t resolution = 256;
353
354 std::vector<rgb8> data;
355 data.reserve(resolution);
356 cgv::math::sequence_transform(std::back_inserter(data), [](float t) {
357 return rgb8(cgv::media::color<float, cgv::media::HLS>(t, 0.5f, 1.0f));
358 }, resolution);
359
361 cgv::data::data_view data_view(&data_format, data.data());
362 return hue_tex.create(ctx, data_view, 0);
363}
364
365bool color_selector::create_color_texture(cgv::render::context& ctx) {
366
367 rgb color = { 0.0f };
368 if(selector_handles.size() > 1)
369 color = cgv::media::color<float, cgv::media::HLS>(selector_handles[1].val.y(), 0.5f, 1.0f);
370
371 std::vector<rgb8> data = {
372 { 0, 0, 0 },
373 { 0, 0, 0 },
374 { 255, 255, 255 },
375 rgb8(color)
376 };
377
379 cgv::data::data_view data_view(&data_format, data.data());
380 return color_tex.create(ctx, data_view, 0);
381}
382
383void color_selector::update_color() {
384
385 const auto& cp = selector_handles[0];
386 const auto& hp = selector_handles[1];
387 const auto& op = selector_handles[2];
388 rgb_color = cgv::media::color<float, cgv::media::HLS>(hp.val.y(), 0.5f, 1.0f);
389
390 float s = cp.val.x(); // saturation
391 float v = cp.val.y(); // value
392
393 rgb_color = v * rgb_color;
394 rgb_color = (1.0f - s)*rgb(v) + s * rgb_color;
395
396 rgba_color = rgba(rgb_color, op.val.y());
397
398 update_texts();
399
400 if(has_opacity) {
401 if(on_change_rgba_callback)
402 on_change_rgba_callback(rgba_color);
403 } else {
404 if(on_change_rgb_callback)
405 on_change_rgb_callback(rgb_color);
406 }
407
408 update_member(&rgb_color);
409}
410
411void color_selector::update_texts() {
412
413 ivec4 components;
414 components[0] = static_cast<int>(round(rgba_color.R() * 255.0f));
415 components[1] = static_cast<int>(round(rgba_color.G() * 255.0f));
416 components[2] = static_cast<int>(round(rgba_color.B() * 255.0f));
417 components[3] = static_cast<int>(round(rgba_color.alpha() * 255.0f));
418
419 components = cgv::math::clamp(components, 0, 255);
420
421 text_geometry.texts[1] = std::to_string(components[0]);
422 text_geometry.texts[3] = std::to_string(components[1]);
423 text_geometry.texts[5] = std::to_string(components[2]);
424 text_geometry.texts[7] = std::to_string(components[3]);
425
426 if(auto* ctx_ptr = get_context())
427 text_geometry.create(*ctx_ptr);
428}
429
430void color_selector::handle_selector_drag(cgv::g2d::DragAction action) {
431
432 if(action == cgv::g2d::DragAction::kDrag) {
433 auto* p = selector_handles.get_dragged();
434 p->update_val();
435
436 update_color();
437 if(p == &selector_handles[1]) {
438 if(auto ctx = get_context())
439 create_color_texture(*ctx);
440 }
441
442 post_damage();
443 }
444}
445
446void color_selector::set_color(rgba color, bool opacity, bool init) {
447
448 this->rgb_color.R() = color.R();
449 this->rgb_color.G() = color.G();
450 this->rgb_color.B() = color.B();
451 this->rgba_color = color;
452
453 float h = color.H();
454 float v = v = color.S() * std::min(color.L(), 1.0f - color.L()) + color.L();
455 float s = v ? 2.0f - 2.0f * color.L() / v : 0.0f;
456
457 selector_handles[0].val = vec2(s, v);
458 selector_handles[1].val.y() = h;
459 selector_handles[2].val.y() = color.alpha();
460
461 selector_handles[0].update_pos();
462 selector_handles[1].update_pos();
463 selector_handles[2].update_pos();
464
465 if(has_opacity != opacity) {
466 // need to recreate the layout and gui in case the opacity usage changed
467 post_recreate_layout();
469 has_opacity = opacity;
470 }
471
472 if(auto ctx = get_context())
473 create_color_texture(*ctx);
474 update_texts();
475
476 if(!init && auto_show)
477 set_visibility(true);
478
479 post_damage();
480}
481
482} // namespace overlay
483} // namespace cgv
void set_name(const std::string &_name)
set a new parent node
Definition named.cxx:13
A data_format describes a multidimensional data block of data entries.
Definition data_format.h:17
the data view gives access to a data array of one, two, three or four dimensions.
Definition data_view.h:153
This class provides methods to test if a stored pointer points to addresses of given variables or ins...
bool points_to(const T &ref) const
Return true if the stored pointer points to the given object.
class to represent all possible mouse events with the EID_MOUSE
Definition mouse_event.h:33
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
T & x()
return first component
Definition fvec.h:136
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
void draw_content(cgv::render::context &ctx) override
draw_content is implemented by decendent classes
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 init_frame(cgv::render::context &ctx) override
this method is called in one pass over all drawables before the draw method
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
void create_gui_impl() override
virtual method to implement the derived class gui creation
void handle_member_change(cgv::data::informed_ptr ptr) override
implement to handle member changes
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
void set_size(const ivec2 &size)
set the default size of the overlay before stretch gets applied
Definition overlay.cxx:111
bool blocks_events() const
return whether this overlay blocks events, i.e. does not pass them to the next event handler
Definition overlay.h:83
void set_visibility(bool visible)
set the visibility of the overlay
Definition overlay.cxx:116
cgv::g2d::irect get_rectangle() const
return the current rectangle area (in screen coordinates) of the overlay taking layout into account
Definition overlay.h:95
void update_layout()
update the layout of the overlay container
Definition overlay.cxx:215
ivec2 get_viewport_size() const
return the current viewport size
Definition overlay.h:89
base class for all drawables, which is independent of the used rendering API.
Definition context.h:670
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
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:214
bool disable(const context &ctx)
disable texture and restore state from before last enable call
Definition texture.cxx:752
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:741
bool destruct(const context &ctx)
destruct the texture and free texture memory and handle
Definition texture.cxx:726
@ CF_RGB
color format with two components R and G
@ MB_LEFT_BUTTON
left button
Definition mouse_event.h:26
@ MA_PRESS
mouse button pressed
Definition mouse_event.h:13
@ TA_RIGHT
center of right edge of text bounds
Definition context.h:332
@ TA_LEFT
center of left edge of text bounds
Definition context.h:331
@ TI_UINT8
signed integer stored in 64 bits
Definition type_id.h:23
this header is dependency free
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:898
cgv::math::fvec< int32_t, 4 > ivec4
declare type of 4d 32 bit integer vectors
Definition fvec.h:712
cgv::media::color< float, cgv::media::RGB > rgb
declare rgb color type with 32 bit components
Definition color.h:896
cgv::math::fvec< int32_t, 2 > ivec2
declare type of 2d 32 bit integer vectors
Definition fvec.h:708
cgv::media::color< cgv::type::uint8_type, cgv::media::RGB > rgb8
declare rgb color type with 8 bit components
Definition color.h:900
cgv::math::fvec< float, 2 > vec2
declare type of 2d single precision floating point vectors
Definition fvec.h:681
cgv::math::fvec< float, 3 > vec3
declare type of 3d single precision floating point vectors
Definition fvec.h:683