summaryrefslogtreecommitdiff
path: root/HelloWorld/seeed_line_chart.h
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2021-12-18 11:45:26 +0200
committerYuval Adam <_@yuv.al>2021-12-18 11:45:26 +0200
commitc1331d40912bc808f927c2f742da44530039123e (patch)
treed4dbecd20686dd764569a7a900b4984c8e0e8346 /HelloWorld/seeed_line_chart.h
parent74b082c851303ff0a87b492c5f3c40759d1fd40c (diff)
Add lots of graphics libs and show axl readings
Diffstat (limited to 'HelloWorld/seeed_line_chart.h')
-rw-r--r--HelloWorld/seeed_line_chart.h422
1 files changed, 422 insertions, 0 deletions
diff --git a/HelloWorld/seeed_line_chart.h b/HelloWorld/seeed_line_chart.h
new file mode 100644
index 0000000..9b82460
--- /dev/null
+++ b/HelloWorld/seeed_line_chart.h
@@ -0,0 +1,422 @@
+#pragma once
+#include"seeed_graphics_base.h"
+#include"float.h"
+
+struct range {
+ double max_value;
+ double min_value;
+ size_t max_count;
+
+ template<class type>
+ range(type & list) {
+ max_value = 0;
+ min_value = 0;
+ max_count = 0;
+
+ if (list.size() == 0) {
+ return;
+ }
+
+ min_value = max_value = list[0].front();
+
+ for (size_t i = 0; i < list.size(); i++) {
+ if (max_count < list[i].size()) {
+ max_count = list[i].size();
+ }
+ for (size_t j = 0; j < list[i].size(); j++) {
+ if (max_value < list[i].front()) {
+ max_value = list[i].front();
+ }
+ else if (min_value > list[i].front()) {
+ min_value = list[i].front();
+ }
+ list[i].push(list[i].front());
+ list[i].pop();
+ }
+ }
+ }
+};
+
+struct match_tick {
+ double abs_value;
+ double top_value;
+ double start_value;
+ double step;
+ pix_t tick;
+
+ match_tick(
+ double max_value,
+ double min_value,
+ double based_on,
+ pix_t max_tick,
+ pix_t min_tick) {
+ constexpr bool found = true;
+ constexpr bool not_found = false;
+ double exp;
+ double val;
+ bool is_neg = false;
+ bool need_find = true;
+ int32_t max_val = 0;
+ int32_t min_val = 0;
+ auto min_step = { 5, 4, 2, 1 };
+ max_value -= based_on;
+ min_value -= based_on;
+ start_value = 0;
+ abs_value = 0;
+ top_value = 0;
+ tick = 0;
+ step = 0;
+
+ auto invoke = [&](int min_step){
+ min_val = int32_t(val) + 1;
+ max_val = min_val * 15 / 100 * 10; //1.510010
+ while (min_tick <= max_tick) {
+ while (max_val > min_val) {
+ if (max_val % min_tick == 0) {
+ tick = min_tick;
+ step = double(max_val / min_tick) * pow(10, -exp);
+ top_value = step * tick;
+ abs_value = top_value - start_value;
+ if (is_neg) {
+ start_value = -top_value;
+ top_value = 0;
+ }
+ min_tick = max_tick;
+ return found;
+ }
+ max_val -= 5;
+ }
+ min_tick++;
+ }
+ return not_found;
+ };
+
+ if (min_value >= 0) {
+ if (max_value <= 1) {
+ exp = round(-log10(max_value)) + 2;
+ }
+ else {
+ exp = -round(log10(max_value)) + 2;
+ }
+ val = max_value * pow(10, exp);
+
+ }
+ else if (max_value <= 0){
+ if (min_value >= -1) {
+ exp = round(-log10(-min_value)) + 2;
+ }
+ else {
+ exp = -round(log10(-min_value)) + 2;
+ }
+ is_neg = true;
+ val = -min_value * pow(10, exp);
+ }
+ else {
+ while(min_tick <= max_tick) {
+ for (double i = 1.0, j = 1; i < 1.5; i += 0.1) {
+ auto tmp = match_tick((max_value - min_value) * i, 0, 0, max_tick, min_tick);
+ min_tick++;
+
+ while (j * tmp.step < -min_value) {
+ j++;
+ }
+
+ auto start = j * tmp.step;
+ auto top = tmp.abs_value - start;
+
+ if (top >= max_value) {
+ abs_value = tmp.abs_value;
+ top_value = top;
+ start_value = -start;
+ step = tmp.step;
+ tick = tmp.tick;
+ min_tick = max_tick + 1;
+ break;
+ }
+ }
+ }
+ need_find = false;
+ }
+ if (need_find){
+ for (auto m : min_step){
+ if (invoke(m) == found){
+ break;
+ }
+ }
+ }
+ top_value += based_on;
+ start_value += based_on;
+ }
+};
+
+struct line_chart {
+ template<class list, class callback>
+ void draw_tick(
+ list & items,
+ size_t tick_count,
+ pix_t total_pix,
+ callback call) {
+ auto tick_step = 1.0 * items.size() / tick_count;
+ auto tick_pix_step = 1.0 * total_pix / tick_count;
+ auto tick_sum = 0.0;
+ auto tick_pix_sum = 0.0;
+
+ for (size_t i = 0; i <= tick_count; i++, tick_sum += tick_step, tick_pix_sum += tick_pix_step) {
+ auto index = size_t(round(tick_sum));
+ auto x = pos_t(round(tick_pix_sum));
+ call(i, x);
+ }
+ }
+public:
+ xpositionx(
+ line_chart,
+ xlist(pix_t width, pix_t height), {
+ _height = height;
+ _width = width;
+ }, {
+ _x_max_tick_count = 10;
+ _x_min_tick_count = 3;
+ _y_max_tick_count = 8;
+ _y_min_tick_count = 3;
+ _x_skip_tick = 0;
+ _tick = 8;
+ _x_auxi_role = dash_line().color(gray);
+ _x_role_color = pan_color;
+ _x_tick_color = pan_color;
+ _x_role_thickness = pan_thickness;
+ _y_role_color = pan_color;
+ _y_tick_color = pan_color;
+ _y_role_thickness = pan_thickness;
+ _format = "%g";
+ _color = classic_colors;
+ _show_circle = std::initializer_list<bool>{ true };
+ _based_on = 0;
+ });
+ xpoint(line_chart);
+ xprop(pix_t, height);
+ xprop(pix_t, width) ;
+ xprop(pix_t, x_max_tick_count);
+ xprop(pix_t, x_min_tick_count);
+ xprop(pix_t, y_max_tick_count);
+ xprop(pix_t, y_min_tick_count);
+ xprop(float, x_skip_tick);
+ xprop(pix_t, tick);
+ xprop(dash_line, x_auxi_role);
+ xprop(color_t, x_role_color);
+ xprop(color_t, x_tick_color);
+ xprop(pix_t, x_role_thickness);
+ xprop(color_t, y_role_color);
+ xprop(color_t, y_tick_color);
+ xprop(pix_t, y_role_thickness);
+ xprop(const char *, format);
+ xprop(double, based_on);
+
+private:
+ std::vector<color_t> _color;
+public:
+ template<class ... arg>
+ auto & color(color_t first, arg ... list){
+ _color = std::vector<color_t>{ first, list... };
+ return this[0];
+ }
+private:
+ std::vector<bool> _show_circle;
+public:
+ template<class ... arg>
+ auto & show_circle(bool first, arg ... list) {
+ _show_circle = std::vector<bool>{ first, list... };
+ return this[0];
+ }
+
+ xprop(std::vector<text_t>, note);
+private:
+ std::vector<doubles> _value;
+public:
+ auto & value(doubles const & list){
+ _value.clear();
+ _value.push_back(list);
+ return this[0];
+ }
+ auto & value(std::vector<double> const & list){
+ doubles que;
+ for(auto i : list){
+ que.push(i);
+ }
+ return value(que);
+ }
+ auto & value(std::vector<doubles> const & items){
+ _value = items;
+ return this[0];
+ }
+
+ void draw() {
+ //y/x
+ //x
+ auto y_tick_value_template = text().origin(right).vorigin(vcenter);
+ auto x_tick_value_template = text().origin(center).vorigin(top);
+ auto r = range(_value);
+ auto m = match_tick(r.max_value, r.min_value, _based_on, _y_max_tick_count, _y_min_tick_count);
+ auto w = pix_t(0);
+ auto max_y_tick_pix_width = 0;
+ char buf[256];
+ char * p = buf;
+ std::vector<text> y_tick_value;
+ for (size_t i = 0; i <= m.tick; i++, p += strlen(p) + 1) {
+ sprintf(p, _format, m.start_value + i * m.step);
+ y_tick_value.push_back(
+ text().value(p).origin(right).vorigin(vcenter).color(_y_tick_color).content_width(&w)
+ );
+ if (max_y_tick_pix_width < w) {
+ max_y_tick_pix_width = w;
+ }
+ }
+
+ //1.5 -> 0.5y 1.0x
+ auto x_extend_step = _value.size() != 0 ? 1.5 : 0.5;
+ auto y_extend_height = _tick + x_extend_step * x_tick_value_template.font_height();
+ auto y_extend_width = _tick + max_y_tick_pix_width;
+ auto x = _x + y_extend_width;
+ auto y = _y + 0.5 * x_tick_value_template.font_height();
+ auto width = _width - y_extend_width;
+ auto height = _height - y_extend_height;
+ auto origin = point(x, y + height);
+ auto y_start = origin(0, _tick);
+ auto y_end = origin(0, -(pos_t)height);
+ auto x_start = origin(-_tick, 0);
+ auto x_end = origin(width, 0);
+
+
+ if (_value.size()) draw_tick(_value, m.tick, height, [&](size_t i, pix_t y) {
+ auto p0 = origin(0, -(pos_t)y);
+ auto p1 = origin(-_tick, -(pos_t)y);
+
+ //
+ if (i != 0) {
+ _x_auxi_role.xy(p0).length(width).draw();
+ }
+
+ //
+ line(p0, p1).color(_x_tick_color).draw();
+ y_tick_value[i].xy(p1).draw();
+ });
+
+ //x
+ auto x_tick = std::min(_note.size(), size_t(_x_max_tick_count));
+ auto x_skip_half_tick = round(_x_skip_tick) > _x_skip_tick;
+ auto x_skip_tick = pix_t(_x_skip_tick);
+ auto x_tick_addition = x_skip_half_tick ? 0 : 1;
+ auto x_step = double(width) / (r.max_count + x_skip_tick - x_tick_addition);
+ auto x_offset = x_skip_half_tick ? x_step / 2 : 0;
+ auto x_tick_width = pix_t(x_step);
+
+ if (_note.size()) draw_tick(_note, x_tick + x_skip_tick - x_tick_addition, width, [&](size_t i, pix_t x) {
+ auto p0 = origin(x, 0);
+ auto p1 = origin(x, _tick);
+ line(p0, p1).color(_x_tick_color).draw();
+
+ if (i < x_skip_tick) {
+ return;
+ }
+
+ auto index = i - x_skip_tick;
+ auto p2 = p1(pix_t(x_offset), 0);
+
+ if (index >= _note.size()) {
+ return;
+ }
+
+ text(p2, _note[index])
+ .origin(center)
+ .width(x_tick_width)
+ .color(_x_tick_color)
+ .draw();
+ });
+
+ //
+ for (size_t i = 0; i < _value.size(); i++) {
+ doubles cur = _value[i];
+ auto default_color = _color[std::min(i, _color.size() - 1)];
+ auto show_circle = _show_circle[std::min(i, _show_circle.size() - 1)];
+ std::vector<point> value_point;
+
+ for(int j = 0; j < cur.size() - 1; j++){
+ cur.push(cur.front());
+ auto a = cur.front(); cur.pop();
+ auto b = cur.front();
+ auto ha = pos_t(round((a - m.start_value) / m.abs_value * height));
+ auto hb = pos_t(round((b - m.start_value) / m.abs_value * height));
+ auto pa = origin(pos_t((j + x_skip_tick) * x_step + x_offset), -(pos_t)ha);
+ auto pb = origin(pos_t((j + x_skip_tick + 1) * x_step + x_offset), -(pos_t)hb);
+ line(pa, pb).color(default_color).draw();
+ if (j == 0) {
+ value_point.push_back(pa);
+ }
+ value_point.push_back(pb);
+ }
+ if (show_circle) {
+ for (auto p : value_point) {
+ ellipse(p, 5, 5).color(default_color).fill(white).origin(center).vorigin(vcenter).draw();
+ }
+ }
+ }
+ line(x_start, x_end).color(_x_role_color).thickness(_x_role_thickness).draw();
+ line(y_start, y_end).color(_y_role_color).thickness(_y_role_thickness).draw();
+ }
+
+ operator can_drawable(){
+ return can_drawable(this);
+ }
+};
+
+// struct ring_chart {
+// xpositionx(
+// ring_chart,
+// xlist(pix_t r), {
+// _r = r;
+// }, {
+// _r = 0;
+// _angle_offset = 0;
+// _color = classic_colors;
+// _thickness.push_back(20);
+// });
+
+// xpoint(ring_chart);
+// xprop(pix_t, r);
+// xprop(float, angle_offset);
+// xvprop(color_t, color);
+// xvprop(pix_t, thickness);
+// xvprop(double, value);
+
+// void draw() {
+// if (_value.size() == 0) {
+// return;
+// }
+
+// auto color = _color.cbegin();
+// auto thickness = _thickness.cbegin();
+// auto sum = 0.0;
+// auto offset = _angle_offset;
+
+// for (auto v : _value) {
+// sum += v;
+// }
+// for (auto v : _value) {
+// auto angle = 360.0 * v / sum;
+// // ellipse(_x, _y, _r)
+// // .start_angle(offset)
+// // .end_angle(offset += angle)
+// // .color(*color)
+// // .thickness(*thickness)
+// // .draw();
+
+// if (++color == _color.cend()) {
+// color = _color.cbegin();
+// }
+// if (thickness + 1 != _thickness.cend()) {
+// thickness++;
+// }
+// }
+// }
+// };
+
+// #endif