Skip to content

Commit

Permalink
Completes testing.
Browse files Browse the repository at this point in the history
  • Loading branch information
TobiaMarcucci committed Sep 4, 2024
1 parent 42912d9 commit 6174bc5
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 40 deletions.
23 changes: 12 additions & 11 deletions pybezier/composite_bezier_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ def __init__(self, curves):
self.duration = self.final_time - self.initial_time
self.knot_times = [self.initial_time] + [curve.final_time for curve in curves]

def curve_segment(self, time : float) -> int:
segment = 0
while self[segment].final_time < time:
segment += 1
return segment

def __call__(self, time : float) -> np.array:
segment = self.curve_segment(time)
return self[segment](time)

def initial_point(self) -> np.array:
return self[0].initial_point()

Expand All @@ -33,16 +43,6 @@ def __iter__(self) -> Iterable[BezierCurve]:
def __getitem__(self, i : int) -> BezierCurve:
return self.curves[i]

def __call__(self, time : float) -> np.array:
segment = self.find_segment(time)
return self[segment](time)

def find_segment(self, time : float) -> int:
segment = 0
while self[segment].final_time < time:
segment += 1
return segment

def __len__(self) -> int:
return(len(self.curves))

Expand Down Expand Up @@ -82,7 +82,8 @@ def __neg__(self) -> Self:
return 0 - self

def knot_points(self) -> np.array:
knots = [curve.points[0] for curve in self]
# assumes that the curve is continuous
knots = [curve.initial_point() for curve in self]
knots.append(self.final_point())
return np.array(knots)

Expand Down
56 changes: 28 additions & 28 deletions tests/test_bezier_curve.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import unittest
import numpy as np

from pybezier.bezier_curve import BezierCurve

class TestBezierCurve(unittest.TestCase):
Expand All @@ -14,6 +13,7 @@ def setUp(self):
self.curve_1 = self.curve
self.points_2 = np.random.rand(7, 3)
self.curve_2 = BezierCurve(self.points_2, self.initial_time, self.final_time)
self.time_samples = np.linspace(self.initial_time, self.final_time)

def test_init(self):
np.testing.assert_equal(self.curve.points, self.points)
Expand All @@ -30,11 +30,11 @@ def test_init_deafult(self):
self.assertEqual(curve.final_time, 1)

def test_initial_final_point(self):
np.testing.assert_array_almost_equal(self.curve(self.initial_time), self.points[0])
np.testing.assert_array_almost_equal(self.curve(self.final_time), self.points[-1])
np.testing.assert_array_almost_equal(self.curve(self.initial_time), self.curve.initial_point())
np.testing.assert_array_almost_equal(self.curve(self.final_time), self.curve.final_point())

def test_berstein(self):
for time in np.linspace(self.initial_time, self.final_time):
for time in self.time_samples:
values = [self.curve._berstein(time, n) for n in range(self.curve.degree + 1)]
# tests partition of unity
self.assertTrue(min(values) >= 0)
Expand Down Expand Up @@ -62,64 +62,63 @@ def test_call(self):
self.assertAlmostEqual(p1[1], p2[1])

def test_scalar_mul(self):
np.random.seed(0)
c = 3.66
prod_1 = self.curve * c
prod_2 = c * self.curve
for time in np.linspace(self.initial_time, self.final_time):
for time in self.time_samples:
value = self.curve(time) * c
np.testing.assert_array_almost_equal(prod_1(time), value)
np.testing.assert_array_almost_equal(prod_2(time), value)

def test_elementwise_mul(self):
prod = self.curve_1 * self.curve_2
for time in np.linspace(self.initial_time, self.final_time):
for time in self.time_samples:
np.testing.assert_array_almost_equal(prod(time), self.curve_1(time) * self.curve_2(time))

def test_elevate_degree(self):
curve = self.curve.elevate_degree(11)
for time in np.linspace(self.initial_time, self.final_time):
for time in self.time_samples:
np.testing.assert_array_almost_equal(self.curve(time), curve(time))

def test_scalar_add_sub(self):
c = 3.66
curve_sum_1 = self.curve + c
curve_sum_2 = c + self.curve
curve_sub_1 = self.curve - c
curve_sub_2 = c - self.curve
for time in np.linspace(self.initial_time, self.final_time):
sum_1 = self.curve + c
sum_2 = c + self.curve
sub_1 = self.curve - c
sub_2 = c - self.curve
for time in self.time_samples:
value = self.curve(time) + c
np.testing.assert_array_almost_equal(curve_sum_1(time), value)
np.testing.assert_array_almost_equal(curve_sum_2(time), value)
np.testing.assert_array_almost_equal(sum_1(time), value)
np.testing.assert_array_almost_equal(sum_2(time), value)
value -= 2 * c
np.testing.assert_array_almost_equal(curve_sub_1(time), value)
np.testing.assert_array_almost_equal(curve_sub_2(time), -value)
np.testing.assert_array_almost_equal(sub_1(time), value)
np.testing.assert_array_almost_equal(sub_2(time), -value)

def test_elementwise_add_sub(self):
curve_sum = self.curve_1 + self.curve_2
curve_sub = self.curve_1 - self.curve_2
for time in np.linspace(self.initial_time, self.final_time):
sum = self.curve_1 + self.curve_2
sub = self.curve_1 - self.curve_2
for time in self.time_samples:
value = self.curve_1(time) + self.curve_2(time)
np.testing.assert_array_almost_equal(curve_sum(time), value)
np.testing.assert_array_almost_equal(sum(time), value)
value -= 2 * self.curve_2(time)
np.testing.assert_array_almost_equal(curve_sub(time), value)
np.testing.assert_array_almost_equal(sub(time), value)

def test_neg(self):
curve = - self.curve
for time in np.linspace(0, 1):
np.testing.assert_array_almost_equal(curve(time), -self.curve(time))
neg = - self.curve
for time in self.time_samples:
np.testing.assert_array_almost_equal(neg(time), -self.curve(time))

def test_derivative(self):
derivative = self.curve.derivative()
der = self.curve.derivative()
time_step = 1e-9
for time in np.linspace(self.initial_time, self.final_time - time_step):
value = (self.curve(time + time_step) - self.curve(time)) / time_step
np.testing.assert_array_almost_equal(derivative(time), value)
np.testing.assert_array_almost_equal(der(time), value)

def test_split(self):
split_time = (self.initial_time + self.final_time) / 2
curve_1, curve_2 = self.curve.split(split_time)
for time in np.linspace(self.initial_time, self.final_time):
for time in self.time_samples:
if time < split_time:
np.testing.assert_array_almost_equal(self.curve(time), curve_1(time))
elif time > split_time:
Expand All @@ -138,6 +137,7 @@ def test_integral_of_convex(self):
# upper bound for curve lenght is equal to distance of control points
derivative = self.curve.derivative()
value = sum(np.linalg.norm(y - x) for x, y in zip(self.points[:-1], self.points[1:]))

self.assertAlmostEqual(derivative.integral_of_convex(np.linalg.norm), value)

if __name__ == '__main__':
Expand Down
1 change: 0 additions & 1 deletion tests/test_binomial.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import unittest

from pybezier.binomial import binomial

class TestBinomial(unittest.TestCase):
Expand Down
156 changes: 156 additions & 0 deletions tests/test_composite_bezier_curve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import unittest
import numpy as np
from pybezier.bezier_curve import BezierCurve
from pybezier.composite_bezier_curve import CompositeBezierCurve

class TestCompositeBezierCurve(unittest.TestCase):

@staticmethod
def random_composite_curve(dimension, n_curves, n_points):
points = np.random.rand((n_points - 1) * n_curves + 1, dimension)
curves = []
for i in range(n_curves):
start = n_points * i - i
stop = n_points * (i + 1) - i
points_i = points[start:stop]
curve_i = BezierCurve(points_i, i, i + 1)
curves.append(curve_i)
return CompositeBezierCurve(curves)

def setUp(self):
np.random.seed(0)
self.dimension = 3
self.n_curves = 4
self.n_points = 5
self.composite_curve = self.random_composite_curve(self.dimension, self.n_curves, self.n_points)
self.initial_time = 0
self.final_time = self.n_curves
self.time_samples = np.linspace(self.initial_time, self.final_time)
self.composite_curve_1 = self.composite_curve
self.composite_curve_2 = self.random_composite_curve(self.dimension, self.n_curves, self.n_points)

def test_init(self):
self.assertEqual(len(self.composite_curve.curves), self.n_curves)
for curve in self.composite_curve.curves:
self.assertEqual(curve.points.shape, (self.n_points, self.dimension))
self.assertEqual(self.composite_curve.dimension, self.dimension)
self.assertEqual(self.composite_curve.initial_time, 0)
self.assertEqual(self.composite_curve.final_time, self.n_curves)
self.assertEqual(self.composite_curve.duration, self.n_curves)
self.assertEqual(self.composite_curve.knot_times, list(range(self.n_curves + 1)))

def test_curve_segment(self):
for time in self.time_samples[:-1]:
self.assertEqual(self.composite_curve.curve_segment(time), int(time))
self.assertEqual(self.composite_curve.curve_segment(self.final_time), self.n_curves - 1)

def test_call(self):
for time in self.time_samples:
value = self.composite_curve(time)
i = self.composite_curve.curve_segment(time)
curve = self.composite_curve.curves[i]
np.testing.assert_array_almost_equal(value, curve(time))

def test_initial_final_point(self):
initial_value = self.composite_curve(self.initial_time)
initial_point = self.composite_curve.initial_point()
np.testing.assert_array_almost_equal(initial_value, initial_point)
final_value = self.composite_curve(self.final_time)
final_point = self.composite_curve.final_point()
np.testing.assert_array_almost_equal(final_value, final_point)

def test_iter(self):
for curve in self.composite_curve:
self.assertEqual(curve.duration, 1)

def test_getitem(self):
for i in range(self.n_curves):
curve = self.composite_curve[i]
self.assertEqual(curve.duration, 1)

def test_len(self):
self.assertEqual(len(self.composite_curve), self.n_curves)

def test_scalar_mul(self):
c = 3.66
prod_1 = self.composite_curve * c
prod_2 = c * self.composite_curve
for time in self.time_samples:
value = self.composite_curve(time) * c
np.testing.assert_array_almost_equal(prod_1(time), value)
np.testing.assert_array_almost_equal(prod_2(time), value)

def test_elementwise_mul(self):
prod = self.composite_curve_1 * self.composite_curve_2
for time in self.time_samples:
value = self.composite_curve_1(time) * self.composite_curve_2(time)
np.testing.assert_array_almost_equal(prod(time), value)

def test_elevate_degree(self):
composite_curve = self.composite_curve.elevate_degree(11)
for time in self.time_samples:
np.testing.assert_array_almost_equal(self.composite_curve(time), composite_curve(time))

def test_scalar_add_sub(self):
c = 3.66
sum_1 = self.composite_curve + c
sum_2 = c + self.composite_curve
sub_1 = self.composite_curve - c
sub_2 = c - self.composite_curve
for time in self.time_samples:
value = self.composite_curve(time) + c
np.testing.assert_array_almost_equal(sum_1(time), value)
np.testing.assert_array_almost_equal(sum_2(time), value)
value -= 2 * c
np.testing.assert_array_almost_equal(sub_1(time), value)
np.testing.assert_array_almost_equal(sub_2(time), -value)

def test_elementwise_add_sub(self):
sum = self.composite_curve_1 + self.composite_curve_2
sub = self.composite_curve_1 - self.composite_curve_2
for time in self.time_samples:
value = self.composite_curve_1(time) + self.composite_curve_2(time)
np.testing.assert_array_almost_equal(sum(time), value)
value -= 2 * self.composite_curve_2(time)
np.testing.assert_array_almost_equal(sub(time), value)

def test_neg(self):
neg = - self.composite_curve
for time in self.time_samples:
np.testing.assert_array_almost_equal(neg(time), -self.composite_curve(time))

def test_derivative(self):
der = self.composite_curve.derivative()
time_step = 1e-9
for time in np.linspace(self.initial_time, self.final_time - time_step):
value = (self.composite_curve(time + time_step) - self.composite_curve(time)) / time_step
np.testing.assert_array_almost_equal(der(time), value)

def test_knot_points(self):
for i, point in enumerate(self.composite_curve.knot_points()):
np.testing.assert_array_almost_equal(point, self.composite_curve(i))

def test_durations(self):
durations = list(self.composite_curve.durations())
self.assertEqual(durations, [1] * self.n_curves)

def test_concatenate(self):
conc = self.composite_curve_1.concatenate(self.composite_curve_2)
for time in self.time_samples * 2:
if time < self.final_time:
value = self.composite_curve_1(time)
np.testing.assert_array_almost_equal(conc(time), value)
elif time > self.final_time:
value = self.composite_curve_2(time - self.final_time)
np.testing.assert_array_almost_equal(conc(time), value)

def test_l2_squared(self):
n_samples = 5000
times = np.linspace(self.initial_time, self.final_time, n_samples)
squared_norm = lambda time: np.linalg.norm(self.composite_curve(time)) ** 2
values = [squared_norm(time) for time in times]
integral = np.trapezoid(values, times)
self.assertAlmostEqual(self.composite_curve.l2_squared(), integral, places=4)

if __name__ == '__main__':
unittest.main()

0 comments on commit 6174bc5

Please sign in to comment.