diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index 5ea43bc..0000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Upload Python Package - -on: - release: - types: [published] - -permissions: - contents: read - -jobs: - deploy: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 91547b8..411cb24 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,15 +1,118 @@ name: tests -run-name: tests -on: [push] +run-name: Python Tests +on: + workflow_call: + push: + branches: + - "**" + jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: 'Checkout repository' + uses: actions/checkout@v3 + with: + submodules: true + - name: 'Setup Python' + uses: actions/setup-python@v5 + with: + python-version: '3.12.3' + - name: Ensure pip is installed + run: python -m ensurepip --upgrade + - name: Upgrade pip and setuptools + run: | + python -m pip install --upgrade pip setuptools + - name: 'Install dependencies' + run: python -m pip install -r requirements.txt + run-unittests: + needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: 'Run unit tests' + run: tox -e docs + - name: 'Test engine/basic_camera_model/main.py' + run: python engine/basic_camera_model/main.py + - name: 'Test engine/extended_camera_model/main.py' + run: python engine/extended_camera_model/main.py + + smoke-tests-linux: + needs: setup + strategy: + fail-fast: false + matrix: + image: ['ubuntu:latest', 'ubuntu:20.04', 'ubuntu:22.04', 'debian:latest', 'archlinux:latest'] + python_version: ['3.12'] + runs-on: ubuntu-latest + container: + image: ${{ matrix.image }} + env: + TZ: Europe/Berlin + steps: + - name: 'Install git (Ubuntu, Debian)' + if: ${{ contains(fromJSON('["ubuntu:latest", "ubuntu:20.04", "ubuntu:22.04", "debian:latest"]'), matrix.image) }} + run: apt-get update && apt-get install -y git + - name: 'Install git (Arch)' + if: ${{ contains('archlinux:latest', matrix.image) }} + run: pacman -Syu --noconfirm git + - name: 'Checkout repository' + uses: actions/checkout@v3 + with: + submodules: true + - name: 'Setup timezone' + run: ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + - name: 'Install OpenGL dependencies (Ubuntu/Debian)' + if: ${{ contains(matrix.image, 'ubuntu') || contains(matrix.image, 'debian') }} + run: apt-get install -y libgl1-mesa-glx + - name: 'Install OpenGL dependencies (Arch)' + if: ${{ contains(matrix.image, 'archlinux') }} + run: pacman -Syu --noconfirm libglvnd + - name: 'Setup Python' + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Ensure pip is installed + run: python -m ensurepip --upgrade + - name: Upgrade pip and setuptools + run: | + python -m pip install --upgrade pip setuptools + - name: 'Install dependencies' + run: python -m pip install -r requirements.txt + - name: 'Run unit tests' + run: tox -e docs + - name: 'Test engine/basic_camera_model/main.py' + run: python engine/basic_camera_model/main.py + - name: 'Test engine/extended_camera_model/main.py' + run: python engine/extended_camera_model/main.py + + smoke-tests-macos: + needs: setup + strategy: + fail-fast: false + matrix: + macos_version: ['macos-latest', 'macos-13'] + python_version: ['3.12'] + runs-on: ${{ matrix.macos_version }} + steps: + - name: 'Checkout repository' + uses: actions/checkout@v3 with: submodules: true - - uses: actions/setup-python@v4 + - name: 'Setup Python' + uses: actions/setup-python@v4 with: - python-version: '3.11' - - run: pip install -r requirements.txt - - run: tox + python-version: ${{ matrix.python_version }} + - name: Ensure pip is installed + run: python -m ensurepip --upgrade + - name: Upgrade pip and setuptools + run: | + python -m pip install --upgrade pip setuptools + - name: 'Install dependencies' + run: python -m pip install -r requirements.txt + - name: 'Run unit tests' + run: tox -e docs + - name: 'Test engine/basic_camera_model/main.py' + run: python engine/basic_camera_model/main.py + - name: 'Test engine/extended_camera_model/main.py' + run: python engine/extended_camera_model/main.py diff --git a/docs/conf.py b/docs/conf.py index 8e179f6..50129ef 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -from template_project_python import __version__ html_static_path = ['_static'] html_extra_path = ['resources/pipeline'] @@ -16,4 +15,4 @@ project = "3D Engine Python" copyright = "2024, Marvin-VW" author = "Marvin Lorenz" -version = release = __version__ +version = release = str(1.4) diff --git a/engine/basic_camera_model/main.py b/engine/basic_camera_model/main.py index ea48303..4baa4a5 100644 --- a/engine/basic_camera_model/main.py +++ b/engine/basic_camera_model/main.py @@ -57,9 +57,8 @@ def DEG_TO_RAD(deg: float) -> float: if __name__ == "__main__": - print("playground_camera_model started!") - camera_model = CameraModel(0.00452, 0.00254, 0.004, 1280, 720, 1280/2, 720/2) + camera_model = CameraModel(0.00452, 0.00339, 0.004, 640*2, 480*2, 640, 480) W_T_V = create_homogeneous_transformation_matrix(0, 0, 0, 0, 0, 0) V_T_C = create_homogeneous_transformation_matrix(0, 0, 0, 0, 0, 0) @@ -102,14 +101,14 @@ def nothing(x): cv.setTrackbarPos("X", "camera settings", 10000) cv.setTrackbarPos("Y", "camera settings", 10000) - cv.setTrackbarPos("Z", "camera settings", 11000) + cv.setTrackbarPos("Z", "camera settings", 5000) cv.setTrackbarPos("Roll", "camera settings", 0) - cv.setTrackbarPos("Pitch", "camera settings", 180) + cv.setTrackbarPos("Pitch", "camera settings", 1800) cv.setTrackbarPos("Yaw", "camera settings", 0) - cv.setTrackbarPos("X", "cube settings", 14000) + cv.setTrackbarPos("X", "cube settings", 10000) cv.setTrackbarPos("Y", "cube settings", 10000) - cv.setTrackbarPos("Z", "cube settings", 11000) + cv.setTrackbarPos("Z", "cube settings", 10000) cv.setTrackbarPos("Roll", "cube settings", 0) cv.setTrackbarPos("Pitch", "cube settings", 0) cv.setTrackbarPos("Yaw", "cube settings", 0) diff --git a/engine/basic_camera_model/requirements.txt b/engine/basic_camera_model/requirements.txt deleted file mode 100644 index 4386354..0000000 --- a/engine/basic_camera_model/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -opencv-python~=4.9.0.80 -numpy<2 \ No newline at end of file diff --git a/engine/basic_camera_model/utils/fps_counter.py b/engine/basic_camera_model/utils/fps_counter.py index 30b78e3..462bdff 100644 --- a/engine/basic_camera_model/utils/fps_counter.py +++ b/engine/basic_camera_model/utils/fps_counter.py @@ -15,7 +15,10 @@ def update(self) -> None: timestamp = time.time() delta_time = timestamp - self.last_timestamp self.last_timestamp = timestamp - self.fps = 1.0 / delta_time + try: + self.fps = 1.0 / delta_time + except: + self.fps = 0 self.fps_history.append(self.fps) if len(self.fps_history) > self.filter_window_size: self.fps_history.pop(0) diff --git a/engine/extended_camera_model/main.py b/engine/extended_camera_model/main.py index cd0b739..be01f1e 100644 --- a/engine/extended_camera_model/main.py +++ b/engine/extended_camera_model/main.py @@ -16,7 +16,7 @@ class Engine: def __init__(self): - self.camera_model = CameraModel(0.00452, 0.00254, 0.004, 1280, 720, 1280/2, 720/2) + self.camera_model = CameraModel(0.00452, 0.00339, 0.004, 640, 480, 640/2, 480/2) self.window = Window() self.clipping_space = Clipping_Space() self.fps_counter = FpsCounter(60) @@ -48,7 +48,6 @@ def main(self): while True: - self.window.handle_movement() self.fps_counter.update() self.camera_model.reset_camera_image() @@ -68,7 +67,7 @@ def main(self): if self.is_triangle_facing_camera(triangle.normal, triangle.centroids, camera_vector_world) < 0.0: - light_direction = (1, -0.5, -0.8) + light_direction = (0.5, -2.0, 0.5) triangle.ilm = Color.intensity(light_direction, triangle.normal) triangle.color = Color.adjust_bgr_intensity(Color.ALICE_BLUE, triangle.ilm) @@ -81,9 +80,6 @@ def main(self): shadow_points_camera = self.camera_model.world_transform(shadow_points, self.C_T_V) self.camera_model.draw_poly(shadow_points_camera) - print(shadow_points_camera) - - clipped_triangles = [] clipped_triangles.extend(self.clipping_space.cube_in_space(sorted_list)) diff --git a/engine/extended_camera_model/requirements.txt b/engine/extended_camera_model/requirements.txt deleted file mode 100644 index 4386354..0000000 --- a/engine/extended_camera_model/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -opencv-python~=4.9.0.80 -numpy<2 \ No newline at end of file diff --git a/engine/extended_camera_model/utils/vectors.py b/engine/extended_camera_model/utils/vectors.py index 6aa2c53..d702270 100644 --- a/engine/extended_camera_model/utils/vectors.py +++ b/engine/extended_camera_model/utils/vectors.py @@ -62,7 +62,7 @@ def normal(triangle, scale = 0.5): def get_shadow(triangles, light_vec): shadow_points = [] - plane_normal = np.array([0, 0, 1]) + plane_normal = np.array([0, 1, 0]) for triangle in triangles: for point in triangle.world_points: diff --git a/engine/extended_camera_model/utils/window.py b/engine/extended_camera_model/utils/window.py index 88f01e8..846f097 100644 --- a/engine/extended_camera_model/utils/window.py +++ b/engine/extended_camera_model/utils/window.py @@ -8,18 +8,18 @@ class Window: def __init__(self): self.camera_system_translation_x = 10000 - self.camera_system_translation_y = 10000 - self.camera_system_translation_z = 10000 - self.camera_system_rotation_roll = 2700 - self.camera_system_rotation_pitch = 0 - self.camera_system_rotation_yaw = 2700 + self.camera_system_translation_y = 13000 + self.camera_system_translation_z = 3000 + self.camera_system_rotation_roll = 1500 + self.camera_system_rotation_pitch = 1800 + self.camera_system_rotation_yaw = 0 - self.cube_system_translation_x = 16000 + self.cube_system_translation_x = 10000 self.cube_system_translation_y = 10000 self.cube_system_translation_z = 10000 self.cube_system_rotation_roll = 0 - self.cube_system_rotation_pitch = 0 - self.cube_system_rotation_yaw = 300 + self.cube_system_rotation_pitch = 500 + self.cube_system_rotation_yaw = 0 self.cube_system_scale = 1 self.camera_window_name = "camera settings" @@ -82,10 +82,9 @@ def window_creator(self): cv.setTrackbarPos("Scale", "cube settings", self.cube_system_scale) cv.setTrackbarPos("Normals", "cube settings", 0) - cv.setTrackbarPos("Planes", "cube settings", 0) - cv.setTrackbarPos("Points", "cube settings", 1) + cv.setTrackbarPos("Planes", "cube settings", 1) + cv.setTrackbarPos("Points", "cube settings", 0) - cv.setMouseCallback("image window", self.mouse_event_handler) def toggle_normal(self, value): self.show_normals = not self.show_normals @@ -142,100 +141,3 @@ def get_cube_system_scale(self): @staticmethod def nothing(value): pass - - - def handle_movement(self): - camera_speed = 100 - current_time = time.time() - if current_time - self.last_update_time >= self.update_interval: - self.last_update_time = current_time - - key = cv.waitKey(30) & 0xFF - - if key == ord('d'): - self.move_camera('forward', camera_speed) - if key == ord('a'): - self.move_camera('backward', camera_speed) - if key == ord('w'): - self.move_camera('left', camera_speed) - if key == ord('s'): - self.move_camera('right', camera_speed) - if key == ord('q'): - self.move_camera('down', camera_speed) - if key == ord('e'): - self.move_camera('up', camera_speed) - - def move_camera(self, direction, speed): - # Calculate vectors - yaw = np.deg2rad(self.camera_system_rotation_yaw / 10.0) - pitch = np.deg2rad(self.camera_system_rotation_pitch / 10.0) - - forward_x = np.cos(pitch) * np.cos(yaw) - forward_y = np.cos(pitch) * np.sin(yaw) - forward_z = np.sin(pitch) - - right_x = np.sin(yaw) - right_y = -np.cos(yaw) - right_z = 0 - - up_x = 0 - up_y = 0 - up_z = 1 - - if direction == 'forward': - self.camera_system_translation_x += int(forward_x * speed) - self.camera_system_translation_y += int(forward_y * speed) - self.camera_system_translation_z += int(forward_z * speed) - elif direction == 'backward': - self.camera_system_translation_x -= int(forward_x * speed) - self.camera_system_translation_y -= int(forward_y * speed) - self.camera_system_translation_z -= int(forward_z * speed) - elif direction == 'left': - self.camera_system_translation_x -= int(right_x * speed) - self.camera_system_translation_y -= int(right_y * speed) - elif direction == 'right': - self.camera_system_translation_x += int(right_x * speed) - self.camera_system_translation_y += int(right_y * speed) - elif direction == 'up': - self.camera_system_translation_z += int(up_z * speed) - elif direction == 'down': - self.camera_system_translation_z -= int(up_z * speed) - - self.camera_system_translation_x = np.clip(self.camera_system_translation_x, 0, 20000) - self.camera_system_translation_y = np.clip(self.camera_system_translation_y, 0, 20000) - self.camera_system_translation_z = np.clip(self.camera_system_translation_z, 0, 20000) - cv.setTrackbarPos("X", self.camera_window_name, self.camera_system_translation_x) - cv.setTrackbarPos("Y", self.camera_window_name, self.camera_system_translation_y) - cv.setTrackbarPos("Z", self.camera_window_name, self.camera_system_translation_z) - - def mouse_event_handler(self, event, x, y, flags, param): - if event == cv.EVENT_LBUTTONDOWN: - self.mouse_is_pressed = True - self.last_mouse_position = (x, y) - elif event == cv.EVENT_LBUTTONUP: - self.mouse_is_pressed = False - elif event == cv.EVENT_RBUTTONDOWN: - self.right_button_mode = True - elif event == cv.EVENT_RBUTTONDBLCLK: - self.right_button_mode = False - self.last_mouse_position = (x, y) - elif event == cv.EVENT_MOUSEMOVE: - if self.mouse_is_pressed or self.right_button_mode: - dx = x - self.last_mouse_position[0] - dy = y - self.last_mouse_position[1] - self.camera_system_rotation_yaw += dx - self.camera_system_rotation_roll += dy - if self.camera_system_rotation_yaw > 3600: - self.camera_system_rotation_yaw -= 3599 - if self.camera_system_rotation_roll > 3600: - self.camera_system_rotation_roll -= 3599 - if self.camera_system_rotation_yaw < 0: - self.camera_system_rotation_yaw += 3599 - if self.camera_system_rotation_roll < 0: - self.camera_system_rotation_roll += 3599 - #self.camera_system_rotation_yaw = np.clip(self.camera_system_rotation_yaw, 0, 3600) - #self.camera_system_rotation_roll = np.clip(self.camera_system_rotation_roll, 0, 3600) - cv.setTrackbarPos("Yaw", self.camera_window_name, self.camera_system_rotation_yaw) - cv.setTrackbarPos("Roll", self.camera_window_name, self.camera_system_rotation_roll) - self.last_mouse_position = (x, y) - \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2467d1e..879403f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,7 @@ versioneer~=0.29 pyinstaller template-project-utils sphinx -sphinx_rtd_theme \ No newline at end of file +sphinx_rtd_theme + +opencv-python~=4.10.0.84 +numpy<2 \ No newline at end of file diff --git a/tox.ini b/tox.ini index f0e4539..d3d58d4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,3 @@ -[tox] -requires = - tox>=4 -env_list = type, lint_check, cli, py{310} - -[testenv] -description = run unit tests -deps = -commands = - python -m unittest discover -s tests/ - [testenv:docs] changedir = docs deps = @@ -17,40 +6,4 @@ deps = setenv = PYTHONPATH = {toxinidir} commands = - sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html - -[testenv:lint_update] -description = run linters -deps = - black==23.3.0 -commands = - black --extend-exclude external/ -l 120 {posargs:.} - -[testenv:lint_check] -description = run linters -deps = - black==23.3.0 -commands = - black --extend-exclude external/ --check -l 120 {posargs:.} - -[testenv:type] -description = run type checks -deps = - mypy>=0.991 -commands = - mypy {posargs:template_project_python tests examples} - -[testenv:cli] -description = run smoke test for cli -deps = - wheel -commands = - python setup.py sdist bdist_wheel install - template_project_python --version - -[testenv:pyinstaller] -description = build executable with pyinstaller -deps = - pyinstaller -commands = - pyinstaller pyinstaller.spec + sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html \ No newline at end of file