1#include "color_scale_legend.h"
3#include <cgv/gui/theme_info.h>
4#include <cgv/math/ftransform.h>
5#include <cgv/utils/algorithm.h>
7#include <cgv_g2d/msdf_gl_font_renderer.h>
12color_scale_legend::color_scale_legend() {
17 layout.padding = padding();
18 layout.total_size =
ivec2(300, 60);
22 tick_renderer = cgv::g2d::generic_2d_renderer(cgv::g2d::shaders::rectangle);
24 label_format.precision = 0;
25 label_format.trailing_zeros =
false;
26 label_format.decimal_integers =
false;
27 label_format.fixed =
true;
28 label_format.grouping =
true;
33 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, -1);
39 tick_renderer.destruct(ctx);
40 tick_geometry.destruct(ctx);
41 label_geometry.destruct(ctx);
48 layout.total_size.
y() = std::max(layout.total_size.
y(), 2 * layout.padding + 4 + layout.label_space);
56 num_ticks = cgv::math::clamp(num_ticks, 2u, 100u);
59 layout.title_space = title ==
"" ? 0 : 12;
60 post_recreate_layout();
64 post_recreate_layout();
69 cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx, 1);
71 register_shader(
"rectangle", cgv::g2d::shaders::rectangle);
72 register_shader(
"grid", cgv::g2d::shaders::grid);
76 success &= tick_renderer.init(ctx);
77 success &= tick_geometry.init(ctx);
78 success &= label_geometry.init(ctx);
85 if(ensure_layout(ctx)) {
87 if(create_ticks(ctx)) {
88 post_recreate_layout();
92 float width_factor =
static_cast<float>(layout.color_ramp_rect.w());
93 float height_factor =
static_cast<float>(layout.color_ramp_rect.h());
94 background_style.texcoord_scaling =
vec2(width_factor, height_factor) / 10.0f;
105 content_canvas.enable_shader(ctx,
"rectangle");
106 content_canvas.set_style(ctx, border_style);
107 content_canvas.draw_shape(ctx, layout.color_ramp_rect.position - 1, layout.color_ramp_rect.size + 2);
110 content_canvas.enable_shader(ctx,
"grid");
111 content_canvas.set_style(ctx, background_style);
112 content_canvas.draw_shape(ctx, layout.color_ramp_rect);
116 content_canvas.push_modelview_matrix();
117 ivec2 pos = layout.color_ramp_rect.position;
118 ivec2 size = layout.color_ramp_rect.size;
121 if(layout.orientation == Orientation::kVertical) {
122 pos.
x() += layout.color_ramp_rect.size.x();
123 std::swap(size.x(), size.y());
127 content_canvas.mul_modelview_matrix(ctx, cgv::math::translate2h(pos));
128 content_canvas.mul_modelview_matrix(ctx, cgv::math::rotate2h(angle));
131 color_ramp_style.use_texture_alpha = show_opacity;
134 color_ramp_style.texcoord_scaling.x() *= -1.0f;
136 content_canvas.enable_shader(ctx,
"rectangle");
137 content_canvas.set_style(ctx, color_ramp_style);
139 content_canvas.draw_shape(ctx,
ivec2(0), size);
142 content_canvas.pop_modelview_matrix(ctx);
144 content_canvas.disable_current_shader(ctx);
147 tick_renderer.render(ctx, content_canvas, cgv::render::PT_POINTS, tick_geometry, tick_style);
150 auto& font_renderer = cgv::g2d::ref_msdf_gl_font_renderer_2d(ctx);
151 font_renderer.render(ctx, content_canvas, label_geometry, text_style);
158 add_member_control(
this,
"Width", layout.total_size[0],
"value_slider",
"min=40;max=500;step=1;ticks=true");
159 add_member_control(
this,
"Height", layout.total_size[1],
"value_slider",
"min=40;max=500;step=1;ticks=true");
165 add_member_control(
this,
"Orientation", layout.orientation,
"dropdown",
"enums='Horizontal,Vertical'");
166 add_member_control(
this,
"Label Alignment", layout.label_alignment,
"dropdown",
"enums='-,Before,Inside,After'");
169 add_member_control(
this,
"Number Precision", label_format.precision,
"value",
"w=28;min=0;max=10;step=1",
" ");
171 add_member_control(
this,
"Show 0s", label_format.trailing_zeros,
"check",
"w=74",
"");
177void color_scale_legend::set_color_scale(std::shared_ptr<const cgv::media::color_scale> color_scale) {
178 if(this->color_scale != color_scale) {
179 this->color_scale = color_scale;
185void color_scale_legend::set_width(
size_t w) {
186 layout.total_size.
x() = int(w);
187 on_set(&layout.total_size.
x());
190void color_scale_legend::set_height(
size_t h) {
191 layout.total_size.
y() = int(h);
192 on_set(&layout.total_size.
y());
195void color_scale_legend::set_title(
const std::string& t) {
200void color_scale_legend::set_orientation(Orientation orientation) {
201 layout.orientation = orientation;
202 on_set(&layout.orientation);
205void color_scale_legend::set_label_alignment(Alignment alignment) {
206 layout.label_alignment = alignment;
207 on_set(&layout.label_alignment);
210void color_scale_legend::set_invert_color(
bool flag) {
215void color_scale_legend::set_num_ticks(
unsigned n) {
220void color_scale_legend::set_label_precision(
unsigned p) {
221 label_format.precision = p;
222 on_set(&label_format.precision);
225void color_scale_legend::set_label_auto_precision(
bool f) {
230void color_scale_legend::set_label_prune_trailing_zeros(
bool f) {
231 label_format.trailing_zeros = !f;
232 on_set(&label_format.trailing_zeros);
235void color_scale_legend::set_label_integer_mode(
bool enabled) {
236 label_format.decimal_integers = enabled;
237 on_set(&label_format.decimal_integers);
240void color_scale_legend::set_show_opacity(
bool enabled) {
241 show_opacity = enabled;
245void color_scale_legend::init_styles() {
246 auto& theme = cgv::gui::theme_info::instance();
247 rgb tick_color = theme.text();
250 tick_color = pow(
rgb(1.0f) - pow(tick_color, 2.2f), 1.0f / 2.2f);
253 border_style.feather_width = 0.0f;
254 border_style.fill_color = tick_color;
255 border_style.border_width = 0.0f;
258 background_style.fill_color =
rgb(0.9f);
259 background_style.border_color =
rgb(0.75f);
260 background_style.feather_width = 0.0f;
261 background_style.pattern = cgv::g2d::grid2d_style::GridPattern::GP_CHECKER;
264 color_ramp_style = border_style;
265 color_ramp_style.use_texture =
true;
266 color_ramp_style.use_texture_alpha =
true;
267 color_ramp_style.use_blending =
true;
270 text_style.fill_color = tick_color;
271 text_style.font_size = 12.0f;
274 tick_style.position_is_center =
true;
275 tick_style.fill_color = tick_color;
276 tick_style.feather_width = 0.0f;
281 ivec2 size(parent_size);
283 switch(layout.label_alignment) {
284 case Alignment::kStart:
285 if(layout.orientation == Orientation::kHorizontal) {
286 offset.x() = layout.x_label_size / 2;
287 offset.y() = layout.title_space;
288 size.x() -= layout.x_label_size;
289 size.y() -= layout.label_space + layout.title_space;
291 offset.x() = layout.x_label_size + 4;
293 size.x() -= layout.x_label_size + 4 + layout.title_space;
296 case Alignment::kEnd:
297 if(layout.orientation == Orientation::kHorizontal) {
298 offset.x() = layout.x_label_size / 2;
299 offset.y() = layout.label_space;
300 size.x() -= layout.x_label_size;
301 size.y() -= layout.label_space + layout.title_space;
303 offset.x() = layout.title_space;
304 size.x() -= layout.x_label_size + 4 + layout.title_space;
310 layout.color_ramp_rect.position = offset + layout.padding;
311 layout.color_ramp_rect.size = size - 2 * layout.padding;
314void color_scale_legend::create_texture() {
318 if(color_scale && color_scale->get_modified_time() > build_time.get_modified_time()) {
321 size_t resolution = 256;
322 std::vector<rgba> colors = color_scale->quantize(resolution);
323 resolution = colors.size();
325 std::vector<cgv::rgba8> texture_data;
326 texture_data.reserve(resolution);
327 std::transform(colors.begin(), colors.end(), std::back_inserter(texture_data), [](
const cgv::rgba& color) {
328 return cgv::rgba8(color);
338 build_time.modified();
339 post_recreate_layout();
345 label_geometry.clear();
346 tick_geometry.clear();
348 if(!color_scale || layout.label_alignment == Alignment::kFree) {
349 layout.x_label_size = 0;
353 const cgv::vec2 domain = color_scale->get_domain();
355 std::vector<float> ticks;
357 ticks = color_scale->get_ticks(num_ticks);
362 layout.x_label_size = 0;
366 ivec2 tick_size(1, 6);
369 int label_offset = 4;
370 Alignment label_alignment = layout.label_alignment;
377 float title_angle = 0.0f;
379 if(layout.orientation == Orientation::kVertical) {
383 std::swap(tick_size.x(), tick_size.y());
385 if(label_alignment == Alignment::kStart) label_alignment = Alignment::kEnd;
386 else if(label_alignment == Alignment::kEnd) label_alignment = Alignment::kStart;
392 std::swap(title_alignment_1, title_alignment_2);
397 ivec2 title_position = layout.color_ramp_rect.position;
398 ivec2 tick_start = layout.color_ramp_rect.position;
401 title_alignment = title_alignment_1;
402 text_alignment = text_v_end;
405 switch(label_alignment) {
406 case Alignment::kStart:
407 title_position[1 - axis] -= 4;
408 tick_start[1 - axis] += layout.color_ramp_rect.size[1 - axis] + 3;
410 case Alignment::kCenter:
411 title_position[axis] += 2;
412 title_position[1 - axis] += layout.color_ramp_rect.size[1 - axis] - (axis ? 3 : 1);
413 tick_start[1 - axis] += 3;
416 case Alignment::kEnd:
417 title_alignment = title_alignment_2;
418 title_position[1 - axis] += layout.color_ramp_rect.size[1 - axis] + 4;
419 tick_start[1 - axis] -= 3;
420 label_offset = -label_offset;
421 text_alignment = text_v_start;
429 unsigned last_precision = label_format.precision;
431 label_format.precision_from_range(domain[0], domain[1]);
433 int length = layout.color_ramp_rect.size[axis] + 2;
435 for(
float tick : ticks) {
436 float normalized_position = color_scale->normalize_value(tick);
437 if(color_scale->is_reversed())
438 normalized_position = 1.0f - normalized_position;
439 int offset =
static_cast<int>(std::round(normalized_position * length));
440 offset = cgv::math::clamp(offset, 0, length - 1);
442 ivec2 tick_pos = tick_start;
443 tick_pos[axis] += offset;
445 ivec2 label_pos = tick_start;
446 label_pos[1 - axis] += label_offset;
447 label_pos[axis] += offset;
449 tick_geometry.add(tick_pos, tick_size);
450 label_geometry.texts.push_back(label_format.convert(tick));
451 label_geometry.positions.push_back(
vec3(label_pos, 0.0f));
455 label_format.precision = last_precision;
457 label_geometry.alignments.resize(ticks.size(), text_alignment);
458 label_geometry.rotations.resize(ticks.size());
462 label_geometry.positions.front()[axis] += 3.0f;
464 label_geometry.positions.back()[axis] -= 3.0f;
468 label_geometry.texts.push_back(title);
471 label_geometry.positions.push_back(
cgv::vec3(
static_cast<cgv::vec2>(title_position), 0.0f));
472 label_geometry.rotations.push_back(
cgv::quat(
cgv::vec3(0.0f, 0.0f, 1.0f), cgv::math::deg2rad(title_angle)));
473 label_geometry.alignments.push_back(title_alignment);
475 label_geometry.create(ctx);
476 const std::vector<cgv::g2d::msdf_text_geometry::text_info>& text_infos = label_geometry.ref_text_infos();
478 int x_label_size = 0;
480 if(label_geometry.size() > 1) {
481 if(layout.orientation == Orientation::kHorizontal) {
482 x_label_size =
static_cast<int>(std::max(text_infos.front().normalized_width, text_infos[text_infos.size() - 2].normalized_width) * text_style.font_size);
484 float max_width = -1.0f;
485 for(
size_t i = 0; i < ticks.size(); ++i)
486 max_width = std::max(max_width, text_infos[i].normalized_width);
487 x_label_size =
static_cast<int>(max_width * text_style.font_size);
491 if(layout.x_label_size != x_label_size) {
492 layout.x_label_size = x_label_size;
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.
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
T & y()
return second component
T & x()
return first component
bool init(cgv::render::context &ctx) override
this method is called after creation or recreation of the context, return whether all necessary funct...
virtual void on_set(void *member_ptr) override
default implementation of that calls handle_member_change and afterwards upates the member in the gui...
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...
void clear(cgv::render::context &ctx) override
clear all objects living in the context like textures or display lists
void handle_member_change(cgv::data::informed_ptr ptr) override
implement to handle member changes
void create_gui_impl() override
virtual method to implement the derived class gui creation
void set_size(const ivec2 &size)
set the default size of the overlay before stretch gets applied
cgv::g2d::irect get_rectangle() const
return the current rectangle area (in screen coordinates) of the overlay taking layout into account
void update_layout()
update the layout of the overlay container
bool background_visible_
whether the background is visible (true by default)
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
void set_mag_filter(TextureFilter _mag_filter)
set the magnification filter
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
void set_min_filter(TextureFilter _min_filter, float _anisotropy=2.0f)
set the minification filters, if minification is set to TF_ANISOTROP, the second floating point param...
@ CF_RGBA
color format with components R, G and B
TextAlignment
different text alignments
@ TA_TOP
center of top edge of text bounds
@ TA_BOTTOM
center of bottom edge of text bounds
@ TA_RIGHT
center of right edge of text bounds
@ TA_LEFT
center of left edge of text bounds
TextureFilter
different texture filter
@ TI_UINT8
signed integer stored in 64 bits
void subdivision_sequence(OutputIt output_first, ParamT start, ParamT stop, size_t n)
Generate a sequence of n uniformly-spaced values in [start,stop] and store the result in an output ra...
this header is dependency free
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
Helper functions to process strings.