1#include "color_selector.h"
3#include <cgv/gui/mouse_event.h>
4#include <cgv/gui/theme_info.h>
5#include <cgv_g2d/msdf_gl_font_renderer.h>
6#include <cgv_gl/gl/gl.h>
11color_selector::color_selector() {
16 layout.padding = padding();
20 selector_handles.callback = std::bind(&color_selector::handle_selector_drag,
this, std::placeholders::_1);
21 selector_handles.set_use_individual_constraints(
true);
26 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, -1);
32 text_geometry.destruct(ctx);
40 cgv::g2d::irect hit_rect;
42 if(layout.color_rect.contains(local_mouse_pos)) {
44 hit_rect = layout.color_rect;
47 if(layout.hue_rect.contains(local_mouse_pos)) {
49 hit_rect = layout.hue_rect;
52 if(layout.opacity_rect.contains(local_mouse_pos)) {
54 hit_rect = layout.opacity_rect;
57 if(hit_index > -1 && hit_index < 4) {
58 vec2 hit_rect_realtive_pos =
static_cast<vec2>(local_mouse_pos - hit_rect.position);
59 vec2 val = hit_rect_realtive_pos /
static_cast<vec2>(hit_rect.size);
62 selector_handles[hit_index].val = val;
63 selector_handles[hit_index].update_pos();
68 update_color_texture();
84 set_rgb_color(rgb_color);
87 set_rgba_color(rgba_color);
95 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, 1);
97 register_shader(
"rectangle", cgv::g2d::shaders::rectangle);
98 register_shader(
"circle", cgv::g2d::shaders::circle);
99 register_shader(
"grid", cgv::g2d::shaders::grid);
103 success &= text_geometry.init(ctx);
105 texts = {
"R",
"0",
"G",
"0",
"B",
"0",
"A",
"0" };
107 text_geometry.set_text_array(ctx, texts);
109 for(
size_t i = 0; i < 4; ++i) {
114 text_geometry.positions = std::vector<vec3>(8, { 0.0f });
121 sh.size =
vec2(16.0f);
122 selector_handles.add(sh);
125 sh.is_rectangular =
true;
126 sh.size =
vec2(20.0f, 10.0f);
127 sh.position_is_center =
false;
128 sh.constraint_reference = cgv::g2d::draggable::CR_MIN_POINT;
129 selector_handles.add(sh);
132 selector_handles.add(sh);
135 selector_handles[0].set_constraint(&layout.color_rect);
136 selector_handles[1].set_constraint(&layout.hue_constraint);
137 selector_handles[2].set_constraint(&layout.opacity_constraint);
140 set_color(
rgba(0.0f, 0.0f, 0.0f, 1.0f),
true,
true);
142 set_color(
rgb(0.0f),
false,
true);
149 if(ensure_layout(ctx)) {
152 for(
size_t i = 0; i < 3; ++i)
153 selector_handles[i].update_pos();
155 int w = layout.opacity_rect.w();
156 int h = layout.opacity_rect.h();
157 opacity_bg_style.texcoord_scaling =
vec2(1.0f,
static_cast<float>(h) /
static_cast<float>(w));
159 ivec2 text_position =
ivec2(layout.preview_rect.b().x() + 10, layout.preview_rect.center().y());
160 for(
unsigned i = 0; i < texts.size(); ++i) {
162 text_geometry.positions[i] =
vec3(text_position, 0.0f);
163 text_position.
x() += i & 1 ? 15 : 40;
173 content_canvas.enable_shader(ctx,
"rectangle");
174 content_canvas.set_style(ctx, border_style);
176 auto& theme = cgv::gui::theme_info::instance();
177 rgba border_color =
rgba(theme.border(), 1.0f);
178 rgba text_background_color =
rgba(theme.text_background(), 1.0f);
179 content_canvas.draw_shape(ctx, layout.border_rect, border_color);
180 content_canvas.draw_shape(ctx, layout.preview_rect, rgb_color);
182 cgv::g2d::irect text_bg = layout.preview_rect;
183 text_bg.size.x() = 48;
184 int n_labels = has_opacity ? 4 : 3;
185 for(
size_t i = 0; i < n_labels; ++i) {
186 text_bg.position.x() =
static_cast<int>(text_geometry.positions[2 * i].x() - 4.0f);
187 content_canvas.draw_shape(ctx, text_bg, text_background_color);
190 content_canvas.set_style(ctx, color_texture_style);
192 content_canvas.draw_shape(ctx, layout.color_rect);
195 content_canvas.set_style(ctx, hue_texture_style);
197 content_canvas.draw_shape(ctx, layout.hue_rect);
201 content_canvas.enable_shader(ctx,
"grid");
202 content_canvas.set_style(ctx, opacity_bg_style);
203 content_canvas.draw_shape(ctx, layout.opacity_rect);
206 glEnable(GL_SCISSOR_TEST);
207 glScissor(layout.color_rect.x(), layout.color_rect.y(), layout.color_rect.w(), layout.color_rect.h());
209 auto& sh = selector_handles;
210 content_canvas.enable_shader(ctx,
"circle");
211 content_canvas.set_style(ctx, color_handle_style);
212 glScissor(layout.color_rect.x(), layout.color_rect.y(), layout.color_rect.w(), layout.color_rect.h());
213 content_canvas.draw_shape(ctx, sh[0].position + 0.5f, sh[0].size);
215 content_canvas.enable_shader(ctx,
"rectangle");
216 content_canvas.set_style(ctx, hue_handle_style);
217 glScissor(layout.hue_rect.x(), layout.hue_rect.y(), layout.hue_rect.w(), layout.hue_rect.h());
218 content_canvas.draw_shape(ctx, sh[1]);
221 glScissor(layout.opacity_rect.x(), layout.opacity_rect.y(), layout.opacity_rect.w(), layout.opacity_rect.h());
223 const auto& r = layout.opacity_rect;
224 content_canvas.enable_shader(ctx,
"rectangle");
225 opacity_color_style.fill_color =
rgba(rgb_color, 1.0f);
226 opacity_color_style.feather_width =
static_cast<float>(r.h());
227 content_canvas.set_style(ctx, opacity_color_style);
228 content_canvas.draw_shape(ctx,
ivec2(r.x(), r.y1() - 1),
ivec2(r.w(), 1));
230 content_canvas.set_style(ctx, hue_handle_style);
231 content_canvas.draw_shape(ctx, sh[2]);
234 content_canvas.disable_current_shader(ctx);
236 glDisable(GL_SCISSOR_TEST);
239 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx).render(ctx, content_canvas, text_geometry, text_style, 0, 2 * n_labels);
248 add_member_control(
this,
"Size", layout.size,
"value_slider",
"min=120;max=1000;step=1;ticks=true");
259void color_selector::set_rgb_color(
rgb color) {
260 set_color(
rgba(color, 1.0f),
false);
263void color_selector::set_rgba_color(
rgba color) {
264 set_color(color,
true);
270 const int slider_width = 20;
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;
277 cgv::g2d::irect content_rect = l.border_rect;
278 content_rect.translate(1, 1);
279 content_rect.size -= 2;
281 int mult = has_opacity ? 2 : 1;
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());
287 l.opacity_rect = l.hue_rect;
288 l.opacity_rect.translate(slider_width + 1, 0);
291 l.color_rect = content_rect;
292 l.color_rect.w() -= 21 * mult;
294 l.preview_rect.position =
ivec2(l.padding);
295 l.preview_rect.size =
ivec2(20, 20);
297 l.hue_constraint = l.hue_rect;
298 l.hue_constraint.translate(0, -5);
299 l.hue_constraint.size.x() = 0;
301 l.opacity_constraint = l.opacity_rect;
302 l.opacity_constraint.translate(0, -5);
303 l.opacity_constraint.size.x() = 0;
307void color_selector::init_styles() {
308 auto& theme = cgv::gui::theme_info::instance();
311 border_style.border_width = 0.0f;
312 border_style.use_fill_color =
false;
313 border_style.feather_width = 0.0f;
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;
321 opacity_color_style.use_blending =
true;
322 opacity_color_style.feather_origin = 1.0f;
325 color_texture_style = border_style;
326 color_texture_style.use_texture =
true;
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);
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;
342 hue_handle_style = color_handle_style;
343 hue_handle_style.position_is_center =
false;
346 text_style.fill_color = theme.text();
347 text_style.font_size = 14.0f;
352 std::vector<uint8_t> data(3*4, 0u);
361 color_tex =
cgv::render::texture(
"uint8[R,G,B]", cgv::render::TF_LINEAR, cgv::render::TF_LINEAR);
362 color_tex.
create(ctx, color_dv, 0);
364 std::vector<uint8_t> hue_data(2*3*256);
366 for(
size_t i = 0; i < 256; ++i) {
367 float hue =
static_cast<float>(i) / 255.0f;
371 col8[0] =
static_cast<uint8_t
>(round(color.R() * 255.0f));
372 col8[1] =
static_cast<uint8_t
>(round(color.G() * 255.0f));
373 col8[2] =
static_cast<uint8_t
>(round(color.B() * 255.0f));
375 hue_data[6 * i + 0] = col8[0];
376 hue_data[6 * i + 1] = col8[1];
377 hue_data[6 * i + 2] = col8[2];
378 hue_data[6 * i + 3] = col8[0];
379 hue_data[6 * i + 4] = col8[1];
380 hue_data[6 * i + 5] = col8[2];
386 hue_tex.
create(ctx, hue_dv, 0);
389void color_selector::update_color_texture() {
394 std::vector<uint8_t> data(3 * 4, 0u);
399 const auto& hp = selector_handles[1];
402 data[9] =
static_cast<uint8_t
>(round(color.R() * 255.0f));
403 data[10] =
static_cast<uint8_t
>(round(color.G() * 255.0f));
404 data[11] =
static_cast<uint8_t
>(round(color.B() * 255.0f));
409 color_tex.
replace(*ctx_ptr, 0, 0, color_dv);
412void color_selector::update_color() {
414 const auto& cp = selector_handles[0];
415 const auto& hp = selector_handles[1];
416 const auto& op = selector_handles[2];
419 float s = cp.val.x();
420 float v = cp.val.y();
422 rgb_color = v * rgb_color;
423 rgb_color = (1.0f - s)*
rgb(v) + s * rgb_color;
425 rgba_color =
rgba(rgb_color, op.val.y());
430 if(on_change_rgba_callback)
431 on_change_rgba_callback(rgba_color);
433 if(on_change_rgb_callback)
434 on_change_rgb_callback(rgb_color);
440void color_selector::update_texts() {
443 components[0] =
static_cast<int>(round(rgba_color.R() * 255.0f));
444 components[1] =
static_cast<int>(round(rgba_color.G() * 255.0f));
445 components[2] =
static_cast<int>(round(rgba_color.B() * 255.0f));
446 components[3] =
static_cast<int>(round(rgba_color.alpha() * 255.0f));
448 components = cgv::math::clamp(components, 0, 255);
450 texts[1] = std::to_string(components[0]);
451 texts[3] = std::to_string(components[1]);
452 texts[5] = std::to_string(components[2]);
453 texts[7] = std::to_string(components[3]);
456 text_geometry.set_text_array(*ctx_ptr, texts);
459void color_selector::handle_selector_drag(cgv::g2d::DragAction action) {
461 if(action == cgv::g2d::DragAction::kDrag) {
462 auto* p = selector_handles.get_dragged();
466 if(p == &selector_handles[1])
467 update_color_texture();
473void color_selector::set_color(
rgba color,
bool opacity,
bool init) {
475 this->rgb_color.R() = color.R();
476 this->rgb_color.G() = color.G();
477 this->rgb_color.B() = color.B();
478 this->rgba_color = color;
481 float v = v = color.S() * std::min(color.L(), 1.0f - color.L()) + color.L();
482 float s = v ? 2.0f - 2.0f * color.L() / v : 0.0f;
484 selector_handles[0].val =
vec2(s, v);
485 selector_handles[1].val.y() = h;
486 selector_handles[2].val.y() = color.alpha();
488 selector_handles[0].update_pos();
489 selector_handles[1].update_pos();
490 selector_handles[2].update_pos();
492 if(has_opacity != opacity) {
494 post_recreate_layout();
496 has_opacity = opacity;
499 update_color_texture();
502 if(!
init && auto_show)
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
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
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 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
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
cgv::g2d::irect get_rectangle() const
return the current rectangle area (in screen coordinates) of the overlay taking layout into account
void set_visibility(bool visible)
set the visibility of the overlay
void update_layout()
update the layout of the overlay container
bool blocks_events() const
return whether this overlay blocks events, i.e. does not pass them to the next event handler
ivec2 get_viewport_size() const
return the current viewport size
void set_size(const ivec2 &size)
set the default size of the overlay before stretch gets applied
void set_name(const std::string &_name)
set a new parent node
the data view gives access to a data array of one, two, three or four dimensions.
class to represent all possible mouse events with the EID_MOUSE
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
void align(const std::string &_align)
send pure alignment information
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.
virtual void update_member(void *member_ptr)
call this to update all views and controls of a member
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
void end_tree_node(const T &value)
template specialization that allows to specify value reference plus node_instance by using the result...
virtual void post_recreate_gui()
delayed recreation of gui
base class for all drawables, which is independent of the used rendering API.
context * get_context() const
access the current context. The context will be available latestly in the init method but not in the ...
virtual bool is_created() const
return whether component has been created
the texture class encapsulates all functionality independent of the rendering api.
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.
bool disable(const context &ctx)
disable texture and restore state from before last enable call
bool enable(const context &ctx, int tex_unit=-1)
enable this texture in the given texture unit, -1 corresponds to the current unit.
bool destruct(const context &ctx)
destruct the texture and free texture memory and handle
bool replace(const context &ctx, int x, const cgv::data::const_data_view &data, int level=-1, const std::vector< cgv::data::data_view > *palettes=0)
replace a block within a 1d texture with the given data.
bool is(const void *ptr) const
Test if the stored pointer points to the given pointer address.
@ CF_RGB
color format with two components R and G
@ MB_LEFT_BUTTON
left button
@ MA_PRESS
mouse button pressed
@ TA_RIGHT
center of right edge of text bounds
@ TA_LEFT
center of left edge of text bounds
@ TI_UINT8
signed integer stored in 64 bits
cgv::media::color< float, cgv::media::RGB, cgv::media::OPACITY > rgba
declare rgba color type with 32 bit components
cgv::math::fvec< int32_t, 4 > ivec4
declare type of 4d 32 bit integer vectors
cgv::media::color< float, cgv::media::RGB > rgb
declare rgb color type with 32 bit components
cgv::math::fvec< int32_t, 2 > ivec2
declare type of 2d 32 bit integer vectors
cgv::math::fvec< float, 2 > vec2
declare type of 2d single precision floating point vectors
cgv::math::fvec< float, 3 > vec3
declare type of 3d single precision floating point vectors