Created
October 23, 2024 15:40
-
-
Save typoman/551f04f18f9608ac3ee8675a8ba401d8 to your computer and use it in GitHub Desktop.
determine cubic bezier curve type using the canonical form in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
from typing import Tuple | |
EPSILON = .1 | |
def forward_transform(points: Tuple[Tuple[float, float], ...], scale: float = 1) -> Tuple[float, float]: | |
p1, p2, p3, p4 = points | |
dy = p2[1] - p1[1] | |
if abs(dy) < EPSILON: | |
return (0, 0) | |
dx = p2[0] - p1[0] | |
k = dy / dx | |
x3 = p3[0] - p1[0] | |
y3 = p3[1] - p1[1] | |
x4 = p4[0] - p1[0] | |
y4 = p4[1] - p1[1] | |
xd = x3 - dx * y3 / dy | |
xn = x4 - dx * y4 / dy | |
if abs(xd) < EPSILON: | |
return (0, 0) | |
np4x = scale * xn / xd | |
yt1 = scale * y4 / dy | |
yt2 = scale - scale * y3 / dy | |
np4y = yt1 + yt2 * xn / xd | |
return (np4x, np4y) | |
def cusp_function(x: float) -> float: | |
return (-x * x + 2 * x + 3) / 4 | |
def loop_function_0(x: float) -> float: | |
return (-x * x + 3 * x) / 3 | |
def loop_function_1(x: float) -> float: | |
d = 4 * x - x * x | |
if d < 0: | |
return float('inf') | |
return (math.sqrt(3 * d) - x) / 2 | |
def determine_region(x: float, y: float) -> str: | |
if y > 1: | |
return "single inflection" | |
if x <= 1: | |
c_y = cusp_function(x) | |
l0_y = loop_function_0(x) if x <= 0 else loop_function_1(x) | |
if abs(y - l0_y) < EPSILON: | |
return "loop at t=0" if x <= 0 else "loop at t=1" | |
if l0_y < y < c_y: | |
return "loop" | |
if abs(y - c_y) < EPSILON: | |
return "cusp" | |
if y > c_y: | |
return "double inflection" | |
if y < c_y: | |
return "plain curve" | |
return "plain curve" | |
def get_curve_type(*points: Tuple[float, float]) -> str: | |
transformed_point = forward_transform(points) | |
return determine_region(transformed_point[0], transformed_point[1]) | |
p1 = (795, 599) | |
p2 = (700, 41) | |
p3 = (1100, 1040) | |
p4 = (280, 139) | |
curve_type = get_curve_type(p1, p2, p3, p4) | |
print(f"The curve type is: {curve_type}") | |
# visualize in drawbot | |
fill(1) | |
rect(0, 0, 1000, 1000) | |
stroke(0) | |
fill(None) | |
newPath() | |
moveTo(p1) | |
curveTo(p2, p3, p4) | |
drawPath() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Based on the canonical form (for cubic curves) it determines if the curve has single or double inflection points, or it has a loop.