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;
}