My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bed_mesh_screen.cpp 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /***********************
  2. * bed_mesh_screen.cpp *
  3. ***********************/
  4. /****************************************************************************
  5. * Written By Marcio Teixeira 2020 *
  6. * *
  7. * This program is free software: you can redistribute it and/or modify *
  8. * it under the terms of the GNU General Public License as published by *
  9. * the Free Software Foundation, either version 3 of the License, or *
  10. * (at your option) any later version. *
  11. * *
  12. * This program is distributed in the hope that it will be useful, *
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  15. * GNU General Public License for more details. *
  16. * *
  17. * To view a copy of the GNU General Public License, go to the following *
  18. * location: <http://www.gnu.org/licenses/>. *
  19. ****************************************************************************/
  20. #include "../config.h"
  21. #if BOTH(TOUCH_UI_FTDI_EVE, HAS_MESH)
  22. #include "screens.h"
  23. #include "screen_data.h"
  24. using namespace FTDI;
  25. using namespace Theme;
  26. using namespace ExtUI;
  27. #ifdef TOUCH_UI_PORTRAIT
  28. #define GRID_COLS 2
  29. #define GRID_ROWS 10
  30. #define MESH_POS BTN_POS(1, 2), BTN_SIZE(2,5)
  31. #define Z_LABEL_POS BTN_POS(1, 8), BTN_SIZE(1,1)
  32. #define Z_VALUE_POS BTN_POS(2, 8), BTN_SIZE(1,1)
  33. #define WAIT_POS BTN_POS(1, 8), BTN_SIZE(2,1)
  34. #define BACK_POS BTN_POS(1,10), BTN_SIZE(2,1)
  35. #else
  36. #define GRID_COLS 5
  37. #define GRID_ROWS 5
  38. #define MESH_POS BTN_POS(1,1), BTN_SIZE(3,5)
  39. #define Z_LABEL_POS BTN_POS(4,2), BTN_SIZE(2,1)
  40. #define Z_VALUE_POS BTN_POS(4,3), BTN_SIZE(2,1)
  41. #define WAIT_POS BTN_POS(4,2), BTN_SIZE(2,2)
  42. #define BACK_POS BTN_POS(4,5), BTN_SIZE(2,1)
  43. #endif
  44. void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts, float autoscale_max) {
  45. constexpr uint8_t rows = GRID_MAX_POINTS_Y;
  46. constexpr uint8_t cols = GRID_MAX_POINTS_X;
  47. #define VALUE(X,Y) (data ? data[X][Y] : 0)
  48. #define ISVAL(X,Y) (data ? !isnan(VALUE(X,Y)) : true)
  49. #define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0)
  50. // Compute the mean, min and max for the points
  51. float val_mean = 0;
  52. float val_max = -INFINITY;
  53. float val_min = INFINITY;
  54. uint8_t val_cnt = 0;
  55. if (data && (opts & USE_AUTOSCALE)) {
  56. for (uint8_t y = 0; y < rows; y++) {
  57. for (uint8_t x = 0; x < cols; x++) {
  58. if (ISVAL(x,y)) {
  59. const float val = VALUE(x,y);
  60. val_mean += val;
  61. val_max = max(val_max, val);
  62. val_min = min(val_min, val);
  63. val_cnt++;
  64. }
  65. }
  66. }
  67. }
  68. if (val_cnt) {
  69. val_mean /= val_cnt;
  70. } else {
  71. val_mean = 0;
  72. val_min = 0;
  73. val_max = 0;
  74. }
  75. const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * autoscale_max;
  76. // These equations determine the appearance of the grid on the screen.
  77. #define TRANSFORM_5(X,Y,Z) (X), (Y) // No transform
  78. #define TRANSFORM_4(X,Y,Z) TRANSFORM_5((X)/(Z),(Y)/-(Z), 0) // Perspective
  79. #define TRANSFORM_3(X,Y,Z) TRANSFORM_4((X), (Z), (Y)) // Swap Z and Y
  80. #define TRANSFORM_2(X,Y,Z) TRANSFORM_3((X), (Y) + 2.5, (Z) - 1) // Translate
  81. #define TRANSFORM(X,Y,Z) TRANSFORM_2(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
  82. // Compute the bounding box for the grid prior to scaling. Do this at compile-time by
  83. // transforming the four corner points via the transformation equations and finding
  84. // the min and max for each axis.
  85. constexpr float bounds[][3] = {{TRANSFORM(0 , 0 , 0)},
  86. {TRANSFORM(cols-1, 0 , 0)},
  87. {TRANSFORM(0 , rows-1, 0)},
  88. {TRANSFORM(cols-1, rows-1, 0)}};
  89. #define APPLY(FUNC, AXIS) FUNC(FUNC(bounds[0][AXIS], bounds[1][AXIS]), FUNC(bounds[2][AXIS], bounds[3][AXIS]))
  90. constexpr float grid_x = APPLY(min,0);
  91. constexpr float grid_y = APPLY(min,1);
  92. constexpr float grid_w = APPLY(max,0) - grid_x;
  93. constexpr float grid_h = APPLY(max,1) - grid_y;
  94. constexpr float grid_cx = grid_x + grid_w/2;
  95. constexpr float grid_cy = grid_y + grid_h/2;
  96. // Figure out scale and offset such that the grid fits within the rectangle given by (x,y,w,h)
  97. const float scale_x = float(w)/grid_w;
  98. const float scale_y = float(h)/grid_h;
  99. const float center_x = x + w/2;
  100. const float center_y = y + h/2;
  101. #undef TRANSFORM_5
  102. #define TRANSFORM_5(X,Y,Z) center_x + (X - grid_cx) * scale_x, center_y + (Y - grid_cy) * scale_y // Fit to bounds
  103. // Draw the grid
  104. const uint16_t basePointSize = min(w,h) / max(cols,rows);
  105. CommandProcessor cmd;
  106. cmd.cmd(SAVE_CONTEXT())
  107. .cmd(VERTEX_FORMAT(0))
  108. .cmd(TAG_MASK(false))
  109. .cmd(SAVE_CONTEXT());
  110. for (uint8_t y = 0; y < rows; y++) {
  111. for (uint8_t x = 0; x < cols; x++) {
  112. if (ISVAL(x,y)) {
  113. const bool hasLeftSegment = x < cols - 1 && ISVAL(x+1,y);
  114. const bool hasRightSegment = y < rows - 1 && ISVAL(x,y+1);
  115. if (hasLeftSegment || hasRightSegment) {
  116. cmd.cmd(BEGIN(LINE_STRIP));
  117. if (hasLeftSegment) cmd.cmd(VERTEX2F(TRANSFORM(x + 1, y , HEIGHT(x + 1, y ))));
  118. cmd.cmd( VERTEX2F(TRANSFORM(x , y , HEIGHT(x , y ))));
  119. if (hasRightSegment) cmd.cmd(VERTEX2F(TRANSFORM(x , y + 1, HEIGHT(x , y + 1))));
  120. }
  121. }
  122. }
  123. if (opts & USE_POINTS) {
  124. const float sq_min = sq(val_min - val_mean);
  125. const float sq_max = sq(val_max - val_mean);
  126. cmd.cmd(POINT_SIZE(basePointSize * 2));
  127. cmd.cmd(BEGIN(POINTS));
  128. for (uint8_t x = 0; x < cols; x++) {
  129. if (ISVAL(x,y)) {
  130. if (opts & USE_COLORS) {
  131. const float val_dev = VALUE(x, y) - val_mean;
  132. const uint8_t neg_byte = sq(val_dev) / (val_dev < 0 ? sq_min : sq_max) * 0xFF;
  133. const uint8_t pos_byte = 255 - neg_byte;
  134. cmd.cmd(COLOR_RGB(pos_byte, pos_byte, 0xFF));
  135. }
  136. cmd.cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
  137. }
  138. }
  139. if (opts & USE_COLORS) {
  140. cmd.cmd(RESTORE_CONTEXT())
  141. .cmd(SAVE_CONTEXT());
  142. }
  143. }
  144. }
  145. cmd.cmd(RESTORE_CONTEXT())
  146. .cmd(TAG_MASK(true));
  147. if (opts & USE_TAGS) {
  148. cmd.cmd(COLOR_MASK(false, false, false, false))
  149. .cmd(POINT_SIZE(basePointSize * 10))
  150. .cmd(BEGIN(POINTS));
  151. for (uint8_t y = 0; y < rows; y++) {
  152. for (uint8_t x = 0; x < cols; x++) {
  153. const uint8_t tag = pointToTag(x, y);
  154. cmd.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
  155. }
  156. }
  157. cmd.cmd(COLOR_MASK(true, true, true, true));
  158. }
  159. if (opts & USE_HIGHLIGHT) {
  160. const uint8_t tag = screen_data.BedMeshScreen.highlightedTag;
  161. uint8_t x, y;
  162. if (tagToPoint(tag, x, y)) {
  163. cmd.cmd(COLOR_A(128))
  164. .cmd(POINT_SIZE(basePointSize * 6))
  165. .cmd(BEGIN(POINTS))
  166. .tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
  167. }
  168. }
  169. cmd.cmd(END());
  170. cmd.cmd(RESTORE_CONTEXT());
  171. }
  172. uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) {
  173. return y * (GRID_MAX_POINTS_X) + x + 10;
  174. }
  175. bool BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) {
  176. if (tag < 10) return false;
  177. x = (tag - 10) % (GRID_MAX_POINTS_X);
  178. y = (tag - 10) / (GRID_MAX_POINTS_X);
  179. return true;
  180. }
  181. void BedMeshScreen::onEntry() {
  182. screen_data.BedMeshScreen.highlightedTag = 0;
  183. screen_data.BedMeshScreen.count = 0;
  184. BaseScreen::onEntry();
  185. }
  186. float BedMeshScreen::getHightlightedValue() {
  187. if (screen_data.BedMeshScreen.highlightedTag) {
  188. xy_uint8_t pt;
  189. tagToPoint(screen_data.BedMeshScreen.highlightedTag, pt.x, pt.y);
  190. return ExtUI::getMeshPoint(pt);
  191. }
  192. return NAN;
  193. }
  194. void BedMeshScreen::drawHighlightedPointValue() {
  195. char str[16];
  196. const float val = getHightlightedValue();
  197. const bool isGood = !isnan(val);
  198. if (isGood)
  199. dtostrf(val, 5, 3, str);
  200. else
  201. strcpy_P(str, PSTR("-"));
  202. CommandProcessor cmd;
  203. cmd.font(Theme::font_medium)
  204. .text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
  205. .text(Z_VALUE_POS, str)
  206. .colors(action_btn)
  207. .tag(1).button( BACK_POS, GET_TEXT_F(MSG_BACK))
  208. .tag(0);
  209. }
  210. void BedMeshScreen::onRedraw(draw_mode_t what) {
  211. #define _INSET_POS(x,y,w,h) x + min(w,h)/10, y + min(w,h)/10, w - min(w,h)/5, h - min(w,h)/5
  212. #define INSET_POS(pos) _INSET_POS(pos)
  213. if (what & BACKGROUND) {
  214. CommandProcessor cmd;
  215. cmd.cmd(CLEAR_COLOR_RGB(bg_color))
  216. .cmd(CLEAR(true,true,true));
  217. // Draw the shadow and tags
  218. cmd.cmd(COLOR_RGB(0x444444));
  219. BedMeshScreen::drawMesh(INSET_POS(MESH_POS), nullptr, USE_POINTS | USE_TAGS);
  220. cmd.cmd(COLOR_RGB(bg_text_enabled));
  221. }
  222. if (what & FOREGROUND) {
  223. constexpr float autoscale_max_amplitude = 0.03;
  224. const bool levelingFinished = screen_data.BedMeshScreen.count >= GRID_MAX_POINTS;
  225. const float levelingProgress = sq(float(screen_data.BedMeshScreen.count) / GRID_MAX_POINTS);
  226. if (levelingFinished) drawHighlightedPointValue();
  227. BedMeshScreen::drawMesh(INSET_POS(MESH_POS), ExtUI::getMeshArray(),
  228. USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0),
  229. autoscale_max_amplitude * levelingProgress
  230. );
  231. }
  232. }
  233. bool BedMeshScreen::onTouchStart(uint8_t tag) {
  234. screen_data.BedMeshScreen.highlightedTag = tag;
  235. return true;
  236. }
  237. bool BedMeshScreen::onTouchEnd(uint8_t tag) {
  238. switch (tag) {
  239. case 1:
  240. GOTO_PREVIOUS();
  241. return true;
  242. default:
  243. return false;
  244. }
  245. }
  246. void BedMeshScreen::onMeshUpdate(const int8_t, const int8_t, const float) {
  247. if (AT_SCREEN(BedMeshScreen))
  248. onRefresh();
  249. }
  250. void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
  251. if (state == ExtUI::PROBE_FINISH) {
  252. screen_data.BedMeshScreen.highlightedTag = pointToTag(x, y);
  253. screen_data.BedMeshScreen.count++;
  254. }
  255. BedMeshScreen::onMeshUpdate(x, y, 0);
  256. }
  257. #endif // TOUCH_UI_FTDI_EVE && HAS_MESH