Skip to content
Open
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
142 changes: 142 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Add Camera System

A flexible camera system for a 2D renderer architecture built with Python and Matplotlib.

---
## students: Nagham Sleman 476937 Salam Ali 503263

## Features

* Top view projection
* Left view projection
* Smooth camera following
* Camera zoom in/out
* Camera panning
* World-to-screen transformation
* Screen-to-world transformation
* Dynamic grid rendering
* Modular renderer architecture

---

## Controls

| Key | Action |
| ----------- | -------------------- |
| Mouse Wheel | Zoom in/out |
| Arrow Keys | Pan camera |
| F | Toggle camera follow |
| V | Switch camera view |

---

## Project Structure

```text
camera-system/
├── camera.py
├── renderer.py
├── robot.py
├── world.py
├── docs/
│ ├── ticket20.jpg
│ └── ticket20.gif
├── main.py
├── README.md
└── requirements.txt
```

---

## Architecture Diagram

![Ticket 20](src/simulator/ticket20.jpg)

## Demo GIF

![Ticket 20 GIF](src/simulator/ticket20.gif)

## Coordinate Systems

### World Space

3D coordinates inside the simulation world.

**Example:**

```text
(100, 50, 20)
```

### Screen Space

2D coordinates rendered on the screen.

**Example:**

```text
(500, 300)
```

---

## Projection Modes

### Top View

Projects:

```text
(X, Y)
```

The Z-axis is hidden.

### Left View

Projects:

```text
(X, Z)
```

The Y-axis is hidden.

---

## Camera Features

* Object following
* Smooth movement
* Zoom scaling
* Coordinate transformations
* Camera panning
* Dynamic grid rendering
* View switching

---

## Installation

```bash
pip install -r requirements.txt
```

---

## Run

```bash
python main.py
```

---

## Requirements

* Python 3.10+
* NumPy
* Matplotlib
68 changes: 68 additions & 0 deletions src/simulator/camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from enum import Enum


class ViewMode(Enum):
TOP = "top"
LEFT = "left"


class Camera:
def init(self, x_limits=(-10.0, 10.0), y_limits=(-10.0, 10.0)):
self.position = [0.0, 0.0, 0.0]
self.zoom = 1.0
self.view_mode = ViewMode.TOP
self.target = None
self.follow_smoothing = 0.25

self.base_width = x_limits[1] - x_limits[0]
self.base_height = y_limits[1] - y_limits[0]

def toggle_view_mode(self):
self.view_mode = ViewMode.LEFT if self.view_mode == ViewMode.TOP else ViewMode.TOP

def follow(self, target):
self.target = target

def update(self):
if self.target is None:
return

target_pos = self.target.get_position()

for i in range(3):
self.position[i] += (target_pos[i] - self.position[i]) * self.follow_smoothing

def zoom_in(self):
self.zoom = min(self.zoom * 1.25, 10.0)

def zoom_out(self):
self.zoom = max(self.zoom / 1.25, 0.2)

def pan(self, dx, dy):
dx /= self.zoom
dy /= self.zoom

if self.view_mode == ViewMode.TOP:
self.position[0] += dx
self.position[1] += dy
else:
self.position[0] += dx
self.position[2] += dy

def project(self, position):
x, y, z = position

if self.view_mode == ViewMode.TOP:
return x, y

return x, z

def apply_to_axes(self, ax):
cx, cy = self.project(self.position)

half_width = self.base_width / (2.0 * self.zoom)
half_height = self.base_height / (2.0 * self.zoom)

ax.set_xlim(cx - half_width, cx + half_width)
ax.set_ylim(cy - half_height, cy + half_height)
ax.set_aspect("equal", adjustable="box")
33 changes: 12 additions & 21 deletions src/simulator/main.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,25 @@
import sys
import numpy as np
import time
import matplotlib.pyplot as plt

from physics import PhysicsEngine
from renderer import Renderer
from world import World

from objects import RobotTree, TwoLink, Tree7, CartPole
from dynamics.ab_algorithm import ABAlgorithm


def main():

fd_solver = ABAlgorithm()

physics = PhysicsEngine(fd_solver, gravity=[0.0, -9.81, 0.0])
renderer = Renderer()
world = World()

world = World(physics, renderer)

# robot = TwoLink()
# robot = Tree7()

# robot = RobotTree()
# robot.some_tree(8,2)

robot = CartPole()
try:
while plt.fignum_exists(renderer.fig.number):
world.update()
renderer.update(world.get_objects())
time.sleep(0.02)

world.add_object(robot)
except KeyboardInterrupt:
pass

world.run(1000)
finally:
renderer.close()


if __name__ == "__main__":
Expand Down
Loading
Loading