shader_type canvas_item; const float line_width = 1.0f; const float interval_step[8] = float[8](10.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 5000.0, 10000.0); // 标尺参数 uniform bool is_horizontal; uniform vec2 ruler_size; uniform vec4 ruler_range; uniform vec4 highlight_range; uniform vec2 mouse_pos; uniform vec4 mouse_line_color : source_color = vec4(0.5, 0.5, 0.5, 1.0); uniform vec4 line_color : source_color = vec4(0.5, 0.5, 0.5, 1.0); uniform vec4 highlight_color : source_color = vec4(0.5, 0.5, 0.5, 1.0); uniform vec4 bg_color : source_color = vec4(0.2, 0.2, 0.2, 1.0); varying vec2 pixel_pos; int quick_digit_count(int number) { int absNum = abs(number); int count = 0; if (absNum >= 100000) count = 6; else if (absNum >= 10000) count = 5; else if (absNum >= 1000) count = 4; else if (absNum >= 100) count = 3; else if (absNum >= 10) count = 2; else count = 1; if (number < 0) count++; return count; } // ########################################################################### // Printing numbers // Modify according to the https://www.shadertoy.com/view/XllSWl float DigitBin(const in int x) { 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; } float PrintValue(const in vec2 fragCoord, const in vec2 vPixelCoords, const in vec2 vFontSize, const in float fValue) { vec2 vStringCharCoords = (vec2(fragCoord.x, 1.0 - fragCoord.y).xy - vPixelCoords) / vFontSize; if ((vStringCharCoords.y < 0.0) || (vStringCharCoords.y >= 1.0)) return 0.0; float fLog10Value = log2(abs(fValue)) / log2(10.0); float fBiggestIndex = max(floor(fLog10Value), 0.0); float fDigitIndex = floor(vStringCharCoords.x); float fCharBin = 0.0; float fNumStartIndex = (fValue < 0.0) ? 1.0 : 0.0; if(fValue < 0.0 && fDigitIndex == 0.0) { fCharBin = 1792.0; } else if (fDigitIndex >= fNumStartIndex && fDigitIndex <= fBiggestIndex + fNumStartIndex) { float fActualDigitIndex = fBiggestIndex - (fDigitIndex - fNumStartIndex); float fDigitValue = abs(fValue / pow(10.0, fActualDigitIndex)); fCharBin = DigitBin(int(floor(mod(0.0001 + fDigitValue, 10.0)))); } else { return 0.0; } return floor(mod((fCharBin / pow(2.0, floor(fract(vStringCharCoords.x) * 4.0) + (floor(vStringCharCoords.y * 5.0) * 4.0))), 2.0)); } // ########################################################################### float affine_transform(float x, float a, float b, float p, float q) { return p + (x - a) * (q - p) / (b - a); } float grid_align_floor(float x, int step_size) { return floor(x / float(step_size)) * float(step_size); } float interval_round(float input_value) { float closest = interval_step[0]; for (int i = 1; i < 8; i++) { if (abs(input_value - interval_step[i]) < abs(input_value - closest)) { closest = interval_step[i]; } } return closest; } int in_line(vec2 p1, vec2 p2, vec2 m) { vec2 v = p2 - p1, w = m - p1; float sq_len = dot(v, v); if (sq_len < 0.001) return length(w) <= line_width ? 0 : -1; float t = clamp(dot(w, v) / sq_len, 0.0, 1.0); return length(m - (p1 + t * v)) <= line_width ? 0 : -1; } float[21] get_mark_line_1(float min_value, float max_value, float interval) { float result[21]; float pin = grid_align_floor(min_value - interval, int(interval)); for(int i = 0; i < 21; i++) { result[i] = pin + interval * float(i); } return result; } float[20] get_mark_line_2(float[21] mark_line_1) { float result[20]; for(int i = 0; i < 20; i++) { result[i] = (mark_line_1[i] + mark_line_1[i + 1]) / 2.0; } return result; } float[160] get_mark_line_3(float[21] mark_line_1, float[20] mark_line_2) { float mark_line[41]; int i = 0, j = 0, k = 0; while (i < 21 && j < 20) { if (mark_line_1[i] < mark_line_2[j]) { mark_line[k++] = mark_line_1[i++]; } else { mark_line[k++] = mark_line_2[j++]; } } while (i < 21) mark_line[k++] = mark_line_1[i++]; while (j < 20) mark_line[k++] = mark_line_2[j++]; float result[160]; for(int i = 0; i < 40; i++) { result[i * 4] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.2; result[i * 4 + 1] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.4; result[i * 4 + 2] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.6; result[i * 4 + 3] = mark_line[i] + (mark_line[i + 1] - mark_line[i]) * 0.8; } return result; } int detect_ruler_pixel(vec2 pos) { // 选择坐标轴相关的参数 float mouse_coord = is_horizontal ? mouse_pos.x : mouse_pos.y; float range_start = is_horizontal ? ruler_range.x : ruler_range.y; float range_end = is_horizontal ? ruler_range.z : ruler_range.w; float ruler_length = is_horizontal ? ruler_size.x : ruler_size.y; float highlight_start = is_horizontal ? highlight_range.x : highlight_range.y; float highlight_end = is_horizontal ? highlight_range.z : highlight_range.w; // 鼠标位置线检测 float t = affine_transform(mouse_coord, range_start, range_end, 0.0, ruler_length); if (is_horizontal) { if (in_line(vec2(t, 0.0), vec2(t, ruler_size.y), pos) >= 0) return 2; } else { if (in_line(vec2(0.0, t), vec2(ruler_size.x, t), pos) >= 0) return 2; } // 标记线检测, 标线间隔根据横向范围决定 float interval = interval_round(abs(ruler_range.z - ruler_range.x) / 7.0); float detect_mark1[21] = get_mark_line_1(range_start, range_end, interval); for (int i = 0; i < 21; i++) { // 主线检测 t = affine_transform(detect_mark1[i], range_start, range_end, 0.0, ruler_length); if (is_horizontal) { if (in_line(vec2(t, 0.0), vec2(t, ruler_size.y), pos) >= 0) return 1; } else { if (in_line(vec2(0.0, t), vec2(ruler_size.x, t), pos) >= 0) return 1; } // 标签检测 float fIsDigit1; if (is_horizontal) { vec2 vFontSize = vec2(8.0, 15.0); fIsDigit1 = PrintValue(pos, vec2(t + 4.0, -vFontSize.y - 2.0), vFontSize, detect_mark1[i]); } else { int d = quick_digit_count(int(detect_mark1[i])); vec2 vFontSize; if (d <= 3) vFontSize = vec2(8.0, 15.0); else if (d <= 5) vFontSize = vec2(6.0, 15.0); else vFontSize = vec2(5.0, 15.0); fIsDigit1 = PrintValue(pos, vec2(2.0, -t - vFontSize.y - 2.0), vFontSize, detect_mark1[i]); } if (fIsDigit1 > 0.0) return 1; } // 二级标记线 float detect_mark2[20] = get_mark_line_2(detect_mark1); for (int i = 0; i < 20; i++) { if (detect_mark2[i] < range_start || detect_mark2[i] > range_end) continue; t = affine_transform(detect_mark2[i], range_start, range_end, 0.0, ruler_length); if (is_horizontal) { if (in_line(vec2(t, ruler_size.y * 0.5), vec2(t, ruler_size.y), pos) >= 0) return 1; } else { if (in_line(vec2(ruler_size.x * 0.5, t), vec2(ruler_size.x, t), pos) >= 0) return 1; } } // 三级标记线 float detect_mark3[160] = get_mark_line_3(detect_mark1, detect_mark2); for (int i = 0; i < 160; i++) { if (detect_mark3[i] < range_start || detect_mark3[i] > range_end) continue; t = affine_transform(detect_mark3[i], range_start, range_end, 0.0, ruler_length); if (is_horizontal) { if (in_line(vec2(t, ruler_size.y * 0.75), vec2(t, ruler_size.y), pos) >= 0) return 1; } else { if (in_line(vec2(ruler_size.x * 0.75, t), vec2(ruler_size.x, t), pos) >= 0) return 1; } } // 高亮区域检测 float coord = is_horizontal ? affine_transform(pos.x, 0.0, ruler_size.x, range_start, range_end) : affine_transform(pos.y, 0.0, ruler_size.y, range_start, range_end); if (highlight_start <= coord && coord <= highlight_end) return 0; // 背景 return -1; } // ########################################################################### // ########################################################################### // ########################################################################### // ########################################################################### void vertex() { pixel_pos = (CANVAS_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy; } void fragment() { int point_mode = detect_ruler_pixel(pixel_pos); if (point_mode == 2) COLOR = mouse_line_color; else if (point_mode == 1) COLOR = line_color; else if (point_mode == 0) COLOR = highlight_color; else COLOR = bg_color; }