Skip to content

Instantly share code, notes, and snippets.

@typoman
Created October 23, 2024 15:40
Show Gist options
  • Save typoman/551f04f18f9608ac3ee8675a8ba401d8 to your computer and use it in GitHub Desktop.
Save typoman/551f04f18f9608ac3ee8675a8ba401d8 to your computer and use it in GitHub Desktop.
determine cubic bezier curve type using the canonical form in python
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()
@typoman
Copy link
Author

typoman commented Oct 23, 2024

Based on the canonical form (for cubic curves) it determines if the curve has single or double inflection points, or it has a loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment