13 using namespace display;
15 static const char *
const TAG =
"graph";
16 static const char *
const TAGL =
"graphlegend";
20 this->samples_.resize(length, NAN);
21 this->last_sample_ =
millis();
26 uint32_t dt = tm - last_sample_;
31 while (this->period_ >= this->update_time_) {
32 this->samples_[this->count_] = data;
33 this->period_ -= this->update_time_;
34 this->count_ = (this->count_ + 1) % this->length_;
35 ESP_LOGV(TAG,
"Updating trace with value: %f", data);
37 if (!std::isnan(data)) {
39 this->recent_min_ = data;
40 this->recent_max_ = data;
41 for (
int i = 0; i < this->length_; i++) {
42 if (!std::isnan(this->samples_[i])) {
43 if (this->recent_max_ < this->samples_[i])
44 this->recent_max_ = this->samples_[i];
45 if (this->recent_min_ > this->samples_[i])
46 this->recent_min_ = this->samples_[i];
53 ESP_LOGI(TAG,
"Init trace for sensor %s", this->get_name().c_str());
55 sensor_->add_on_state_callback([
this](
float state) { this->data_.take_sample(state); });
63 buff->
horizontal_line(x_offset, y_offset + this->height_ - 1, this->width_, color);
64 buff->
vertical_line(x_offset, y_offset, this->height_, color);
65 buff->
vertical_line(x_offset + this->width_ - 1, y_offset, this->height_, color);
70 for (
auto *trace : traces_) {
71 float mx = trace->get_tracedata()->get_recent_max();
72 float mn = trace->get_tracedata()->get_recent_min();
73 if (std::isnan(ymax) || (ymax < mx))
75 if (std::isnan(ymin) || (ymin > mn))
79 if (!std::isnan(this->min_value_))
80 ymin = this->min_value_;
81 if (!std::isnan(this->max_value_))
82 ymax = this->max_value_;
84 float yrange = ymax - ymin;
85 if (yrange > this->max_range_) {
89 for (uint32_t i = 0; i < this->width_; i++) {
90 for (
auto *trace : traces_) {
91 float v = trace->get_tracedata()->get_value(i);
93 if ((v - mn) > this->max_range_)
95 if ((mx - v) > this->max_range_)
97 if (std::isnan(mx) || (v > mx))
99 if (std::isnan(mn) || (v < mn))
104 yrange = this->max_range_;
105 if (!std::isnan(mn)) {
107 ymax = ymin + this->max_range_;
109 ESP_LOGV(TAG,
"Graphing at max_range. Using local min %f, max %f", mn, mx);
112 float y_per_div = this->min_range_;
113 if (!std::isnan(this->gridspacing_y_)) {
114 y_per_div = this->gridspacing_y_;
117 if (yrange > 10 * y_per_div) {
118 while (yrange > 10 * y_per_div) {
121 ESP_LOGW(TAG,
"Graphing reducing y-scale to prevent too many gridlines");
127 if (!std::isnan(ymin) && !std::isnan(ymax)) {
128 yn = (int) floorf(ymin / y_per_div);
129 ym = (int) ceilf(ymax / y_per_div);
133 ymin = yn * y_per_div;
134 ymax = ym * y_per_div;
135 yrange = ymax - ymin;
139 if (!std::isnan(this->gridspacing_y_)) {
140 for (
int y = yn;
y <= ym;
y++) {
141 int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0 - (float) (
y - yn) / (ym - yn)));
142 for (uint32_t
x = 0;
x < this->width_;
x += 2) {
147 if (!std::isnan(this->gridspacing_x_) && (this->gridspacing_x_ > 0)) {
148 int n = this->duration_ / this->gridspacing_x_;
154 ESP_LOGW(TAG,
"Graphing reducing x-scale to prevent too many gridlines");
156 for (
int i = 0; i <= n; i++) {
157 for (uint32_t
y = 0;
y < this->height_;
y += 2) {
158 buff->
draw_pixel_at(x_offset + i * (this->width_ - 1) / n, y_offset +
y, color);
164 ESP_LOGV(TAG,
"Updating graph. ymin %f, ymax %f", ymin, ymax);
165 for (
auto *trace : traces_) {
166 Color c = trace->get_line_color();
167 int16_t thick = trace->get_line_thickness();
168 bool continuous = trace->get_continuous();
169 bool has_prev =
false;
172 for (uint32_t i = 0; i < this->width_; i++) {
173 float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange;
174 if (!std::isnan(v) && (thick > 0)) {
175 int16_t
x = this->width_ - 1 - i + x_offset;
177 bool b = (trace->get_line_type() & bit) == bit;
179 int16_t
y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2 + y_offset;
180 auto draw_pixel_at = [&buff, c, y_offset,
this](int16_t
x, int16_t
y) {
181 if (y >= y_offset && y < y_offset + this->height_)
184 if (!continuous || !has_prev || !prev_b || (abs(y - prev_y) <= thick)) {
185 for (int16_t t = 0; t < thick; t++) {
186 draw_pixel_at(x, y + t);
189 int16_t mid_y = (y + prev_y + thick) / 2;
191 for (int16_t t = prev_y + thick; t <= mid_y; t++)
192 draw_pixel_at(x + 1, t);
193 for (int16_t t = mid_y + 1; t < y + thick; t++)
196 for (int16_t t = prev_y - 1; t >= mid_y; t--)
197 draw_pixel_at(x + 1, t);
198 for (int16_t t = mid_y - 1; t >=
y; t--)
218 int txtw = 0, txth = 0;
219 int valw = 0, valh = 0;
221 for (
auto *trace : g->
traces_) {
222 std::string txtstr = trace->get_name();
223 int fw, fos, fbl, fh;
224 this->font_label_->measure(txtstr.c_str(), &fw, &fos, &fbl, &fh);
229 if (trace->get_line_thickness() > lt)
230 lt = trace->get_line_thickness();
231 ESP_LOGI(TAGL,
" %s %d %d", txtstr.c_str(), fw, fh);
234 std::stringstream ss;
235 ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state();
236 std::string valstr = ss.str();
238 valstr += trace->sensor_->get_unit_of_measurement();
240 this->font_value_->measure(valstr.c_str(), &fw, &fos, &fbl, &fh);
245 ESP_LOGI(TAGL,
" %s %d %d", valstr.c_str(), fw, fh);
253 uint16_t w = this->width_;
254 uint16_t
h = this->height_;
257 if (!this->font_value_) {
261 this->yl_ = txth + (txth / 4) + lt / 2;
276 this->yv_ = txth + (txth / 4);
278 this->yv_ += txth / 4 + lt;
280 this->xv_ = (txtw + valw) / 2;
285 this->x0_ = txtw / 2;
288 this->xs_ = std::max(txtw, valw);
290 this->x0_ = this->xs_ / 2;
292 this->xs_ = txtw + valw;
295 this->width_ = this->xs_;
297 this->width_ = this->xs_ * n;
301 this->x0_ = this->xs_ / 2;
307 this->ys_ = txth + txth / 2 + valh;
313 this->ys_ = std::max(txth + txth / 4 + lt + txth / 4, valh + valh / 4);
315 this->ys_ = std::max(txth + txth / 4, valh + valh / 4);
317 this->height_ = this->ys_ * n;
320 this->height_ = this->ys_;
322 this->height_ = this->ys_ * n;
341 int w = legend_->width_;
342 int h = legend_->height_;
349 int x = x_offset + legend_->x0_;
351 for (
auto *trace : traces_) {
352 std::string txtstr = trace->get_name();
353 ESP_LOGV(TAG,
" %s", txtstr.c_str());
355 buff->
printf(x, y, legend_->font_label_, trace->get_line_color(), TextAlign::TOP_CENTER,
"%s", txtstr.c_str());
357 if (legend_->lines_) {
358 uint16_t thick = trace->get_line_thickness();
359 for (
int i = 0; i < legend_->x0_ * 4 / 3; i++) {
361 if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) {
362 buff->
vertical_line(x - legend_->x0_ * 2 / 3 + i, y + legend_->yl_ - thick / 2, thick,
363 trace->get_line_color());
369 int xv = x + legend_->xv_;
370 int yv = y + legend_->yv_;
371 std::stringstream ss;
372 ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state();
373 std::string valstr = ss.str();
374 if (legend_->units_) {
375 valstr += trace->sensor_->get_unit_of_measurement();
377 buff->
printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER,
"%s", valstr.c_str());
378 ESP_LOGV(TAG,
" value: %s", valstr.c_str());
386 for (
auto *trace : traces_) {
392 for (
auto *trace : traces_) {
393 ESP_LOGCONFIG(TAG,
"Graph for sensor %s", trace->get_name().c_str());
void horizontal_line(int x, int y, int width, Color color=COLOR_ON)
Draw a horizontal line from the point [x,y] to [x+width,y] with the given color.
void take_sample(float data)
uint32_t IRAM_ATTR HOT millis()
std::vector< GraphTrace * > traces_
void dump_config() override
void draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color)
void init(Graph *g)
Determine the best coordinates of drawing text + lines.
void vertical_line(int x, int y, int height, Color color=COLOR_ON)
Draw a vertical line from the point [x,y] to [x,y+width] with the given color.
void draw_pixel_at(int x, int y)
Set a single pixel at the specified coordinates to default color.
void printf(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format,...) __attribute__((format(printf
Evaluate the printf-format format and print the result with the anchor point at [x,y] with font.
Implementation of SPI Controller mode.
void draw(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color)