Skip to content
This repository has been archived by the owner on Sep 25, 2022. It is now read-only.

Commit

Permalink
Merge 697b008 into 1c577c9
Browse files Browse the repository at this point in the history
  • Loading branch information
mohammadbashiri committed Jun 12, 2018
2 parents 1c577c9 + 697b008 commit 918615c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 49 deletions.
2 changes: 1 addition & 1 deletion ratcave/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
except ImportError:
pass
from . import utils
from .camera import Camera, PerspectiveProjection, OrthoProjection, CameraGroup
from .camera import Camera, PerspectiveProjection, OrthoProjection, CameraGroup, StereoCameraGroup
from .collision import CylinderCollisionChecker, SphereCollisionChecker
from .fbo import FBO
from .gl_states import GLStateManager, default_states
Expand Down
68 changes: 45 additions & 23 deletions ratcave/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ def update(self):
def viewport(self):
return get_viewport()

def copy(self):
"""Returns a copy of the projection matrix"""
params = {}
for key, val in self.__dict__.items():
if 'matrix' not in key:
k = key[1:] if key[0] == '_' else key
params[k] = val
# params = {param: params[param] for param in params}
return self.__class__(**params)


ScreenEdges = namedtuple('ScreenEdges', 'left right bottom top')

Expand Down Expand Up @@ -251,38 +261,50 @@ def projection_matrix(self):

class CameraGroup(PhysicalGraph):

def __init__(self, position=(0, 0, 0), rotation=(0, 0, 0), distance=.1, look_at=(0, 0, 0), projection=None, *args, **kwargs):
def __init__(self, cameras=None, *args, **kwargs):
""" Creates a group of cameras that behave dependently"""

super(CameraGroup, self).__init__(position=position, rotation=rotation, *args, **kwargs)
self.cam_left = Camera(position=(-distance / 2, 0., 0.))
self.cam_right = Camera(position=(distance / 2, 0., 0.))
self.projection = PerspectiveProjection() if not projection else projection
super(CameraGroup, self).__init__(*args, **kwargs)
self.cameras = cameras
self.add_children(*self.cameras)

self.distance = distance
self.look_at(*look_at)
self.add_children(self.cam_left, self.cam_right)
def look_at(self, x, y, z):
"""Converges the two cameras to look at the specific point"""
for camera in self.cameras:
camera.look_at(x, y, z)

@property
def projection(self):
return self._projection

@projection.setter
def projection(self, value):
self._projection = value
self.cam_left.projection = self._projection
self.cam_right.projection = self._projection
class StereoCameraGroup(CameraGroup):

def __init__(self, distance=.1, projection=None, convergence=0., *args, **kwargs):
""" Creates a group of cameras that behave dependently"""
cameras = [Camera(projection=projection) for _ in range(2)]
super(StereoCameraGroup, self).__init__(cameras=cameras, *args, **kwargs)
for camera, x in zip(self.cameras, [-distance / 2, distance / 2]):
project = projection.copy() if isinstance(projection, ProjectionBase) else PerspectiveProjection()
camera.projection = project
camera.position.x = x

self.left, self.right = self.cameras
self.distance = distance
self.convergence = convergence

@property
def distance(self):
return self.cam_right.position.x - self.cam_left.position.x
return self.right.position.x - self.left.position.x

@distance.setter
def distance(self, value):
self.cam_left.position.x = -value / 2
self.cam_right.position.x = value / 2
self.left.position.x = -value / 2
self.right.position.x = value / 2

def look_at(self, x, y, z):
"""Converges the two cameras to look at the specific point"""
self.cam_left.look_at(x, y, z)
self.cam_right.look_at(x, y, z)
@property
def convergence(self):
return self._convergence

@convergence.setter
def convergence(self, value):
self.left.projection.x_shift = value
self.right.projection.x_shift = -value
self._convergence = value

71 changes: 46 additions & 25 deletions tests/test_camera.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ratcave import Camera, CameraGroup, PerspectiveProjection, OrthoProjection
from ratcave import Camera, CameraGroup, StereoCameraGroup, PerspectiveProjection, OrthoProjection
import pytest
import numpy as np
import pyglet
Expand Down Expand Up @@ -96,52 +96,73 @@ def test_projection_matrix_updates_when_assigning_new_projection():
assert (cam.projection_matrix == old_projmat).all()


def test_cameragroup_contains_two_cameras():
cam = CameraGroup()
assert hasattr(cam, "cam_right") and hasattr(cam, "cam_left")
def test_cameraggroups_has_all_cameras():
cameras_n = np.random.randint(1, 11)
cameras = [Camera() for _ in range(cameras_n)]

cam = CameraGroup(cameras=cameras)
assert len(cam.cameras) == cameras_n


def test_cameras_are_cameragroup_childrens():
cam = CameraGroup()
assert cam.cam_right in cam.children
assert cam.cam_left in cam.children
cameras_n = np.random.randint(1, 11)
cameras = [Camera() for _ in range(cameras_n)]

cam = CameraGroup(cameras=cameras)
for camera in cam.cameras:
assert camera in cam.children


def test_stereocameragroup_contains_two_cameras():
cam = StereoCameraGroup()
assert hasattr(cam, "right") and hasattr(cam, "left")


def test_cameras_have_correct_distance():
def test_cameras_have_correct_distance_in_stereocameragroup():
dist = 10
cam = CameraGroup(distance=dist)
cam = StereoCameraGroup(distance=dist)
assert cam.distance == dist
assert (cam.cam_right.position.x - cam.cam_left.position.x) == cam.distance
assert (cam.right.position.x - cam.left.position.x) == cam.distance

cam.distance = 20
assert (cam.cam_right.position.x - cam.cam_left.position.x) == cam.distance
assert (cam.right.position.x - cam.left.position.x) == cam.distance


def test_projection_instance_is_shared_between_cameragroup_and_its_children():
cam = CameraGroup()
assert (cam.projection == cam.cam_right.projection) and (cam.projection == cam.cam_left.projection)
def test_attributes_given_as_input_to_StereoCameraGroup():
cam = StereoCameraGroup(projection=OrthoProjection(), convergence=10.)
assert isinstance(cam.left.projection, OrthoProjection)
assert isinstance(cam.left.projection, OrthoProjection)
assert cam.convergence == 10.

cam.projection = OrthoProjection()
assert isinstance(cam.projection, OrthoProjection)
assert (cam.projection == cam.cam_right.projection) and (cam.projection == cam.cam_left.projection)


def test_projection_instance_is_not_shared_between_children_of_stereocameragroup():
cam = StereoCameraGroup()
assert (cam.left.projection is not cam.right.projection)

cam.left.projection = OrthoProjection()
assert isinstance(cam.left.projection, OrthoProjection)
assert isinstance(cam.right.projection, PerspectiveProjection)
assert (cam.left.projection is not cam.right.projection)


def test_look_at_updates_for_children():
dist = 2.
cam = CameraGroup(distance=dist)
cam = StereoCameraGroup(distance=dist)
point = np.array([0, 0, 0, 1]).reshape(-1, 1)
point[2] = -1 #np.random.randint(1, 6)

angle = np.arctan(point[2]/(cam.distance/2))[0]
cam.cam_right.rotation.y = -np.rad2deg(angle)
cam.cam_left.rotation.y = np.rad2deg(angle)
point_view_mat_left = np.dot(cam.cam_left.view_matrix, point)
point_view_mat_right = np.dot(cam.cam_right.view_matrix, point)
cam.right.rotation.y = -np.rad2deg(angle)
cam.left.rotation.y = np.rad2deg(angle)
point_view_mat_left = np.dot(cam.left.view_matrix, point)
point_view_mat_right = np.dot(cam.right.view_matrix, point)
assert (point_view_mat_left == point_view_mat_right).all()

cam2 = CameraGroup(distance=dist, look_at=point[:3])
point_view_mat_left2 = np.dot(cam2.cam_left.view_matrix, point)
point_view_mat_right2 = np.dot(cam2.cam_right.view_matrix, point)
cam2 = StereoCameraGroup(distance=dist)
cam2.look_at(*point[:3])
point_view_mat_left2 = np.dot(cam2.left.view_matrix, point)
point_view_mat_right2 = np.dot(cam2.right.view_matrix, point)
assert (point_view_mat_left == point_view_mat_left2).all() and (point_view_mat_right == point_view_mat_right2).all()


Expand Down

0 comments on commit 918615c

Please sign in to comment.