Skip to content

Commit 63a1c41

Browse files
Faraz126cclauss
andcommitted
Added implementation for Bezier Curve, under a new graphics directory. (TheAlgorithms#1713)
* Added bezier curve * black formatted * corrected spell check * edited scipy import * updated documentation for readablitity * Update bezier_curve.py * Update bezier_curve.py Co-authored-by: Christian Clauss <[email protected]>
1 parent 46ac50a commit 63a1c41

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

graphics/bezier_curve.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# https://en.wikipedia.org/wiki/B%C3%A9zier_curve
2+
# https://www.tutorialspoint.com/computer_graphics/computer_graphics_curves.htm
3+
4+
from typing import List, Tuple
5+
from scipy.special import comb
6+
7+
8+
class BezierCurve:
9+
"""
10+
Bezier curve is a weighted sum of a set of control points.
11+
Generate Bezier curves from a given set of control points.
12+
This implementation works only for 2d coordinates in the xy plane.
13+
"""
14+
15+
def __init__(self, list_of_points: List[Tuple[float, float]]):
16+
"""
17+
list_of_points: Control points in the xy plane on which to interpolate. These
18+
points control the behavior (shape) of the Bezier curve.
19+
"""
20+
self.list_of_points = list_of_points
21+
# Degree determines the flexibility of the curve.
22+
# Degree = 1 will produce a straight line.
23+
self.degree = len(list_of_points) - 1
24+
25+
def basis_function(self, t: float) -> List[float]:
26+
"""
27+
The basis function determines the weight of each control point at time t.
28+
t: time value between 0 and 1 inclusive at which to evaluate the basis of
29+
the curve.
30+
returns the x, y values of basis function at time t
31+
32+
>>> curve = BezierCurve([(1,1), (1,2)])
33+
>>> curve.basis_function(0)
34+
[1.0, 0.0]
35+
>>> curve.basis_function(1)
36+
[0.0, 1.0]
37+
"""
38+
assert 0 <= t <= 1, "Time t must be between 0 and 1."
39+
output_values: List[float] = []
40+
for i in range(len(self.list_of_points)):
41+
# basis function for each i
42+
output_values.append(
43+
comb(self.degree, i) * ((1 - t) ** (self.degree - i)) * (t ** i)
44+
)
45+
# the basis must sum up to 1 for it to produce a valid Bezier curve.
46+
assert round(sum(output_values), 5) == 1
47+
return output_values
48+
49+
def bezier_curve_function(self, t: float) -> Tuple[float, float]:
50+
"""
51+
The function to produce the values of the Bezier curve at time t.
52+
t: the value of time t at which to evaluate the Bezier function
53+
Returns the x, y coordinates of the Bezier curve at time t.
54+
The first point in the curve is when t = 0.
55+
The last point in the curve is when t = 1.
56+
57+
>>> curve = BezierCurve([(1,1), (1,2)])
58+
>>> curve.bezier_curve_function(0)
59+
(1.0, 1.0)
60+
>>> curve.bezier_curve_function(1)
61+
(1.0, 2.0)
62+
"""
63+
64+
assert 0 <= t <= 1, "Time t must be between 0 and 1."
65+
66+
basis_function = self.basis_function(t)
67+
x = 0.0
68+
y = 0.0
69+
for i in range(len(self.list_of_points)):
70+
# For all points, sum up the product of i-th basis function and i-th point.
71+
x += basis_function[i] * self.list_of_points[i][0]
72+
y += basis_function[i] * self.list_of_points[i][1]
73+
return (x, y)
74+
75+
def plot_curve(self, step_size: float = 0.01):
76+
"""
77+
Plots the Bezier curve using matplotlib plotting capabilities.
78+
step_size: defines the step(s) at which to evaluate the Bezier curve.
79+
The smaller the step size, the finer the curve produced.
80+
"""
81+
import matplotlib.pyplot as plt
82+
83+
to_plot_x: List[float] = [] # x coordinates of points to plot
84+
to_plot_y: List[float] = [] # y coordinates of points to plot
85+
86+
t = 0.0
87+
while t <= 1:
88+
value = self.bezier_curve_function(t)
89+
to_plot_x.append(value[0])
90+
to_plot_y.append(value[1])
91+
t += step_size
92+
93+
x = [i[0] for i in self.list_of_points]
94+
y = [i[1] for i in self.list_of_points]
95+
96+
plt.plot(
97+
to_plot_x,
98+
to_plot_y,
99+
color="blue",
100+
label="Curve of Degree " + str(self.degree),
101+
)
102+
plt.scatter(x, y, color="red", label="Control Points")
103+
plt.legend()
104+
plt.show()
105+
106+
107+
if __name__ == "__main__":
108+
import doctest
109+
110+
doctest.testmod()
111+
112+
BezierCurve([(1, 2), (3, 5)]).plot_curve() # degree 1
113+
BezierCurve([(0, 0), (5, 5), (5, 0)]).plot_curve() # degree 2
114+
BezierCurve([(0, 0), (5, 5), (5, 0), (2.5, -2.5)]).plot_curve() # degree 3

0 commit comments

Comments
 (0)