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_base.cpp 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*********************
  2. * bed_mesh_base.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: <https://www.gnu.org/licenses/>. *
  19. ****************************************************************************/
  20. #include "../config.h"
  21. #include "screens.h"
  22. #ifdef FTDI_BED_MESH_BASE
  23. using namespace FTDI;
  24. void BedMeshBase::_drawMesh(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t opts, float autoscale_max, uint8_t highlightedTag, mesh_getter_ptr func, void *data) {
  25. constexpr uint8_t rows = GRID_MAX_POINTS_Y;
  26. constexpr uint8_t cols = GRID_MAX_POINTS_X;
  27. #define VALUE(X,Y) (func ? func(X,Y,data) : 0)
  28. #define ISVAL(X,Y) (func ? !isnan(VALUE(X,Y)) : true)
  29. #define HEIGHT(X,Y) (ISVAL(X,Y) ? (VALUE(X,Y) - val_min) * scale_z : 0)
  30. // Compute the mean, min and max for the points
  31. float val_mean = 0;
  32. float val_max = -INFINITY;
  33. float val_min = INFINITY;
  34. uint8_t val_cnt = 0;
  35. if (opts & USE_AUTOSCALE) {
  36. for (uint8_t y = 0; y < rows; y++) {
  37. for (uint8_t x = 0; x < cols; x++) {
  38. if (ISVAL(x,y)) {
  39. const float val = VALUE(x,y);
  40. val_mean += val;
  41. val_max = max(val_max, val);
  42. val_min = min(val_min, val);
  43. val_cnt++;
  44. }
  45. }
  46. }
  47. }
  48. if (val_cnt)
  49. val_mean /= val_cnt;
  50. else {
  51. val_mean = 0;
  52. val_min = 0;
  53. val_max = 0;
  54. }
  55. const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * autoscale_max;
  56. /**
  57. * The 3D points go through a 3D graphics pipeline to determine the final 2D point on the screen.
  58. * This is written out as a stack of macros that each apply an affine transformation to the point.
  59. * At compile time, the compiler should be able to reduce these expressions.
  60. *
  61. * The last transformation in the chain (TRANSFORM_5) is initially set to a no-op so we can measure
  62. * the dimensions of the grid, but is later replaced with a scaling transform that scales the grid
  63. * to fit.
  64. */
  65. #define TRANSFORM_5(X,Y,Z) (X), (Y) // No transform
  66. #define TRANSFORM_4(X,Y,Z) TRANSFORM_5((X)/(Z),(Y)/-(Z), 0) // Perspective
  67. #define TRANSFORM_3(X,Y,Z) TRANSFORM_4((X), (Z), (Y)) // Swap Z and Y
  68. #define TRANSFORM_2(X,Y,Z) TRANSFORM_3((X), (Y) + 2.5, (Z) - 1) // Translate
  69. #define TRANSFORM(X,Y,Z) TRANSFORM_2(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
  70. // Compute the bounding box for the grid prior to scaling. Do this at compile-time by
  71. // transforming the four corner points via the transformation equations and finding
  72. // the min and max for each axis.
  73. constexpr float bounds[][3] = {{TRANSFORM(0 , 0 , 0)},
  74. {TRANSFORM(cols-1, 0 , 0)},
  75. {TRANSFORM(0 , rows-1, 0)},
  76. {TRANSFORM(cols-1, rows-1, 0)}};
  77. #define APPLY(FUNC, AXIS) FUNC(FUNC(bounds[0][AXIS], bounds[1][AXIS]), FUNC(bounds[2][AXIS], bounds[3][AXIS]))
  78. constexpr float grid_x = APPLY(min,0);
  79. constexpr float grid_y = APPLY(min,1);
  80. constexpr float grid_w = APPLY(max,0) - grid_x;
  81. constexpr float grid_h = APPLY(max,1) - grid_y;
  82. constexpr float grid_cx = grid_x + grid_w/2;
  83. constexpr float grid_cy = grid_y + grid_h/2;
  84. // Figure out scale and offset such that the grid fits within the rectangle given by (x,y,w,h)
  85. const float scale_x = float(w)/grid_w;
  86. const float scale_y = float(h)/grid_h;
  87. const float center_x = x + w/2;
  88. const float center_y = y + h/2;
  89. // Now replace the last transformation in the chain with a scaling operation.
  90. #undef TRANSFORM_5
  91. #define TRANSFORM_6(X,Y,Z) (X)*16, (Y)*16 // Scale to 1/16 pixel units
  92. #define TRANSFORM_5(X,Y,Z) TRANSFORM_6( center_x + ((X) - grid_cx) * scale_x, \
  93. center_y + ((Y) - grid_cy) * scale_y, 0) // Scale to bounds
  94. // Draw the grid
  95. const uint16_t basePointSize = min(w,h) / max(cols,rows);
  96. cmd.cmd(SAVE_CONTEXT())
  97. .cmd(TAG_MASK(false))
  98. .cmd(SAVE_CONTEXT());
  99. for (uint8_t y = 0; y < rows; y++) {
  100. for (uint8_t x = 0; x < cols; x++) {
  101. if (ISVAL(x,y)) {
  102. const bool hasLeftSegment = x < cols - 1 && ISVAL(x+1,y);
  103. const bool hasRightSegment = y < rows - 1 && ISVAL(x,y+1);
  104. if (hasLeftSegment || hasRightSegment) {
  105. cmd.cmd(BEGIN(LINE_STRIP));
  106. if (hasLeftSegment) cmd.cmd(VERTEX2F(TRANSFORM(x + 1, y , HEIGHT(x + 1, y ))));
  107. cmd.cmd( VERTEX2F(TRANSFORM(x , y , HEIGHT(x , y ))));
  108. if (hasRightSegment) cmd.cmd(VERTEX2F(TRANSFORM(x , y + 1, HEIGHT(x , y + 1))));
  109. }
  110. }
  111. }
  112. if (opts & USE_POINTS) {
  113. const float sq_min = sq(val_min - val_mean);
  114. const float sq_max = sq(val_max - val_mean);
  115. cmd.cmd(POINT_SIZE(basePointSize * 2));
  116. cmd.cmd(BEGIN(POINTS));
  117. for (uint8_t x = 0; x < cols; x++) {
  118. if (ISVAL(x,y)) {
  119. if (opts & USE_COLORS) {
  120. const float val_dev = sq(VALUE(x, y) - val_mean);
  121. uint8_t r = 0, b = 0;
  122. //*(VALUE(x, y) < 0 ? &r : &b) = val_dev / sq_min * 0xFF;
  123. if (VALUE(x, y) < 0)
  124. r = val_dev / sq_min * 0xFF;
  125. else
  126. b = val_dev / sq_max * 0xFF;
  127. cmd.cmd(COLOR_RGB(0xFF - b, 0xFF - b - r, 0xFF - r));
  128. }
  129. cmd.cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
  130. }
  131. }
  132. if (opts & USE_COLORS) {
  133. cmd.cmd(RESTORE_CONTEXT())
  134. .cmd(SAVE_CONTEXT());
  135. }
  136. }
  137. }
  138. cmd.cmd(RESTORE_CONTEXT())
  139. .cmd(TAG_MASK(true));
  140. if (opts & USE_TAGS) {
  141. cmd.cmd(COLOR_MASK(false, false, false, false))
  142. .cmd(POINT_SIZE(basePointSize * 10))
  143. .cmd(BEGIN(POINTS));
  144. for (uint8_t y = 0; y < rows; y++) {
  145. for (uint8_t x = 0; x < cols; x++) {
  146. const uint8_t tag = pointToTag(x, y);
  147. cmd.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
  148. }
  149. }
  150. cmd.cmd(COLOR_MASK(true, true, true, true));
  151. }
  152. if (opts & USE_HIGHLIGHT) {
  153. const uint8_t tag = highlightedTag;
  154. xy_uint8_t pt;
  155. if (tagToPoint(tag, pt)) {
  156. cmd.cmd(COLOR_A(128))
  157. .cmd(POINT_SIZE(basePointSize * 6))
  158. .cmd(BEGIN(POINTS))
  159. .tag(tag).cmd(VERTEX2F(TRANSFORM(pt.x, pt.y, HEIGHT(pt.x, pt.y))));
  160. }
  161. }
  162. cmd.cmd(END());
  163. cmd.cmd(RESTORE_CONTEXT());
  164. }
  165. uint8_t BedMeshBase::pointToTag(uint8_t x, uint8_t y) {
  166. return x >= 0 && x < GRID_MAX_POINTS_X && y >= 0 && y < GRID_MAX_POINTS_Y ? y * (GRID_MAX_POINTS_X) + x + 10 : 0;
  167. }
  168. bool BedMeshBase::tagToPoint(uint8_t tag, xy_uint8_t &pt) {
  169. if (tag < 10) return false;
  170. pt.x = (tag - 10) % (GRID_MAX_POINTS_X);
  171. pt.y = (tag - 10) / (GRID_MAX_POINTS_X);
  172. return true;
  173. }
  174. void BedMeshBase::drawMeshBackground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h) {
  175. cmd.cmd(COLOR_RGB(Theme::bed_mesh_shadow_rgb));
  176. _drawMesh(cmd, x, y, w, h, USE_POINTS | USE_TAGS, 0.1, 0, nullptr, nullptr);
  177. }
  178. void BedMeshBase::drawMeshForeground(CommandProcessor &cmd, int16_t x, int16_t y, int16_t w, int16_t h, mesh_getter_ptr func, void *data, uint8_t highlightedTag, float progress) {
  179. constexpr float autoscale_max_amplitude = 0.03;
  180. cmd.cmd(COLOR_RGB(Theme::bed_mesh_lines_rgb));
  181. _drawMesh(cmd, x, y, w, h,
  182. USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (progress > 0.95 ? USE_COLORS : 0),
  183. autoscale_max_amplitude * progress,
  184. highlightedTag,
  185. func, data
  186. );
  187. }
  188. #endif // FTDI_BED_MESH_BASE