28 la::vec<2> axis_direction{0, 0};
29 la::vec<2> label_direction{0, 0};
30 la::scalar label_offset{0};
31 la::vec<2> label_orientation{0, 0};
33 bool major_ticks_show =
false;
34 bool minor_ticks_show =
false;
35 unsigned int minor_ticks_count = 4;
36 std::string title =
"";
38 la::scalar title_offset{0};
40 std::function<void(la::scalar)> move_callback;
41 std::function<void()> reset_callback;
45 std::vector<cydui::components::component_holder_t> result{};
48 auto start = props.step * std::floor((props.min / props.step));
49 auto num_steps = (props.max - start) / props.step;
50 for (la::scalar step_i = 0; step_i < num_steps; step_i = step_i + la::scalar{1}) {
51 la::scalar X = start + step_i * props.step;
52 if (X < props.min || X > props.max) {
55 auto pos = props.axis_direction * ((X - props.min) / (props.max - props.min));
56 result.push_back(props.label_component(X));
58 auto label = result.back().get_components()[0];
59 auto label_dim = label->get_dimensional_relations();
60 if (props.axis_direction[0] > la::scalar{0}) {
61 label_dim.x = $width *
screen_measure{pos[0]} - (label_dim.width / 2_px) +
62 (props.label_direction[0] * props.label_offset);
63 }
else if (props.axis_direction[0] < la::scalar{0}) {
65 (props.label_direction[0] * props.label_offset) + $width;
67 if (props.label_direction[0] > 0) {
68 label_dim.x = $width * pos[0] - (label_dim.width / 2) +
69 (props.label_direction[0] * props.label_offset);
71 label_dim.x = $width * pos[0] - (label_dim.width / 2) +
72 (props.label_direction[0] * props.label_offset) + $width;
75 if (props.axis_direction[1] > 0) {
76 label_dim.y = $height * pos[1] + (label_dim.height / 2) +
77 (props.label_direction[1] * props.label_offset);
78 }
else if (props.axis_direction[1] < 0) {
79 label_dim.y = $height * pos[1] + (label_dim.height / 2) +
80 (props.label_direction[1] * props.label_offset) + $height;
82 if (props.label_direction[1] > 0) {
83 label_dim.y = $height * pos[1] + (label_dim.height / 2) +
84 (props.label_direction[1] * props.label_offset);
86 label_dim.y = $height * pos[1] + (label_dim.height / 2) +
87 (props.label_direction[1] * props.label_offset) + $height;
98 if (props.axis_direction[0] < 0) {
100 }
else if (props.axis_direction[0] == 0) {
101 if (props.label_direction[0] <= 0) {
106 if (props.axis_direction[1] < 0) {
108 }
else if (props.axis_direction[1] == 0) {
109 if (props.label_direction[1] <= 0) {
123 }
else if (y2 < -$height) {
125 }
else if (x2 > $width) {
127 }
else if (x2 < -$width) {
131 vg::line{}.x1(x1).y1(y1).x2($width).y2($height).stroke_width(2).stroke(
"#FFFFFF"_color)
134 fragment.append(vg::line{}
140 .stroke(
"#777777"_color));
141 const auto line_length = std::sqrt(la::vec<2>{(x2 - x1).value, (y2 - y1).value}.mag2());
144 auto start = props.step * std::floor((props.min / props.step));
145 if (props.major_ticks_show) {
146 for (la::scalar X = start; X < props.max; X += props.step) {
147 auto pos = props.axis_direction * ((X - props.min) / (props.max - props.min));
151 if (X >= props.min && X <= props.max) {
152 fragment.append(vg::line{}
158 props.label_direction[0] * props.label_offset * 0.25
164 props.label_direction[1] * props.label_offset * 0.25
169 .stroke(
"#777777"_color));
172 if (props.minor_ticks_show) {
173 la::scalar minor_step = props.step / (props.minor_ticks_count + 1.0);
174 for (la::scalar Y = minor_step; Y <= props.step - minor_step && (X + Y) < props.max;
176 pos = props.axis_direction * (((X + Y) - props.min) / (props.max - props.min));
180 if ((X + Y) >= props.min && (X + Y) <= props.max) {
181 fragment.append(vg::line{}
187 props.label_direction[0] * props.label_offset * 0.15
193 props.label_direction[1] * props.label_offset * 0.15
198 .stroke(
"#777777"_color));
206 if (!props.title.empty()) {
208 vg::text{props.title}.font_family(
"Helvetica").font_size(14).fill(
"#EEEEEE"_color);
209 auto footprint = title.get_footprint();
210 title.pivot_x(footprint.w / 2);
211 title.pivot_y(footprint.h / 2);
215 int text_offset_y = footprint.h.value_as_base_unit() / 2;
216 switch (props.title_align) {
223 text_offset_x = footprint.w.value_as_base_unit() / 2;
227 text_offset_x = footprint.w.value_as_base_unit();
230 auto pos = (props.axis_direction * ratio * line_length) +
231 (props.label_direction * props.title_offset);
233 std::atan2(props.axis_direction[1], props.axis_direction[0]) * 180.0 / std::numbers::pi;
238 fragment.append(title);
244 props.move_callback(0.1);
245 state.parent()->mark_dirty();
246 }
else if (dy.value < 0) {
247 props.move_callback(-0.1);
248 state.parent()->mark_dirty();
254 props.reset_callback();
255 state.parent()->mark_dirty();
317 const la::vec<2> axis_direction,
318 const la::vec<2> label_direction,
319 const la::vec<2> label_orientation,
321 la::scalar default_label_offset = 30,
322 la::scalar default_title_offset = 45
325 : show_(default_show),
326 label_offset_(default_label_offset),
327 title_offset_(default_title_offset),
329 axis_direction_(axis_direction),
330 label_direction_(label_direction),
331 label_orientation_(label_orientation) {
332 initial_min_value_ = min_value_;
333 initial_max_value_ = max_value_;
334 initial_step_ = step_;
337 C&
show(
const bool show_axis) {
342 C&
bounds(
const la::scalar min,
const la::scalar max,
const la::scalar step) {
344 auto_scale_bound_zero_ =
false;
348 initial_min_value_ = min_value_;
349 initial_max_value_ = max_value_;
350 initial_step_ = step_;
356 auto_scale_bound_zero_ = bind_zero;
361 major_ticks_show_ = show_ticks;
366 minor_ticks_show_ = show_ticks;
371 minor_ticks_count_ = tick_count;
375 C&
title(
const std::string& title_str) {
381 title_align_ = align;
386 title_offset_ = offset;
394 label_builder_ = [&](
const la::scalar value) {
return builder(value); };
398 template <
typename L>
400 label_builder_ = [&](
const la::scalar value) {
return L{{value}}; };
407 auto_scale_ = rhl.auto_scale_;
408 auto_scale_bound_zero_ = rhl.auto_scale_bound_zero_;
409 min_value_ = rhl.min_value_;
410 max_value_ = rhl.max_value_;
412 initial_min_value_ = rhl.initial_min_value_;
413 initial_max_value_ = rhl.initial_max_value_;
414 initial_step_ = rhl.initial_step_;
415 axis_direction_ = rhl.axis_direction_;
416 label_direction_ = rhl.label_direction_;
417 label_orientation_ = rhl.label_orientation_;
418 label_builder_ = rhl.label_builder_;
419 label_offset_ = rhl.label_offset_;
421 title_align_ = rhl.title_align_;
422 title_offset_ = rhl.title_offset_;
426 PlotAxis build_component(
auto&& x,
auto&& y,
auto&& w,
auto&& h) {
428 {.axis_direction = axis_direction_,
429 .label_direction = label_direction_,
430 .label_offset = label_offset_,
431 .label_orientation = label_orientation_,
435 .major_ticks_show = major_ticks_show_,
436 .minor_ticks_show = minor_ticks_show_,
437 .minor_ticks_count = minor_ticks_count_,
439 .title_align = title_align_,
440 .title_offset = title_offset_,
441 .label_component = label_builder_,
443 [&](
const la::scalar dx) {
449 min_value_ = initial_min_value_;
450 max_value_ = initial_max_value_;
451 step_ = initial_step_;
454 pa.x(x).y(y).width(w).height(h);
459 friend typename C::event_handler_t;
467 bool auto_scale_{
true};
468 bool auto_scale_bound_zero_{
false};
475 bool major_ticks_show_{
true};
476 bool minor_ticks_show_{
false};
477 unsigned int minor_ticks_count_{3};
478 la::scalar label_offset_{30};
479 std::string title_{
""};
481 la::scalar title_offset_{45};
485 la::vec<2> axis_direction_{0, 0};
486 la::vec<2> label_direction_{0, 0};
487 la::vec<2> label_orientation_{0, 0};
488 std::function<cydui::components::component_builder_t(la::scalar)> label_builder_{
489 [](
const la::scalar value) {
return NumericAxisLabel{{value}}; }