Compare commits
3
Commits
cd3da3dde1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6d4274525 | ||
|
|
b649addb49 | ||
|
|
043440e38c |
No files matched your search
Generated
+1
@@ -2,6 +2,7 @@
|
|||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13 (matrixmania)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.13 (matrixmania)" jdkType="Python SDK" />
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
from compute import matmul, transpose, rot_2D
|
from .compute import matmul, transpose, rot_2D, rot_3D, project_ortho, project_perspective
|
||||||
@@ -91,6 +91,78 @@ def rot_2D(theta: float) -> list[list[float]]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def rot_3D(angle: float, axis: str) -> list[list[float]]:
|
||||||
|
"""Return a 3‑D rotation matrix.
|
||||||
|
|
||||||
|
The function converts *angle* from degrees to radians and builds the
|
||||||
|
standard rotation matrix around the requested *axis*.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
angle: Rotationswinkel in Grad.
|
||||||
|
axis: ``"x"``, ``"y"`` oder ``"z"`` – der Drehachse.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
3x3 Rotationsmatrix als verschachtelte Listen.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Wenn *axis* keinen gültigen Wert hat.
|
||||||
|
"""
|
||||||
|
# Validate axis
|
||||||
|
axis_lower = axis.lower()
|
||||||
|
if axis_lower not in {"x", "y", "z"}:
|
||||||
|
raise ValueError(
|
||||||
|
f"Unbekannte Achse '{axis}'. Erwartet: 'x', 'y' oder 'z'."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert degrees to radians for math functions
|
||||||
|
rad = math.radians(angle)
|
||||||
|
c = math.cos(rad)
|
||||||
|
s = math.sin(rad)
|
||||||
|
|
||||||
|
if axis_lower == "x":
|
||||||
|
return [
|
||||||
|
[1, 0, 0],
|
||||||
|
[0, c, -s],
|
||||||
|
[0, s, c],
|
||||||
|
]
|
||||||
|
if axis_lower == "y":
|
||||||
|
return [
|
||||||
|
[c, 0, s],
|
||||||
|
[0, 1, 0],
|
||||||
|
[
|
||||||
|
-s,
|
||||||
|
0,
|
||||||
|
c,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
# z
|
||||||
|
return [
|
||||||
|
[c, -s, 0],
|
||||||
|
[s, c, 0],
|
||||||
|
[0, 0, 1],
|
||||||
|
]
|
||||||
|
|
||||||
|
def project_ortho(point: tuple[float, float, float], scale: float = 1) -> tuple[float, float]:
|
||||||
|
"""Project a 3D point onto a 2D plane using orthogonal projection."""
|
||||||
|
x, y, _z = point
|
||||||
|
return (x * scale, y * scale)
|
||||||
|
|
||||||
|
|
||||||
|
def project_perspective(point: tuple[float, float, float],
|
||||||
|
focal_length: float = 1.0,
|
||||||
|
scale: float = 1.0,
|
||||||
|
camera_distance: float = 4.0) -> tuple[float, float]:
|
||||||
|
"""Project a 3D point onto a 2D plane using perspective projection."""
|
||||||
|
x, y ,z = point
|
||||||
|
factor = focal_length / (z+camera_distance)
|
||||||
|
x_2d = x * factor * scale
|
||||||
|
y_2d = y * factor * scale
|
||||||
|
return (x_2d, y_2d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
#matmul prüfung
|
#matmul prüfung
|
||||||
|
|||||||
Whitespace-only changes.
@@ -0,0 +1,48 @@
|
|||||||
|
import math
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from matrixmania import project_perspective
|
||||||
|
|
||||||
|
|
||||||
|
def approx_equal(a: float, b: float, rel_tol: float = 1e-9) -> bool:
|
||||||
|
"""Utility to compare two floats with a relative tolerance."""
|
||||||
|
return math.isclose(a, b, rel_tol=rel_tol)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"point,focal_length,scale,expected",
|
||||||
|
[
|
||||||
|
# Case 1 – no scaling, unit focal length
|
||||||
|
(
|
||||||
|
(1.0, 2.0, 3.0),
|
||||||
|
1.0, # focal_length
|
||||||
|
1.0, # scale
|
||||||
|
(
|
||||||
|
1.0 / (4.0 + 3.0), # x projected
|
||||||
|
2.0 / (4.0 + 3.0), # y projected
|
||||||
|
),
|
||||||
|
),
|
||||||
|
# Case 2 – scale 100, focal length 2
|
||||||
|
(
|
||||||
|
(1.0, 2.0, 3.0),
|
||||||
|
2.0, # focal_length
|
||||||
|
100.0, # scale
|
||||||
|
(
|
||||||
|
2.0 * 100.0 / (4.0 + 3.0), # x projected
|
||||||
|
2.0 * 100.0 * 2.0 / (4.0 + 3.0), # y projected
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_project_perspective(point, focal_length, scale, expected):
|
||||||
|
"""Test that project_perspective returns the correct 2‑D projection."""
|
||||||
|
# Call the function under test
|
||||||
|
result = project_perspective(point, focal_length=focal_length, scale=scale)
|
||||||
|
|
||||||
|
# Basic sanity checks
|
||||||
|
assert isinstance(result, tuple), "Result should be a tuple"
|
||||||
|
assert len(result) == 2, "Result should contain two coordinates"
|
||||||
|
|
||||||
|
# Confirm values are close to expected (within tiny tolerance)
|
||||||
|
assert approx_equal(result[0], expected[0]), f"X coordinate mismatch: {result[0]} != {expected[0]}"
|
||||||
|
assert approx_equal(result[1], expected[1]), f"Y coordinate mismatch: {result[1]} != {expected[1]}"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from matrixmania import project_ortho
|
||||||
|
|
||||||
|
|
||||||
|
def test_projection_point():
|
||||||
|
#Arrange
|
||||||
|
point = (1.0, 2.0, 3.0)
|
||||||
|
expected_value = (1.0, 2.0)
|
||||||
|
|
||||||
|
#Act
|
||||||
|
result = project_ortho(point)
|
||||||
|
|
||||||
|
#Assert
|
||||||
|
assert result == expected_value
|
||||||
|
|
||||||
|
def test_projection_scale():
|
||||||
|
point = (1.0, 2.0, 3.0)
|
||||||
|
scale = 100
|
||||||
|
expected_value = (100.0, 200.0)
|
||||||
|
|
||||||
|
result = project_ortho(point, scale)
|
||||||
|
assert result == expected_value
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import pytest
|
||||||
|
from matrixmania import rot_2D, rot_3D
|
||||||
|
import math
|
||||||
|
|
||||||
|
def test_rot_2D():
|
||||||
|
#Arrange
|
||||||
|
theta = math.pi / 2
|
||||||
|
expected_value = [
|
||||||
|
[0.0, -1.0],
|
||||||
|
[1.0, 0.0]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#Act
|
||||||
|
result = rot_2D(theta)
|
||||||
|
|
||||||
|
#Assert
|
||||||
|
assert len(result) == len(expected_value)
|
||||||
|
for row_result, row_expected in zip(result, expected_value):
|
||||||
|
assert row_result == pytest.approx(row_expected, abs=1e-5)
|
||||||
|
|
||||||
|
def test_rot_3D():
|
||||||
|
#Arrange
|
||||||
|
angle = 90
|
||||||
|
axis = "z"
|
||||||
|
expected_value = [
|
||||||
|
[0.0, -1.0, 0.0],
|
||||||
|
[1.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 1.0]
|
||||||
|
]
|
||||||
|
|
||||||
|
#Act
|
||||||
|
result = rot_3D(angle,axis)
|
||||||
|
|
||||||
|
#Assert
|
||||||
|
assert len(result) == len(expected_value)
|
||||||
|
for row_result, row_expected in zip(result, expected_value):
|
||||||
|
assert row_result == pytest.approx(row_expected, abs=1e-5)
|
||||||
Reference in new issue
Block a user