Skip to content

Commit

Permalink
refactor: remove unneeded generics, use more builders
Browse files Browse the repository at this point in the history
This change removes the need for generic path metadata from shapes.
raydeon's Materials allow associating an arbitrary `usize`, which can be
used to accomplish the same thing via a lookup table after rendering.
Removing the trait bounds makes it easier to maintain the library.

This change also introduces the builder pattern to most of raydeon's
shape types.
  • Loading branch information
cbgbt committed Nov 27, 2024
1 parent df32410 commit a27899f
Show file tree
Hide file tree
Showing 21 changed files with 448 additions and 493 deletions.
36 changes: 21 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,27 @@ fn main() {

let scene = Scene::new()
.geometry(vec![
Arc::new(AxisAlignedCuboid::tagged(
(-1.0, -1.0, -1.0),
(1.0, 1.0, 1.0),
Material::new(3.0, 2.0, 2.0, 0),
)),
Arc::new(AxisAlignedCuboid::tagged(
(1.8, -1.0, -1.0),
(3.8, 1.0, 1.0),
Material::new(2.0, 2.0, 2.0, 0),
)),
Arc::new(AxisAlignedCuboid::tagged(
(-1.4, 1.8, -1.0),
(0.6, 3.8, 1.0),
Material::new(3.0, 2.0, 2.0, 0),
)),
Arc::new(
AxisAlignedCuboid::new()
.min((-1.0, -1.0, -1.0))
.max((1.0, 1.0, 1.0))
.material(Material::new(3.0, 2.0, 2.0, 0))
.build(),
),
Arc::new(
AxisAlignedCuboid::new()
.min((1.8, -1.0, -1.0))
.max((3.8, 1.0, 1.0))
.material(Material::new(2.0, 2.0, 2.0, 0))
.build(),
),
Arc::new(
AxisAlignedCuboid::new()
.min((-1.4, 1.8, -1.0))
.max((0.6, 3.8, 1.0))
.material(Material::new(3.0, 2.0, 2.0, 0))
.build(),
),
])
.lighting(
SceneLighting::new()
Expand Down
37 changes: 19 additions & 18 deletions pyraydeon/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use numpy::{Ix1, PyArray, PyReadonlyArray1};
use pyo3::prelude::*;
use raydeon::{SceneLighting, WorldSpace};
use raydeon::SceneLighting;

use crate::light::PointLight;
use crate::linear::{ArbitrarySpace, Point2, Point3, Vec3};
Expand Down Expand Up @@ -108,7 +108,7 @@ impl Camera {

#[pyclass(frozen)]
pub(crate) struct Scene {
scene: Arc<raydeon::Scene<raydeon::Material>>,
scene: Arc<raydeon::Scene>,
}

#[pymethods]
Expand All @@ -123,16 +123,15 @@ impl Scene {
) -> PyResult<Self> {
let geometry = geometry.unwrap_or_default();
let lights = lights.unwrap_or_default();
let geometry: Vec<Arc<dyn raydeon::Shape<WorldSpace, raydeon::material::Material>>> =
geometry
.into_iter()
.map(|g| {
let geom: Py<Geometry> = g.extract(py)?;
let raydeon_shape = geom.borrow(py);
let raydeon_shape = raydeon_shape.geometry(g);
Ok(raydeon_shape)
})
.collect::<PyResult<_>>()?;
let geometry: Vec<Arc<dyn raydeon::Shape>> = geometry
.into_iter()
.map(|g| {
let geom: Py<Geometry> = g.extract(py)?;
let raydeon_shape = geom.borrow(py);
let raydeon_shape = raydeon_shape.geometry(g);
Ok(raydeon_shape)
})
.collect::<PyResult<_>>()?;
let lights: Vec<Arc<dyn raydeon::Light>> = lights
.into_iter()
.map(|l| Arc::new(l.0) as Arc<dyn raydeon::lights::Light>)
Expand Down Expand Up @@ -216,7 +215,7 @@ impl LineSegment2D {
}
}

pywrap!(LineSegment3D, raydeon::path::LineSegment3D<ArbitrarySpace, raydeon::material::Material>);
pywrap!(LineSegment3D, raydeon::path::LineSegment3D<ArbitrarySpace>);

#[pymethods]
impl LineSegment3D {
Expand All @@ -229,12 +228,14 @@ impl LineSegment3D {
) -> PyResult<Self> {
let p1 = Point3::try_from(p1)?;
let p2 = Point3::try_from(p2)?;
Ok(raydeon::path::LineSegment3D::tagged(
p1.cast_unit(),
p2.cast_unit(),
material.unwrap_or_default().0,
Ok(
raydeon::path::LineSegment3D::new(
p1.cast_unit(),
p2.cast_unit(),
material.map(|i| i.0),
)
.into(),
)
.into())
}

#[getter]
Expand Down
25 changes: 10 additions & 15 deletions pyraydeon/src/shapes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ use crate::material::Material;
use crate::ray::{HitData, Ray, AABB3};
use crate::scene::{Camera, LineSegment3D};

type RMaterial = raydeon::material::Material;

#[derive(Debug)]
enum InnerGeometry {
Native(Arc<dyn raydeon::Shape<WorldSpace, RMaterial>>),
Native(Arc<dyn raydeon::Shape>),
Py,
}

Expand All @@ -27,7 +25,7 @@ pub(crate) struct Geometry {
}

impl Geometry {
pub(crate) fn native(geom: Arc<dyn raydeon::Shape<WorldSpace, RMaterial>>) -> Self {
pub(crate) fn native(geom: Arc<dyn raydeon::Shape>) -> Self {
let geom = InnerGeometry::Native(geom);
Self { geom }
}
Expand All @@ -37,7 +35,7 @@ impl Geometry {
Self { geom }
}

pub(crate) fn geometry(&self, obj: PyObject) -> Arc<dyn raydeon::Shape<WorldSpace, RMaterial>> {
pub(crate) fn geometry(&self, obj: PyObject) -> Arc<dyn raydeon::Shape> {
match &self.geom {
InnerGeometry::Native(ref geom) => Arc::clone(geom),
InnerGeometry::Py => Arc::new(PythonGeometry::new(obj, PythonGeometryKind::Draw)),
Expand Down Expand Up @@ -90,7 +88,7 @@ impl Geometry {

#[derive(Debug)]
enum InnerCollisionGeometry {
Native(Arc<dyn raydeon::CollisionGeometry<WorldSpace>>),
Native(Arc<dyn raydeon::CollisionGeometry>),
Py,
}

Expand All @@ -101,7 +99,7 @@ pub(crate) struct CollisionGeometry {
}

impl CollisionGeometry {
pub(crate) fn native(geom: Arc<dyn raydeon::CollisionGeometry<WorldSpace>>) -> Self {
pub(crate) fn native(geom: Arc<dyn raydeon::CollisionGeometry>) -> Self {
let geom = InnerCollisionGeometry::Native(geom);
Self { geom }
}
Expand Down Expand Up @@ -176,8 +174,8 @@ impl PythonGeometry {
}
}

impl raydeon::Shape<WorldSpace, raydeon::material::Material> for PythonGeometry {
fn collision_geometry(&self) -> Option<Vec<Arc<dyn raydeon::CollisionGeometry<WorldSpace>>>> {
impl raydeon::Shape for PythonGeometry {
fn collision_geometry(&self) -> Option<Vec<Arc<dyn raydeon::CollisionGeometry>>> {
let collision_geometry: Option<_> = Python::with_gil(|py| {
let inner = self.slf.bind(py);
let call_result = inner.call_method1("collision_geometry", ()).ok()?;
Expand All @@ -189,7 +187,7 @@ impl raydeon::Shape<WorldSpace, raydeon::material::Material> for PythonGeometry
.map(|obj| {
Ok(
Arc::new(PythonGeometry::as_collision_geometry(obj?.into_py(py)))
as Arc<dyn raydeon::CollisionGeometry<WorldSpace>>,
as Arc<dyn raydeon::CollisionGeometry>,
)
})
.collect::<PyResult<_>>()
Expand All @@ -200,10 +198,7 @@ impl raydeon::Shape<WorldSpace, raydeon::material::Material> for PythonGeometry
collision_geometry
}

fn paths(
&self,
cam: &raydeon::Camera,
) -> Vec<raydeon::path::LineSegment3D<WorldSpace, raydeon::material::Material>> {
fn paths(&self, cam: &raydeon::Camera) -> Vec<raydeon::path::LineSegment3D<WorldSpace>> {
let segments: Option<_> = Python::with_gil(|py| {
let inner = self.slf.bind(py);
let cam = Camera::from(cam.clone());
Expand Down Expand Up @@ -236,7 +231,7 @@ impl raydeon::Shape<WorldSpace, raydeon::material::Material> for PythonGeometry
}
}

impl raydeon::CollisionGeometry<WorldSpace> for PythonGeometry {
impl raydeon::CollisionGeometry for PythonGeometry {
fn hit_by(&self, ray: &raydeon::Ray) -> Option<raydeon::HitData> {
if let PythonGeometryKind::Collision { aabb: Some(aabb) } = &self.kind {
raydeon::shapes::AxisAlignedCuboid::from(aabb.0.cast_unit()).hit_by(ray)?;
Expand Down
106 changes: 55 additions & 51 deletions pyraydeon/src/shapes/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,21 @@ use crate::material::Material;
use numpy::{Ix1, PyArray, PyArrayLike1, PyArrayLike2};
use pyo3::exceptions::PyIndexError;
use pyo3::prelude::*;
use raydeon::WorldSpace;
use std::sync::Arc;

type RMaterial = raydeon::material::Material;

#[pyclass(frozen, extends=Geometry, subclass)]
pub(crate) struct AxisAlignedCuboid(pub(crate) Arc<raydeon::shapes::AxisAlignedCuboid<RMaterial>>);
pub(crate) struct AxisAlignedCuboid(pub(crate) Arc<raydeon::shapes::AxisAlignedCuboid>);

impl ::std::ops::Deref for AxisAlignedCuboid {
type Target = Arc<raydeon::shapes::AxisAlignedCuboid<RMaterial>>;
type Target = Arc<raydeon::shapes::AxisAlignedCuboid>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Arc<raydeon::shapes::AxisAlignedCuboid<RMaterial>>> for AxisAlignedCuboid {
fn from(value: Arc<raydeon::shapes::AxisAlignedCuboid<RMaterial>>) -> Self {
impl From<Arc<raydeon::shapes::AxisAlignedCuboid>> for AxisAlignedCuboid {
fn from(value: Arc<raydeon::shapes::AxisAlignedCuboid>) -> Self {
Self(value)
}
}
Expand All @@ -38,32 +35,32 @@ impl AxisAlignedCuboid {
let min: Vec3 = min.try_into()?;
let max: Vec3 = max.try_into()?;

let shape = Arc::new(raydeon::shapes::AxisAlignedCuboid::tagged(
min.cast_unit(),
max.cast_unit(),
material.unwrap_or_default().0,
));
let geom =
Geometry::native(Arc::clone(&shape)
as Arc<dyn raydeon::Shape<WorldSpace, raydeon::material::Material>>);
let shape = Arc::new(
raydeon::shapes::AxisAlignedCuboid::new()
.min(min.cast_unit())
.max(max.cast_unit())
.material(material.map(|m| m.0).unwrap_or_default())
.build(),
);
let geom = Geometry::native(Arc::clone(&shape) as Arc<dyn raydeon::Shape>);

Ok((Self(shape), geom))
}
}

#[pyclass(frozen, extends=Geometry, subclass)]
pub(crate) struct Tri(pub(crate) Arc<raydeon::shapes::Triangle<RMaterial>>);
pub(crate) struct Tri(pub(crate) Arc<raydeon::shapes::Triangle>);

impl ::std::ops::Deref for Tri {
type Target = Arc<raydeon::shapes::Triangle<RMaterial>>;
type Target = Arc<raydeon::shapes::Triangle>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Arc<raydeon::shapes::Triangle<RMaterial>>> for Tri {
fn from(value: Arc<raydeon::shapes::Triangle<RMaterial>>) -> Self {
impl From<Arc<raydeon::shapes::Triangle>> for Tri {
fn from(value: Arc<raydeon::shapes::Triangle>) -> Self {
Self(value)
}
}
Expand All @@ -82,14 +79,15 @@ impl Tri {
let p2: Point3 = p2.try_into()?;
let p3: Point3 = p3.try_into()?;

let shape = Arc::new(raydeon::shapes::Triangle::tagged(
p1.cast_unit(),
p2.cast_unit(),
p3.cast_unit(),
material.unwrap_or_default().0,
));
let geom =
Geometry::native(Arc::clone(&shape) as Arc<dyn raydeon::Shape<WorldSpace, RMaterial>>);
let shape = Arc::new(
raydeon::shapes::Triangle::new()
.v0(p1.cast_unit())
.v1(p2.cast_unit())
.v2(p3.cast_unit())
.material(material.map(|m| m.0).unwrap_or_default())
.build(),
);
let geom = Geometry::native(Arc::clone(&shape) as Arc<dyn raydeon::Shape>);
Ok((Self(shape), geom))
}
}
Expand Down Expand Up @@ -121,13 +119,14 @@ impl Plane {
let point: Point3 = point.try_into()?;
let normal: Vec3 = normal.try_into()?;

let shape = Arc::new(raydeon::shapes::Plane::new(
point.0.cast_unit(),
normal.0.cast_unit(),
));
let geom = CollisionGeometry::native(
Arc::clone(&shape) as Arc<dyn raydeon::CollisionGeometry<WorldSpace>>
let shape = Arc::new(
raydeon::shapes::Plane::new()
.point(point.0.cast_unit())
.normal(normal.0.cast_unit())
.build(),
);
let geom =
CollisionGeometry::native(Arc::clone(&shape) as Arc<dyn raydeon::CollisionGeometry>);
Ok((Self(shape), geom))
}

Expand Down Expand Up @@ -162,13 +161,17 @@ impl From<Arc<raydeon::shapes::Sphere>> for Sphere {
#[pymethods]
impl Sphere {
#[new]
fn new(point: &Bound<'_, PyAny>, radius: f64) -> PyResult<(Self, CollisionGeometry)> {
let point: Point3 = point.try_into()?;

let shape = Arc::new(raydeon::shapes::Sphere::new(point.0.cast_unit(), radius));
let geom = CollisionGeometry::native(
Arc::clone(&shape) as Arc<dyn raydeon::CollisionGeometry<WorldSpace>>
fn new(center: &Bound<'_, PyAny>, radius: f64) -> PyResult<(Self, CollisionGeometry)> {
let center: Point3 = center.try_into()?;

let shape = Arc::new(
raydeon::shapes::Sphere::new()
.center(center.0.cast_unit())
.radius(radius)
.build(),
);
let geom =
CollisionGeometry::native(Arc::clone(&shape) as Arc<dyn raydeon::CollisionGeometry>);
Ok((Self(shape), geom))
}

Expand All @@ -184,18 +187,18 @@ impl Sphere {
}

#[pyclass(frozen, extends=Geometry, subclass)]
pub(crate) struct Quad(pub(crate) Arc<raydeon::shapes::Quad<RMaterial>>);
pub(crate) struct Quad(pub(crate) Arc<raydeon::shapes::Quad>);

impl ::std::ops::Deref for Quad {
type Target = Arc<raydeon::shapes::Quad<RMaterial>>;
type Target = Arc<raydeon::shapes::Quad>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Arc<raydeon::shapes::Quad<RMaterial>>> for Quad {
fn from(value: Arc<raydeon::shapes::Quad<RMaterial>>) -> Self {
impl From<Arc<raydeon::shapes::Quad>> for Quad {
fn from(value: Arc<raydeon::shapes::Quad>) -> Self {
Self(value)
}
}
Expand Down Expand Up @@ -241,14 +244,15 @@ impl Quad {
})
.map(|dims| [dims[0], dims[1]])?;

let shape = Arc::new(raydeon::shapes::Quad::tagged(
origin.0.cast_unit(),
basis,
dims,
material.unwrap_or_default().0,
));
let geom =
Geometry::native(Arc::clone(&shape) as Arc<dyn raydeon::Shape<WorldSpace, RMaterial>>);
let shape = Arc::new(
raydeon::shapes::Quad::new()
.origin(origin.0.cast_unit())
.basis(basis)
.dims(dims)
.material(material.map(|m| m.0).unwrap_or_default())
.build(),
);
let geom = Geometry::native(Arc::clone(&shape) as Arc<dyn raydeon::Shape>);
Ok((Self(shape), geom))
}
}
Loading

0 comments on commit a27899f

Please sign in to comment.