UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

231 lines (200 loc) 7.86 kB
const shader = ` precision mediump float; const int butt_cap = 0; const int round_cap = 1; const int square_cap = 2; const int miter_join = 0; const int round_join = 1; const int bevel_join = 2; attribute vec2 a_position; attribute vec2 a_point_prev; attribute vec2 a_point_start; attribute vec2 a_point_end; attribute vec2 a_point_next; attribute float a_show_prev; attribute float a_show_curr; attribute float a_show_next; attribute float a_linewidth; attribute vec4 a_line_color; attribute float a_line_cap; attribute float a_line_join; #ifdef DASHED attribute float a_length_so_far; attribute vec4 a_dash_tex_info; attribute float a_dash_scale; attribute float a_dash_offset; #endif uniform vec2 u_canvas_size; uniform float u_antialias; uniform float u_miter_limit; varying float v_linewidth; varying vec4 v_line_color; varying float v_line_cap; varying float v_line_join; varying float v_segment_length; varying vec2 v_coords; varying float v_flags; // Boolean flags varying float v_cos_turn_angle_start; varying float v_cos_turn_angle_end; #ifdef DASHED varying float v_length_so_far; varying vec4 v_dash_tex_info; varying float v_dash_scale; varying float v_dash_offset; #endif #define SMALL 1e-6 float cross_z(in vec2 v0, in vec2 v1) { return v0.x*v1.y - v0.y*v1.x; } vec2 right_vector(in vec2 v) { return vec2(v.y, -v.x); } // Calculate cos/sin turn angle with adjacent segment, and unit normal vector to right float calc_turn_angle(in bool has_cap, in vec2 segment_right, in vec2 other_right, out vec2 point_right, out float sin_turn_angle) { float cos_turn_angle; vec2 diff = segment_right + other_right; float len = length(diff); if (has_cap || len < SMALL) { point_right = segment_right; cos_turn_angle = -1.0; // Turns back on itself. sin_turn_angle = 0.0; } else { point_right = diff / len; cos_turn_angle = dot(segment_right, other_right); // cos zero at +/-pi/2, +ve angle is turn right sin_turn_angle = cross_z(segment_right, other_right); } return cos_turn_angle; } // If miter too large use bevel join instead bool miter_too_large(in int join_type, in float cos_turn_angle) { float cos_half_angle_sqr = 0.5*(1.0 + cos_turn_angle); // Trig identity return join_type == miter_join && cos_half_angle_sqr < 1.0 / (u_miter_limit*u_miter_limit); } vec2 normalize_check_len(in vec2 vec, in float len) { if (abs(len) < SMALL) return vec2(1.0, 0.0); else return vec / len; } vec2 normalize_check(in vec2 vec) { return normalize_check_len(vec, length(vec)); } void main() { if (a_show_curr < 0.5) { // Line segment has non-finite value at one or both ends, do not render. gl_Position = vec4(-2.0, -2.0, 0.0, 1.0); return; } int join_type = int(a_line_join + 0.5); int cap_type = int(a_line_cap + 0.5); v_linewidth = a_linewidth; v_line_color = a_line_color; if (v_linewidth < 1.0) { // Linewidth less than 1 is implemented as 1 but with reduced alpha. v_line_color.a *= v_linewidth; v_linewidth = 1.0; } float halfwidth = 0.5*(v_linewidth + u_antialias); vec2 segment_along = a_point_end - a_point_start; v_segment_length = length(a_point_end - a_point_start); segment_along = normalize_check_len(segment_along, v_segment_length); // unit vector. vec2 segment_right = right_vector(segment_along); // unit vector. vec2 xy; // in screen coords vec2 prev_along = normalize_check(a_point_start - a_point_prev); vec2 prev_right = right_vector(prev_along); vec2 next_right = right_vector(normalize_check(a_point_next - a_point_end)); v_coords.y = a_position.y*halfwidth; // Overwritten later for join points. // Start and end cap properties bool has_start_cap = a_show_prev < 0.5; bool has_end_cap = a_show_next < 0.5; // Start and end join properties vec2 point_right_start, point_right_end; float sin_turn_angle_start, sin_turn_angle_end; v_cos_turn_angle_start = calc_turn_angle(has_start_cap, segment_right, prev_right, point_right_start, sin_turn_angle_start); v_cos_turn_angle_end = calc_turn_angle(has_end_cap, segment_right, next_right, point_right_end, sin_turn_angle_end); float sign_turn_right_start = sin_turn_angle_start >= 0.0 ? 1.0 : -1.0; bool miter_too_large_start = !has_start_cap && miter_too_large(join_type, v_cos_turn_angle_start); bool miter_too_large_end = !has_end_cap && miter_too_large(join_type, v_cos_turn_angle_end); float sign_at_start = -sign(a_position.x); // +ve at segment start, -ve end. vec2 point = sign_at_start > 0.0 ? a_point_start : a_point_end; if ( (has_start_cap && sign_at_start > 0.0) || (has_end_cap && sign_at_start < 0.0) ) { // Cap. xy = point - segment_right*(halfwidth*a_position.y); if (cap_type == butt_cap) xy -= sign_at_start*0.5*u_antialias*segment_along; else xy -= sign_at_start*halfwidth*segment_along; } else if (sign_at_start > 0.0) { vec2 inside_point = a_point_start + segment_right*(sign_turn_right_start*halfwidth); vec2 prev_outside_point = a_point_start - prev_right*(sign_turn_right_start*halfwidth); // join at start. if (join_type == round_join || join_type == bevel_join || miter_too_large_start) { if (v_cos_turn_angle_start <= 0.0) { // |turn_angle| > 90 degrees xy = a_point_start - segment_right*(halfwidth*a_position.y) - halfwidth*segment_along; } else { if (a_position.x < -1.5) { xy = prev_outside_point; v_coords.y = -dot(xy - a_point_start, segment_right); } else if (a_position.y*sign_turn_right_start > 0.0) { // outside corner of turn float d = halfwidth*abs(sin_turn_angle_start); xy = a_point_start - segment_right*(halfwidth*a_position.y) - d*segment_along; } else { // inside corner of turn xy = inside_point; } } } else { // miter join if (a_position.x < -1.5) { xy = prev_outside_point; v_coords.y = -dot(xy - a_point_start, segment_right); } else if (a_position.y*sign_turn_right_start > 0.0) { // outside corner of turn float tan_half_turn_angle = (1.0-v_cos_turn_angle_start) / sin_turn_angle_start; // Trig identity float d = sign_turn_right_start*halfwidth*tan_half_turn_angle; xy = a_point_start - segment_right*(halfwidth*a_position.y) - d*segment_along; } else { // inside corner if turn xy = inside_point; } } } else { xy = point - segment_right*(halfwidth*a_position.y); } vec2 pos = xy + 0.5; // Bokeh's offset. pos /= u_canvas_size; // in 0..1 gl_Position = vec4(2.0*pos.x - 1.0, 1.0 - 2.0*pos.y, 0.0, 1.0); bool turn_right_start = sin_turn_angle_start >= 0.0; bool turn_right_end = sin_turn_angle_end >= 0.0; v_coords.x = dot(xy - a_point_start, segment_along); v_flags = float(int(has_start_cap) + 2*int(has_end_cap) + 4*int(miter_too_large_start) + 8*int(miter_too_large_end) + 16*int(turn_right_start) + 32*int(turn_right_end)); v_line_cap = a_line_cap; v_line_join = a_line_join; #ifdef DASHED v_length_so_far = a_length_so_far; v_dash_tex_info = a_dash_tex_info; v_dash_scale = a_dash_scale; v_dash_offset = a_dash_offset; #endif } `; export default shader;