diff options
| author | Yuval Adam <_@yuv.al> | 2021-12-18 11:45:26 +0200 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2021-12-18 11:45:26 +0200 |
| commit | c1331d40912bc808f927c2f742da44530039123e (patch) | |
| tree | d4dbecd20686dd764569a7a900b4984c8e0e8346 | |
| parent | 74b082c851303ff0a87b492c5f3c40759d1fd40c (diff) | |
Add lots of graphics libs and show axl readings
| -rw-r--r-- | HelloWorld/ADXL335.cpp | 84 | ||||
| -rw-r--r-- | HelloWorld/ADXL335.h | 84 | ||||
| -rw-r--r-- | HelloWorld/HelloWorld.ino | 48 | ||||
| -rw-r--r-- | HelloWorld/seeed_graphics_base.cpp | 155 | ||||
| -rw-r--r-- | HelloWorld/seeed_graphics_base.h | 418 | ||||
| -rw-r--r-- | HelloWorld/seeed_graphics_define.h | 102 | ||||
| -rw-r--r-- | HelloWorld/seeed_line_chart.h | 422 |
7 files changed, 1312 insertions, 1 deletions
diff --git a/HelloWorld/ADXL335.cpp b/HelloWorld/ADXL335.cpp new file mode 100644 index 0000000..9abdb3f --- /dev/null +++ b/HelloWorld/ADXL335.cpp @@ -0,0 +1,84 @@ +/* + ADXL335.h + Library for accelerometer_ADXL335 + + Copyright (c) 2013 seeed technology inc. + Author : FrankieChu + Create Time : Jan 2013 + Change Log : + + The MIT License (MIT) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include <Arduino.h> +#include "ADXL335.h" + +void ADXL335::pinsInit() { + pinMode(X_AXIS_PIN, INPUT); + pinMode(Y_AXIS_PIN, INPUT); + pinMode(Z_AXIS_PIN, INPUT); +} +void ADXL335::begin() { + pinsInit(); + scale = (float)SENSITIVITY * ADC_AMPLITUDE / ADC_REF; +} +void ADXL335::getXYZ(int* x, int* y, int* z) { + *x = analogRead(X_AXIS_PIN); + *y = analogRead(Y_AXIS_PIN); + *z = analogRead(Z_AXIS_PIN); +} +void ADXL335::getAcceleration(float* ax, float* ay, float* az) { + int x, y, z; + float xvoltage, yvoltage, zvoltage; + getXYZ(&x, &y, &z); + + xvoltage = (float)x * ADC_REF / ADC_AMPLITUDE; + yvoltage = (float)y * ADC_REF / ADC_AMPLITUDE; + zvoltage = (float)z * ADC_REF / ADC_AMPLITUDE; + + *ax = (xvoltage - ZERO_X) / SENSITIVITY; + *ay = (yvoltage - ZERO_Y) / SENSITIVITY; + *az = (zvoltage - ZERO_Z) / SENSITIVITY; +} +float ADXL335::getAccelerationX() { + int x, y, z; + float xvoltage, ax; + getXYZ(&x, &y, &z); + xvoltage = (float)x * ADC_REF / ADC_AMPLITUDE; + ax = (xvoltage - ZERO_X) / SENSITIVITY; + return ax; +} +float ADXL335::getAccelerationY() { + int x, y, z; + float yvoltage, ay; + getXYZ(&x, &y, &z); + yvoltage = (float)y * ADC_REF / ADC_AMPLITUDE; + ay = (yvoltage - ZERO_Y) / SENSITIVITY; + return ay; +} +float ADXL335::getAccelerationZ() { + int x, y, z; + float zvoltage, az; + getXYZ(&x, &y, &z); + zvoltage = (float)z * ADC_REF / ADC_AMPLITUDE; + az = (zvoltage - ZERO_Z) / SENSITIVITY; + return az; +} diff --git a/HelloWorld/ADXL335.h b/HelloWorld/ADXL335.h new file mode 100644 index 0000000..cc70a03 --- /dev/null +++ b/HelloWorld/ADXL335.h @@ -0,0 +1,84 @@ +/* + ADXL335.h + Library for accelerometer_ADXL335 + + Copyright (c) 2013 seeed technology inc. + Author : FrankieChu + Create Time : Jan 2013 + Change Log : + + The MIT License (MIT) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef __ADXL335_H__ +#define __ADXL335_H__ + +#include <Arduino.h> + +// macro definitions of Analog read pins +#ifndef X_AXIS_PIN +#define X_AXIS_PIN A0 +#endif +#ifndef Y_AXIS_PIN +#define Y_AXIS_PIN A1 +#endif +#ifndef Z_AXIS_PIN +#define Z_AXIS_PIN A2 +#endif + +// amplitude of the 10bit-ADC of Arduino is 1024LSB, using as default +#ifndef ADC_AMPLITUDE +#define ADC_AMPLITUDE 1024 +#endif +#ifndef ADC_REF +#define ADC_REF 5 // ADC reference is 5v +#endif + +// accleration of X-AXIS is 0g, the voltage of X-AXIS is 1.22v +#ifndef ZERO_X +#define ZERO_X 1.22 +#endif +#ifndef ZERO_Y +#define ZERO_Y 1.22 // +#endif +#ifndef ZERO_Z +#define ZERO_Z 1.25 // +#endif + +// sensitivity of X/Y/Z axis is 0.25v/g +#ifndef SENSITIVITY +#define SENSITIVITY 0.25 +#endif + +class ADXL335 { + private: + void pinsInit(); + float scale; + public: + void begin(); + void getXYZ(int* x, int* y, int* z); + void getAcceleration(float* ax, float* ay, float* az); + float getAccelerationX(); + float getAccelerationY(); + float getAccelerationZ(); +}; + +#endif diff --git a/HelloWorld/HelloWorld.ino b/HelloWorld/HelloWorld.ino index ad56df4..defcab1 100644 --- a/HelloWorld/HelloWorld.ino +++ b/HelloWorld/HelloWorld.ino @@ -1,6 +1,16 @@ -#include"TFT_eSPI.h" +#include "TFT_eSPI.h" +#include "ADXL335.h" +#include "seeed_graphics_define.h" +#include "seeed_graphics_base.h" +#include "seeed_line_chart.h" TFT_eSPI tft; +ADXL335 axl; + +#define max_size 50 + +doubles accelerator_readings[3]; +TFT_eSprite spr = TFT_eSprite(&tft); void setup() { tft.begin(); @@ -10,8 +20,44 @@ void setup() { tft.setTextSize(4); tft.drawString("Hello world!", 20, 100); + spr.createSprite(TFT_HEIGHT, TFT_WIDTH); + + axl.begin(); } void loop() { + spr.fillSprite(TFT_WHITE); + float ax = axl.getAccelerationX(); + float ay = axl.getAccelerationY(); + float az = axl.getAccelerationZ(); + + if (accelerator_readings[0].size() == max_size) { + for (uint8_t i = 0; i<3; i++){ + accelerator_readings[i].pop(); + } + } + accelerator_readings[0].push(ax); + accelerator_readings[1].push(ay); + accelerator_readings[2].push(az); + + auto header = text(0, 0); + header.value("Acceleration Readings"); + header.align(center); + header.valign(vcenter); + header.width(tft.width()); + header.thickness(2); + header.height(header.font_height() * 2); + header.draw(); + + auto content = line_chart(20, header.height()); + content.height(tft.height() - header.height() * 1.5); + content.width(tft.width() - content.x() * 2); + content.based_on(-2.0); + content.show_circle(false); + content.value({accelerator_readings[0],accelerator_readings[1], accelerator_readings[2]}); + content.color(TFT_BLUE, TFT_RED, TFT_GREEN); + content.draw(); + spr.pushSprite(0, 0); + delay(50); } diff --git a/HelloWorld/seeed_graphics_base.cpp b/HelloWorld/seeed_graphics_base.cpp new file mode 100644 index 0000000..012687b --- /dev/null +++ b/HelloWorld/seeed_graphics_base.cpp @@ -0,0 +1,155 @@ +#include<math.h> +#include"seeed_graphics_base.h" + +std::vector<color_t> classic_colors { + yellow, + red, + green, + black, + blue, + darkcyan, + magenta, +}; + +constexpr double pi = 3.14159265358979323846; +extern TFT_eSprite spr; + +namespace detail { + void draw(void * poly, bool gen){ + detail::poly<polygen> & p = *(detail::poly<polygen> *)poly; + auto default_color = black; + auto default_thickness = pan_thickness; + auto & value = p.value(); + auto & color = p.color(); + auto & thickness = p.thickness(); + + if (value.size() <= 1) { + return; + } + for (size_t i = 1; i < value.size() + gen; i++){ + auto ai = (i - 1) % value.size(); + auto bi = (i) % value.size(); + auto a = value[ai]; + auto b = value[bi]; + if (i - 1 < color.size()){ + default_color = color[i - 1]; + } + if (i - 1 < thickness.size()){ + default_thickness = thickness[i - 1]; + } + line(a, b) + .color(default_color) + .thickness(default_thickness) + .draw(); + } + } +} + +void dot::draw() { + spr.drawPixel(_x, _y, _color); +} +void line::draw() { + spr.drawLine(_x0, _y0, _x1, _y1, _color); +} +void dash_line::draw() { + point origin(_x, _y); + point solid_offset; + point empty_offset; + point end; + if (_orientation == horizon) { + solid_offset = point(_solid, 0); + empty_offset = point(_empty, 0); + end = origin(_length - 1, 0); + } + else { + solid_offset = point(0, _solid); + empty_offset = point(0, _empty); + end = origin(0, _length - 1); + } + for (bool loop = true; loop;) { + auto next = origin(solid_offset); + if (next.x > end.x) { + next.x = end.x; + loop = false; + } + if (next.y > end.y) { + next.y = end.y; + loop = false; + } + line(origin, next) + .color(_color) + .thickness(_thickness) + .draw(); + origin = next(empty_offset); + } +} + +void rectangle::draw() { + auto origin = adjust(_width, _height); + auto left_top = origin; + auto left_bottom = origin(0, -(pos_t)_height + 1); + auto right_top = left_top(_width - 1, 0); + auto right_bottom = left_bottom(_width - 1, 0); + spr.drawRect(left_top.x, left_top.y, _width, _height, _color); + + if (_fill != transparent) { + spr.fillRect(left_top.x + 1, left_top.y + 1, _width - 2, _height - 2, _fill); + } +} + +void ellipse::draw() { + auto p = adjust(_width, _height); + if (_fill != transparent) { + spr.fillEllipse(_x, _y, _width / 2, _height / 2, _fill); + } + spr.drawEllipse(_x, _y, _width / 2, _height / 2, _color); +} +text & text::font(font_t value){ + _font = value; + spr.setTextFont(uint8_t(value)); + return this[0]; +} +text & text::thickness(pix_t value){ + _thickness = value; + spr.setTextSize(uint8_t(value)); + return this[0]; +} +text & text::font_height(pix_t * value) { + font(font()); + thickness(thickness()); + value[0] = spr.fontHeight(); + return this[0]; +} +text & text::content_width(pix_t * value) { + font(font()); + thickness(thickness()); + value[0] = spr.textWidth(_value); + return this[0]; +} + +void text::draw(){ + pix_t height = font_height(); + pix_t width = content_width(); + point p = adjust(width, height); + if (_align == center){ + p.x += (_width - width) / 2; + } + else if (_align == right){ + p.x += _width - width; + } + if (_valign == vcenter){ + p.y += (_height - height) / 2; + } + else if (_valign == bottom){ + p.y += _height - height; + } + spr.setTextColor(_color); + spr.drawString(_value, p.x, p.y); +} +void polyline::draw() { + detail::draw(this, false); +} +void polygen::draw() { + detail::draw(this, true); +} + diff --git a/HelloWorld/seeed_graphics_base.h b/HelloWorld/seeed_graphics_base.h new file mode 100644 index 0000000..994f1e2 --- /dev/null +++ b/HelloWorld/seeed_graphics_base.h @@ -0,0 +1,418 @@ +#pragma once +#include"seeed_graphics_define.h" + +#define xlist(...) __VA_ARGS__ +#define xprop(type,name,...) \ +protected: \ +type _ ## name; \ +public: \ +auto & name(type value) { \ + _ ## name = value; \ + return (__VA_ARGS__ this)[0]; \ +} \ +auto & name(type * value){ \ + value[0] = _ ## name; \ + return (__VA_ARGS__ this)[0]; \ +} \ +auto & name(){ \ + return _ ## name; \ +} + +#define xvprop(type,name,...) \ +protected: \ +std::vector<type> _ ## name; \ +public: \ +auto & name(type value) { \ + _ ## name = std::vector<type>{ value }; \ + return (__VA_ARGS__ this)[0]; \ +} \ +template<class ... arg> \ +auto & name(type value, arg ... list) { \ + _ ## name = std::vector<type>{ value, list... }; \ + return (__VA_ARGS__ this)[0]; \ +} \ +auto & name(std::vector<type> * value){ \ + value[0] = _ ## name; \ + return (__VA_ARGS__ this)[0]; \ +} \ +auto & name(){ \ + return _ ## name; \ +} + + +#define xpoint(type,...) \ +xprop(pos_t, x ## __VA_ARGS__); \ +xprop(pos_t, y ## __VA_ARGS__); \ +auto & xy ## __VA_ARGS__( \ + pos_t x, \ + pos_t y){ \ + this->x ## __VA_ARGS__(x); \ + this->y ## __VA_ARGS__(y); \ + return ((type *)this)[0]; \ +} \ +auto & xy ## __VA_ARGS__( \ + pos_t * x, \ + pos_t * y){ \ + this->x ## __VA_ARGS__(x); \ + this->y ## __VA_ARGS__(y); \ + return ((type *)this)[0]; \ +} \ +auto & xy ## __VA_ARGS__(point p){ \ + return xy ## __VA_ARGS__(p.x, p.y); \ +} \ +auto xy ## __VA_ARGS__(){ \ + return point(x ## __VA_ARGS__(), y ## __VA_ARGS__()); \ +} \ +auto & xy ## __VA_ARGS__(point * p){ \ + return xy ## __VA_ARGS__(& p->x, & p->y); \ +} + +#define xpositionx(struct_type,list,set,default_set) \ +struct_type() { \ + _x = 0; \ + _y = 0; \ + default_set; \ +} \ +struct_type(pos_t x, pos_t y) : \ + struct_type(){ \ + _x = x; \ + _y = y; \ +} \ +struct_type(point p) : \ + struct_type(p.x, p.y) { \ +} \ +struct_type(point p, list) : \ + struct_type(p.x, p.y) { \ + set; \ +} \ +struct_type(pos_t x, pos_t y, list) : \ + struct_type(x,y){ \ + set; \ +} \ + + +#define xposition(struct_type,...) \ +struct_type() { \ + _x = 0; \ + _y = 0; \ + __VA_ARGS__; \ +} \ +struct_type(pos_t x, pos_t y) : \ + struct_type(){ \ + _x = x; \ + _y = y; \ +} \ +struct_type(point p) : \ + struct_type(p.x, p.y) { \ +} \ + + +namespace detail { + template<class return_type> + struct poly{ + poly(){ + _color.push_back(pan_color); + _thickness.push_back(pan_thickness); + } + poly(std::initializer_list<point> const & value) : + poly(){ + _value = value; + } + xvprop(point, value, (return_type *)); + xvprop(color_t, color, (return_type *)); + xvprop(pix_t, thickness, (return_type*)); + }; + + template<class return_type> + struct aligner{ + aligner() : + _x(0), + _y(0), + _width(0), + _height(0), + _origin(left), + _vorigin(top){ + } + xpoint(return_type); + xprop(pix_t, width, (return_type*)); + xprop(pix_t, height, (return_type*)); + xprop(align_type, origin, (return_type*)); + xprop(valign_type, vorigin, (return_type*)); + //protected: + point adjust(pix_t width, pix_t height) { + auto x = _x; + auto y = _y; + + if (_width == 0) { + _width = width; + } + if (_height == 0) { + _height = height; + } + if (_origin == center) { + x -= width / 2; + } + else if (_origin == right) { + x -= width; + } + if (_vorigin == vcenter) { + y -= height / 2; + } + else if (_vorigin == bottom) { + y -= height; + } + return point(x, y); + } + }; +} + +struct dot{ + xposition(dot, { + _color = pan_color; + }); + xpoint(dot); + xprop(color_t, color); + void draw(); + operator can_drawable(){ + return can_drawable(this); + } +}; + +struct line{ + line(){ + _x0 = 0; + _y0 = 0; + _x1 = 0; + _y1 = 0; + _thickness = 1; + _color = black; + } + line(point p0, point p1) : + line(p0.x, p0.y, p1.x, p1.y){ + } + line(pos_t x0, pos_t y0, pos_t x1, pos_t y1): + line() { + _x0 = x0; + _y0 = y0; + _x1 = x1; + _y1 = y1; + } + xpoint(line, 0); + xpoint(line, 1); + xprop(color_t, color); + xprop(pix_t, thickness); + void draw(); + operator can_drawable(){ + return can_drawable(this); + } +}; + +struct rectangle; +struct rectangle : detail::aligner<rectangle>{ + xpositionx( + rectangle, xlist(pix_t width, pix_t height), { + _width = width; + _height = height; + }, { + _fill = transparent; + _color = pan_color; + _thickness_left = pan_thickness; + _thickness_top = pan_thickness; + _thickness_right = pan_thickness; + _thickness_bottom = pan_thickness; + }); + xprop(color_t, fill); + xprop(color_t, color); + xprop(pix_t, thickness_left); + xprop(pix_t, thickness_top); + xprop(pix_t, thickness_right); + xprop(pix_t, thickness_bottom); + rectangle & thickness(pix_t all){ + return thickness(all, all, all, all); + } + rectangle & thickness(pix_t left, pix_t top, pix_t right, pix_t bottom){ + _thickness_left = left; + _thickness_top = top; + _thickness_right = right; + _thickness_bottom = bottom; + return this[0]; + } + void draw(); + operator can_drawable() { + return can_drawable(this); + } +}; + +struct dash_line; +struct dash_line { + xpositionx(dash_line, + pix_t length, { + _length = length; + }, { + _length = 0; + _solid = 2; + _empty = 2; + _color = pan_color; + _thickness = pan_thickness; + _orientation = horizon; + }); + xpoint(dash_line); + xprop(pix_t, length); + xprop(pix_t, empty); + xprop(pix_t, solid); + xprop(color_t, color); + xprop(pix_t, thickness); + xprop(::orientation, orientation); + + void draw(); + operator can_drawable(){ + return can_drawable(this); + } +}; + +struct ellipse; +struct ellipse : detail::aligner<ellipse> { + ellipse() : + detail::aligner<ellipse>(){ + _color = pan_color; + _fill = transparent; + _thickness = pan_thickness; + _origin = center; + _vorigin = vcenter; + } + ellipse(pos_t x, pos_t y): + ellipse() { + _x = x; + _y = y; + } + ellipse(point p) : + ellipse(p.x, p.y) { + } + ellipse(point p, pix_t r) : + ellipse(p.x, p.y, r * 2, r * 2) { + } + ellipse(point p, pix_t width, pix_t height) : + ellipse(p.x, p.y, width, height) { + } + ellipse(pos_t x, pos_t y, pix_t r) : + ellipse(x, y, r * 2, r * 2) { + } + ellipse(pos_t x, pos_t y, pix_t width, pix_t height): + ellipse(x, y) { + _width = width; + _height = height; + } + xprop(color_t, color); + xprop(color_t, fill); + xprop(pix_t, thickness); + + auto & r(pix_t value) { + _height = _width = value * 2; + return this[0]; + } + void draw(); + operator can_drawable() { + return can_drawable(this); + } +protected: + point adjust(pix_t width, pix_t height) { + auto x = _x; + auto y = _y; + + if (_width == 0) { + _width = width; + } + if (_height == 0) { + _height = height; + } + if (_origin == left) { + x += _width / 2; + } + else if (_origin == right) { + x -= _width / 2; + } + if (_vorigin == top) { + y -= _height / 2; + } + else if (_vorigin == bottom) { + y += _height / 2; + } + return point(x, y); + } +}; + +struct text; +struct text : detail::aligner<text>{ + xpositionx(text, + text_t value, { + _value = value; + }, { + _value = ""; + _font = pix; + _font_size = 12; + _color = pan_color; + _align = left; + _valign = top; + _thickness = 1; + }); +private: + font_t _font; + pix_t _thickness; +public: + text & font(font_t value); + font_t font(){ + return _font; + } + text & thickness(pix_t value); + pix_t thickness(){ + return _thickness; + } + xprop(pix_t, font_size); + xprop(color_t, color); + xprop(text_t, value); + xprop(align_type, align); + xprop(valign_type, valign); + text & font_height(pix_t * value); + text & content_width(pix_t * value); + pix_t font_height() { + pix_t height; + font_height(& height); + return height; + } + pix_t content_width() { + pix_t width; + content_width(& width); + return width; + } + + void draw(); + operator can_drawable(){ + return can_drawable(this); + } +}; + +struct polyline; +struct polyline : detail::poly<polyline>{ + polyline(){} + polyline(std::initializer_list<point> data) : + detail::poly<polyline>(data){ + } + void draw(); + operator can_drawable() { + return can_drawable(this); + } +}; + +struct polygen; +struct polygen : detail::poly<polygen>{ + polygen() {} + polygen(std::initializer_list<point> data) : + detail::poly<polygen>(data) { + } + void draw(); + operator can_drawable() { + return can_drawable(this); + } +}; + + diff --git a/HelloWorld/seeed_graphics_define.h b/HelloWorld/seeed_graphics_define.h new file mode 100644 index 0000000..00b3ae6 --- /dev/null +++ b/HelloWorld/seeed_graphics_define.h @@ -0,0 +1,102 @@ +#pragma once +#define null16 32767 +#include<TFT_eSPI.h> +#pragma push(max) +#pragma push(min) +#undef max +#undef min +#include<initializer_list> +#include<list> +#include<vector> +#include<queue> +#include<stdint.h> +#pragma pop(min) +#pragma pop(max) +#include<string> + +typedef const char * text_t; +typedef int16_t pos_t; +typedef uint16_t pix_t; +typedef uint32_t color_t; +typedef std::queue<double> doubles; + +enum class font_t : uint16_t {}; +enum class align_type : uint8_t {}; +enum class valign_type : uint8_t {}; +enum class orientation : uint8_t {}; + +constexpr orientation horizon = orientation(0); +constexpr orientation vertical = orientation(1); + +constexpr align_type left = align_type(0); +constexpr align_type center = align_type(1); +constexpr align_type right = align_type(2); +constexpr valign_type top = valign_type(0); +constexpr valign_type vcenter = valign_type(1); +constexpr valign_type bottom = valign_type(2); + + +constexpr color_t black = 0x0000; /* 0, 0, 0 */ +constexpr color_t navy = 0x000F; /* 0, 0, 128 */ +constexpr color_t drakgreen = 0x03E0; /* 0, 128, 0 */ +constexpr color_t darkcyan = 0x03EF; /* 0, 128, 128 */ +constexpr color_t maroon = 0x7800; /* 128, 0, 0 */ +constexpr color_t purple = 0x780F; /* 128, 0, 128 */ +constexpr color_t olive = 0x7BE0; /* 128, 128, 0 */ +constexpr color_t lightgray = 0xC618; /* 192, 192, 192 */ +constexpr color_t darkgray = 0x7BEF; /* 128, 128, 128 */ +constexpr color_t gray = 0xCE79; +constexpr color_t blue = 0x001F; /* 0, 0, 255 */ +constexpr color_t green = 0x07E0; /* 0, 255, 0 */ +constexpr color_t cyan = 0x07FF; /* 0, 255, 255 */ +constexpr color_t red = 0xF800; /* 255, 0, 0 */ +constexpr color_t magenta = 0xF81F; /* 255, 0, 255 */ +constexpr color_t yellow = 0xFFE0; /* 255, 255, 0 */ +constexpr color_t white = 0xFFFF; /* 255, 255, 255 */ +constexpr color_t orange = 0xFDA0; /* 255, 180, 0 */ +constexpr color_t greenyellow = 0xB7E0; /* 180, 255, 0 */ +constexpr color_t pink = 0xFC9F; +constexpr color_t transparent = 0xFF000000; +constexpr color_t pan_color = color_t(black); +constexpr font_t pix = font_t(1); +constexpr pix_t pan_thickness = 1; + +struct can_drawable { + typedef void (can_drawable::*invoke_t)(); + typedef can_drawable * can_drawable_p; + template<class type> + can_drawable(type * obj){ + invoke = invoke_t(& type::draw); + object = can_drawable_p(obj); + } + void draw(){ + (object->*invoke)(); + } +private: + void empty(){} + invoke_t invoke; + can_drawable * object; +}; + +extern std::vector<color_t> classic_colors; + +struct point{ + pos_t x; + pos_t y; + point():x(0), y(0){} + point(pos_t x, pos_t y): + x(x), y(y){ + } + point operator()(const point & p){ + return point{ this->x + p.x, this->y + p.y }; + } + point operator()(pos_t x, pos_t y) { + return point{ this->x + x, this->y + y }; + } + point operator + (point value) { + return point{ this->x + value.x, this->y + value.y }; + } + point operator - (point value) { + return point{ this->x - value.x, this->y - value.y }; + } +}; 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 |
