CanvasRuler.gdshader 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. shader_type canvas_item;
  2. const float line_width = 1.0f;
  3. const float interval_step[8] = float[8](10.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 5000.0, 10000.0);
  4. // 标尺参数
  5. uniform bool is_horizontal;
  6. uniform vec2 ruler_size;
  7. uniform vec4 ruler_range;
  8. uniform vec4 highlight_range;
  9. uniform vec2 mouse_pos;
  10. uniform vec4 mouse_line_color : source_color = vec4(0.5, 0.5, 0.5, 1.0);
  11. uniform vec4 line_color : source_color = vec4(0.5, 0.5, 0.5, 1.0);
  12. uniform vec4 highlight_color : source_color = vec4(0.5, 0.5, 0.5, 1.0);
  13. uniform vec4 bg_color : source_color = vec4(0.2, 0.2, 0.2, 1.0);
  14. varying vec2 pixel_pos;
  15. int quick_digit_count(int number) {
  16. int absNum = abs(number);
  17. int count = 0;
  18. if (absNum >= 100000) count = 6;
  19. else if (absNum >= 10000) count = 5;
  20. else if (absNum >= 1000) count = 4;
  21. else if (absNum >= 100) count = 3;
  22. else if (absNum >= 10) count = 2;
  23. else count = 1;
  24. if (number < 0) count++;
  25. return count;
  26. }
  27. // ###########################################################################
  28. // Printing numbers
  29. // Modify according to the https://www.shadertoy.com/view/XllSWl
  30. float DigitBin(const in int x) {
  31. return x==0?480599.0:x==1?139810.0:x==2?476951.0:x==3?476999.0:x==4?350020.0:x==5?464711.0:x==6?464727.0:x==7?476228.0:x==8?481111.0:x==9?481095.0:0.0;
  32. }
  33. float PrintValue(const in vec2 fragCoord, const in vec2 vPixelCoords, const in vec2 vFontSize, const in float fValue) {
  34. vec2 vStringCharCoords = (vec2(fragCoord.x, 1.0 - fragCoord.y).xy - vPixelCoords) / vFontSize;
  35. if ((vStringCharCoords.y < 0.0) || (vStringCharCoords.y >= 1.0)) return 0.0;
  36. float fLog10Value = log2(abs(fValue)) / log2(10.0);
  37. float fBiggestIndex = max(floor(fLog10Value), 0.0);
  38. float fDigitIndex = floor(vStringCharCoords.x);
  39. float fCharBin = 0.0;
  40. float fNumStartIndex = (fValue < 0.0) ? 1.0 : 0.0;
  41. if(fValue < 0.0 && fDigitIndex == 0.0) {
  42. fCharBin = 1792.0;
  43. }
  44. else if (fDigitIndex >= fNumStartIndex && fDigitIndex <= fBiggestIndex + fNumStartIndex) {
  45. float fActualDigitIndex = fBiggestIndex - (fDigitIndex - fNumStartIndex);
  46. float fDigitValue = abs(fValue / pow(10.0, fActualDigitIndex));
  47. fCharBin = DigitBin(int(floor(mod(0.0001 + fDigitValue, 10.0))));
  48. } else {
  49. return 0.0;
  50. }
  51. return floor(mod((fCharBin / pow(2.0, floor(fract(vStringCharCoords.x) * 4.0) + (floor(vStringCharCoords.y * 5.0) * 4.0))), 2.0));
  52. }
  53. // ###########################################################################
  54. float affine_transform(float x, float a, float b, float p, float q) {
  55. return p + (x - a) * (q - p) / (b - a);
  56. }
  57. float grid_align_floor(float x, int step_size) {
  58. return floor(x / float(step_size)) * float(step_size);
  59. }
  60. float interval_round(float input_value) {
  61. float closest = interval_step[0];
  62. for (int i = 1; i < 8; i++) {
  63. if (abs(input_value - interval_step[i]) < abs(input_value - closest)) {
  64. closest = interval_step[i];
  65. }
  66. }
  67. return closest;
  68. }
  69. int in_line(vec2 p1, vec2 p2, vec2 m) {
  70. vec2 v = p2 - p1, w = m - p1;
  71. float sq_len = dot(v, v);
  72. if (sq_len < 0.001) return length(w) <= line_width ? 0 : -1;
  73. float t = clamp(dot(w, v) / sq_len, 0.0, 1.0);
  74. return length(m - (p1 + t * v)) <= line_width ? 0 : -1;
  75. }
  76. float[21] get_mark_line_1(float min_value, float max_value, float interval) {
  77. float result[21];
  78. float pin = grid_align_floor(min_value - interval, int(interval));
  79. for(int i = 0; i < 21; i++) {
  80. result[i] = pin + interval * float(i);
  81. }
  82. return result;
  83. }
  84. float[20] get_mark_line_2(float[21] mark_line_1) {
  85. float result[20];
  86. for(int i = 0; i < 20; i++) {
  87. result[i] = (mark_line_1[i] + mark_line_1[i + 1]) / 2.0;
  88. }
  89. return result;
  90. }
  91. float[160] get_mark_line_3(float[21] mark_line_1, float[20] mark_line_2) {
  92. float mark_line[41];
  93. int i = 0, j = 0, k = 0;
  94. while (i < 21 && j < 20) {
  95. if (mark_line_1[i] < mark_line_2[j]) {
  96. mark_line[k++] = mark_line_1[i++];
  97. } else {
  98. mark_line[k++] = mark_line_2[j++];
  99. }
  100. }
  101. while (i < 21) mark_line[k++] = mark_line_1[i++];
  102. while (j < 20) mark_line[k++] = mark_line_2[j++];
  103. float result[160];
  104. for(int i = 0; i < 40; i++) {
  105. result[i * 4] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.2;
  106. result[i * 4 + 1] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.4;
  107. result[i * 4 + 2] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.6;
  108. result[i * 4 + 3] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.8;
  109. }
  110. return result;
  111. }
  112. int detect_ruler_pixel(vec2 pos) {
  113. // 选择坐标轴相关的参数
  114. float mouse_coord = is_horizontal ? mouse_pos.x : mouse_pos.y;
  115. float range_start = is_horizontal ? ruler_range.x : ruler_range.y;
  116. float range_end = is_horizontal ? ruler_range.z : ruler_range.w;
  117. float ruler_length = is_horizontal ? ruler_size.x : ruler_size.y;
  118. float highlight_start = is_horizontal ? highlight_range.x : highlight_range.y;
  119. float highlight_end = is_horizontal ? highlight_range.z : highlight_range.w;
  120. // 鼠标位置线检测
  121. float t = affine_transform(mouse_coord, range_start, range_end, 0.0, ruler_length);
  122. if (is_horizontal) {
  123. if (in_line(vec2(t, 0.0), vec2(t, ruler_size.y), pos) >= 0) return 2;
  124. } else {
  125. if (in_line(vec2(0.0, t), vec2(ruler_size.x, t), pos) >= 0) return 2;
  126. }
  127. // 标记线检测, 标线间隔根据横向范围决定
  128. float interval = interval_round(abs(ruler_range.z - ruler_range.x) / 7.0);
  129. float detect_mark1[21] = get_mark_line_1(range_start, range_end, interval);
  130. for (int i = 0; i < 21; i++) {
  131. // 主线检测
  132. t = affine_transform(detect_mark1[i], range_start, range_end, 0.0, ruler_length);
  133. if (is_horizontal) {
  134. if (in_line(vec2(t, 0.0), vec2(t, ruler_size.y), pos) >= 0) return 1;
  135. } else {
  136. if (in_line(vec2(0.0, t), vec2(ruler_size.x, t), pos) >= 0) return 1;
  137. }
  138. // 标签检测
  139. float fIsDigit1;
  140. if (is_horizontal) {
  141. vec2 vFontSize = vec2(8.0, 15.0);
  142. fIsDigit1 = PrintValue(pos, vec2(t + 4.0, -vFontSize.y - 2.0), vFontSize, detect_mark1[i]);
  143. } else {
  144. int d = quick_digit_count(int(detect_mark1[i]));
  145. vec2 vFontSize;
  146. if (d <= 3) vFontSize = vec2(8.0, 15.0);
  147. else if (d <= 5) vFontSize = vec2(6.0, 15.0);
  148. else vFontSize = vec2(5.0, 15.0);
  149. fIsDigit1 = PrintValue(pos, vec2(2.0, -t - vFontSize.y - 2.0), vFontSize, detect_mark1[i]);
  150. }
  151. if (fIsDigit1 > 0.0) return 1;
  152. }
  153. // 二级标记线
  154. float detect_mark2[20] = get_mark_line_2(detect_mark1);
  155. for (int i = 0; i < 20; i++) {
  156. if (detect_mark2[i] < range_start || detect_mark2[i] > range_end) continue;
  157. t = affine_transform(detect_mark2[i], range_start, range_end, 0.0, ruler_length);
  158. if (is_horizontal) {
  159. if (in_line(vec2(t, ruler_size.y * 0.5), vec2(t, ruler_size.y), pos) >= 0) return 1;
  160. } else {
  161. if (in_line(vec2(ruler_size.x * 0.5, t), vec2(ruler_size.x, t), pos) >= 0) return 1;
  162. }
  163. }
  164. // 三级标记线
  165. float detect_mark3[160] = get_mark_line_3(detect_mark1, detect_mark2);
  166. for (int i = 0; i < 160; i++) {
  167. if (detect_mark3[i] < range_start || detect_mark3[i] > range_end) continue;
  168. t = affine_transform(detect_mark3[i], range_start, range_end, 0.0, ruler_length);
  169. if (is_horizontal) {
  170. if (in_line(vec2(t, ruler_size.y * 0.75), vec2(t, ruler_size.y), pos) >= 0) return 1;
  171. } else {
  172. if (in_line(vec2(ruler_size.x * 0.75, t), vec2(ruler_size.x, t), pos) >= 0) return 1;
  173. }
  174. }
  175. // 高亮区域检测
  176. float coord = is_horizontal ? affine_transform(pos.x, 0.0, ruler_size.x, range_start, range_end)
  177. : affine_transform(pos.y, 0.0, ruler_size.y, range_start, range_end);
  178. if (highlight_start <= coord && coord <= highlight_end) return 0;
  179. // 背景
  180. return -1;
  181. }
  182. // ###########################################################################
  183. // ###########################################################################
  184. // ###########################################################################
  185. // ###########################################################################
  186. void vertex() {
  187. pixel_pos = (CANVAS_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
  188. }
  189. void fragment() {
  190. int point_mode = detect_ruler_pixel(pixel_pos);
  191. if (point_mode == 2) COLOR = mouse_line_color;
  192. else if (point_mode == 1) COLOR = line_color;
  193. else if (point_mode == 0) COLOR = highlight_color;
  194. else COLOR = bg_color;
  195. }