Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions pymunk/tests/test_vec2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,31 @@ def testComparison(self) -> None:
self.assertTrue(int_vec != (0, 0))
self.assertTrue(int_vec != 5) # type: ignore
self.assertTrue(int_vec != (3, -2, -5)) # type: ignore

def testNonorthogonalBasis(self) -> None:
bases = [
((32, 16), (-32, 16)),
((2, 1), (1, 3)),
((1, 3), (2, 1)),
((-2, -1), (1, 3)),
]
for x_axis, y_axis in bases:
for expected in [(1, 0), (0, 1), (3, -2), (-0.5, 1.5)]:
with self.subTest(basis=(x_axis, y_axis), expected=expected):
world = Vec2d(*x_axis) * expected[0] + Vec2d(*y_axis) * expected[1]
actual = world.convert_to_basis(x_axis, y_axis)
self.assertAlmostEqual(actual.x, expected[0])
self.assertAlmostEqual(actual.y, expected[1])

def testOrthogonalBasis(self) -> None:
for x_axis, y_axis in [((5, 0), (0, 0.5)), ((3, 4), (-4, 3))]:
with self.subTest(basis=(x_axis, y_axis)):
expected = Vec2d(2, -3)
world = Vec2d(*x_axis) * expected.x + Vec2d(*y_axis) * expected.y
self.assertEqual(world.convert_to_basis(x_axis, y_axis), expected)

def testDependentBasis(self) -> None:
for x_axis, y_axis in [((0, 0), (0, 1)), ((1, 2), (2, 4))]:
with self.subTest(basis=(x_axis, y_axis)):
with self.assertRaises(ZeroDivisionError):
Vec2d(3, 4).convert_to_basis(x_axis, y_axis)
12 changes: 10 additions & 2 deletions pymunk/vec2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,13 +571,21 @@ def convert_to_basis(
) -> "Vec2d":
"""Convert the vector to a new basis defined by the given x and y vectors.

The basis vectors must be linearly independent, but need not be
perpendicular.

>>> Vec2d(10, 1).convert_to_basis((5, 0), (0, 0.5))
Vec2d(2.0, 2.0)
>>> Vec2d(32, 16).convert_to_basis((32, 16), (-32, 16))
Vec2d(1.0, 0.0)

:raises ZeroDivisionError: If the basis vectors are linearly dependent.
"""
assert len(x_vector) == 2
assert len(y_vector) == 2
x = self.dot(x_vector) / Vec2d(*x_vector).length_squared
y = self.dot(y_vector) / Vec2d(*y_vector).length_squared
determinant = x_vector[0] * y_vector[1] - x_vector[1] * y_vector[0]
x = (self.x * y_vector[1] - self.y * y_vector[0]) / determinant
y = (x_vector[0] * self.y - x_vector[1] * self.x) / determinant
return Vec2d(x, y)

@property
Expand Down
Loading