diff --git a/manimlib/shaders/inserts/get_xyz_to_uv.glsl b/manimlib/shaders/inserts/get_xyz_to_uv.glsl index 86e62012..dc22dd5b 100644 --- a/manimlib/shaders/inserts/get_xyz_to_uv.glsl +++ b/manimlib/shaders/inserts/get_xyz_to_uv.glsl @@ -65,38 +65,40 @@ mat4 map_onto_x_axis(vec3 src0, vec3 src1){ if(vect.x > 1 - 1e-6) return shift; // Equivalent to cross(vect, vec3(1, 0, 0)) - vec3 axis = vec3(0.0, vect.z, -vect.y); - mat4 rotate = rotation(normalize(axis), vect.x); + vec3 axis = normalize(vec3(0.0, vect.z, -vect.y)); + mat4 rotate = rotation(axis, vect.x); return rotate * shift; } -mat4 get_xyz_to_uv(vec3 b0, vec3 b1, vec3 b2, float temp_is_linear, out float is_linear){ +mat4 get_xyz_to_uv( + vec3 b0, vec3 b1, vec3 b2, + float threshold, + out bool exceeds_threshold +){ /* - Returns a matrix for an affine transformation which maps a set of quadratic - bezier controls points into a new coordinate system such that the bezier curve - coincides with y = x^2, or in the case of a linear curve, it's mapped to the x-axis. + Populates the matrix `result` with an affine transformation which maps a set of + quadratic bezier controls points into a new coordinate system such that the bezier + curve coincides with y = x^2. + + If the x-range under this part of the curve exceeds `threshold`, this returns false + and populates result a matrix mapping b0 and b2 onto the x-axis */ - is_linear = temp_is_linear; + vec2 xs = xs_on_clean_parabola(b0, b1, b2); + float x0 = xs[0]; + float x1 = 0.5 * (xs[0] + xs[1]); + float x2 = xs[1]; // Portions of the parabola y = x^2 where abs(x) exceeds // this value are treated as straight lines. - float thresh = 2.0; - if (!bool(is_linear)){ - vec2 xs = xs_on_clean_parabola(b0, b1, b2); - float x0 = xs.x; - float x2 = xs.y; - if((x0 > thresh && x2 > thresh) || (x0 < -thresh && x2 < -thresh)){ - is_linear = 1.0; - }else{ - // This triangle on the xy plane should be isometric - // to (b0, b1, b2), and it should define a quadratic - // bezier segment aligned with y = x^2 - vec3 dst0 = vec3(x0, x0 * x0, 0.0); - vec3 dst1 = vec3(0.5 * (x0 + x2), x0 * x2, 0.0); - vec3 dst2 = vec3(x2, x2 * x2, 0.0); - return map_triangles(b0, b1, b2, dst0, dst1, dst2); - } + exceeds_threshold = (min(x0, x2) > threshold || max(x0, x2) < -threshold); + if(exceeds_threshold){ + return map_onto_x_axis(b0, b2); } - // Only lands here if is_linear is 1.0 - return map_onto_x_axis(b0, b2); + // This triangle on the xy plane should be isometric + // to (b0, b1, b2), and it should define a quadratic + // bezier segment aligned with y = x^2 + vec3 dst0 = vec3(x0, x0 * x0, 0.0); + vec3 dst1 = vec3(x1, x0 * x2, 0.0); + vec3 dst2 = vec3(x2, x2 * x2, 0.0); + return map_triangles(b0, b1, b2, dst0, dst1, dst2); } diff --git a/manimlib/shaders/quadratic_bezier_fill/geom.glsl b/manimlib/shaders/quadratic_bezier_fill/geom.glsl index 5f9f8ab6..eaeb6d6f 100644 --- a/manimlib/shaders/quadratic_bezier_fill/geom.glsl +++ b/manimlib/shaders/quadratic_bezier_fill/geom.glsl @@ -79,11 +79,13 @@ void emit_pentagon( } // Compute xy_to_uv matrix, and potentially re-evaluate bezier degree - mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, is_linear, is_linear); + bool too_steep; + mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, 10.0, too_steep); + if(too_steep) is_linear = 1.0; uv_anti_alias_width = aaw * length(xyz_to_uv[0].xyz); for(int i = 0; i < 5; i++){ - int j = int(sign(i - 1) + 1); // Maps i = [0, 1, 2, 3, 4] onto j = [0, 0, 1, 2, 2] + int j = int[5](0, 0, 1, 2, 2)[i]; vec3 corner = corners[i]; uv_coords = (xyz_to_uv * vec4(corner, 1.0)).xy; emit_vertex_wrapper(corner, j, unit_normal); diff --git a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl index f19484cb..1a9e1357 100644 --- a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl +++ b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl @@ -49,7 +49,7 @@ vec3 get_joint_unit_normal(vec4 joint_product){ result = v_joint_product[1].xyz; } float norm = length(result); - return (norm > 1e-10) ? result / norm : vec3(0.0, 0.0, 1.0); + return (norm > 1e-5) ? result / norm : vec3(0.0, 0.0, 1.0); } @@ -171,28 +171,40 @@ void main() { float cos_angle = v_joint_product[1].w; is_linear = float(cos_angle > COS_THRESHOLD); - // If the curve is flat, put the middle control in the midpoint - if (bool(is_linear)) p1 = 0.5 * (p0 + p2); - // We want to change the coordinates to a space where the curve // coincides with y = x^2, between some values x0 and x2. Or, in - // the case of a linear curve (bezier degree 1), just put it on - // the x-axis - mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, is_linear, is_linear); + // the case of a linear curve just put it on the x-axis + mat4 xyz_to_uv; + float uv_scale_factor; + if(!bool(is_linear)){ + bool too_steep; + xyz_to_uv = get_xyz_to_uv(p0, p1, p2, 2.0, too_steep); + if(too_steep) is_linear = 1.0; + uv_scale_factor = length(xyz_to_uv[0].xyz); + } - float uv_scale_factor = length(xyz_to_uv[0].xyz); float scaled_aaw = anti_alias_width * pixel_size; - uv_anti_alias_width = uv_scale_factor * scaled_aaw; - vec3 corners[6]; get_corners(p0, p1, p2, v01, v12, scaled_aaw, corners); // Emit each corner for(int i = 0; i < 6; i++){ - int vert_index = i / 2; - uv_coords = (xyz_to_uv * vec4(corners[i], 1)).xy; - uv_stroke_width = uv_scale_factor * v_stroke_width[vert_index]; - color = finalize_color(v_color[vert_index], corners[i], unit_normal); + float stroke_width = v_stroke_width[i / 2]; + + if(bool(is_linear)){ + float sign = vec2(-1, 1)[i % 2]; + // In this case, we only really care about + // the v coordinate + uv_coords = vec2(0, sign * (0.5 * stroke_width + scaled_aaw)); + uv_anti_alias_width = scaled_aaw; + uv_stroke_width = stroke_width; + }else{ + uv_coords = (xyz_to_uv * vec4(corners[i], 1.0)).xy; + uv_stroke_width = uv_scale_factor * stroke_width; + uv_anti_alias_width = uv_scale_factor * scaled_aaw; + } + + color = finalize_color(v_color[i / 2], corners[i], unit_normal); gl_Position = get_gl_Position(corners[i]); EmitVertex(); }