Skip to content

Commit 4e3dbaa

Browse files
committed
feat: expose pen and hatch options to callers
1 parent 03190ca commit 4e3dbaa

File tree

8 files changed

+164
-104
lines changed

8 files changed

+164
-104
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ raydeon = { path = "./raydeon", version = "0.2" }
1010
pyrayeon = { path = "./pyraydeon", version = "0.1.0-alpha" }
1111

1212
anyhow = "1"
13+
bon = "3"
1314
cgmath = "0.17"
1415
collision = "0.20"
1516
env_logger = "0.11"

pyraydeon/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use pyo3::prelude::*;
22

33
macro_rules! pywrap {
44
($name:ident, $wraps:ty) => {
5-
#[derive(Debug, Clone, Copy)]
5+
#[derive(Debug, Clone)]
66
#[pyclass(frozen)]
77
pub(crate) struct $name(pub(crate) $wraps);
88

pyraydeon/src/scene.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,23 @@ use crate::linear::{ArbitrarySpace, Point2, Point3, Vec3};
99
use crate::material::Material;
1010
use crate::shapes::Geometry;
1111

12-
pywrap!(Camera, raydeon::Camera<raydeon::Perspective, raydeon::Observation>);
12+
#[derive(Debug, Clone)]
13+
#[pyclass(frozen)]
14+
pub(crate) struct Camera(pub(crate) raydeon::Camera<raydeon::Perspective, raydeon::Observation>);
15+
16+
impl ::std::ops::Deref for Camera {
17+
type Target = raydeon::Camera<raydeon::Perspective, raydeon::Observation>;
18+
19+
fn deref(&self) -> &Self::Target {
20+
&self.0
21+
}
22+
}
23+
24+
impl From<raydeon::Camera<raydeon::Perspective, raydeon::Observation>> for Camera {
25+
fn from(value: raydeon::Camera<raydeon::Perspective, raydeon::Observation>) -> Self {
26+
Self(value)
27+
}
28+
}
1329

1430
#[pymethods]
1531
impl Camera {
@@ -29,12 +45,16 @@ impl Camera {
2945
let up = Vec3::try_from(up)?;
3046
Ok(self
3147
.0
48+
.clone()
3249
.look_at(eye.cast_unit(), center.cast_unit(), up.cast_unit())
3350
.into())
3451
}
3552

3653
fn perspective(&self, fovy: f64, width: usize, height: usize, znear: f64, zfar: f64) -> Camera {
37-
self.0.perspective(fovy, width, height, znear, zfar).into()
54+
self.0
55+
.clone()
56+
.perspective(fovy, width, height, znear, zfar)
57+
.into()
3858
}
3959

4060
#[getter]
@@ -137,7 +157,7 @@ impl Scene {
137157

138158
fn render(&self, py: Python, camera: &Camera) -> Vec<LineSegment2D> {
139159
py.allow_threads(|| {
140-
let cam = self.scene.attach_camera(camera.0);
160+
let cam = self.scene.attach_camera(camera.0.clone());
141161
cam.render()
142162
.into_iter()
143163
.map(|ls| ls.cast_unit().into())
@@ -153,7 +173,7 @@ impl Scene {
153173
seed: Option<u64>,
154174
) -> Vec<LineSegment2D> {
155175
py.allow_threads(|| {
156-
let cam = self.scene.attach_camera(camera.0);
176+
let cam = self.scene.attach_camera(camera.0.clone());
157177
let cam = if let Some(seed) = seed {
158178
cam.with_seed(seed)
159179
} else {

pyraydeon/src/shapes/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl raydeon::Shape<WorldSpace, raydeon::material::Material> for PythonGeometry
206206
) -> Vec<raydeon::path::LineSegment3D<WorldSpace, raydeon::material::Material>> {
207207
let segments: Option<_> = Python::with_gil(|py| {
208208
let inner = self.slf.bind(py);
209-
let cam = Camera::from(*cam);
209+
let cam = Camera::from(cam.clone());
210210
let call_result = inner.call_method1("paths", (cam,)).unwrap();
211211

212212
let segments = call_result
@@ -238,7 +238,7 @@ impl raydeon::Shape<WorldSpace, raydeon::material::Material> for PythonGeometry
238238

239239
impl raydeon::CollisionGeometry<WorldSpace> for PythonGeometry {
240240
fn hit_by(&self, ray: &raydeon::Ray) -> Option<raydeon::HitData> {
241-
if let PythonGeometryKind::Collision { aabb: Some(aabb) } = self.kind {
241+
if let PythonGeometryKind::Collision { aabb: Some(aabb) } = &self.kind {
242242
raydeon::shapes::AxisAlignedCuboid::from(aabb.0.cast_unit()).hit_by(ray)?;
243243
}
244244
Python::with_gil(|py| {

raydeon/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ authors = ["cbgbt <[email protected]>"]
55
edition = "2021"
66

77
[dependencies]
8+
bon.workspace = true
89
cgmath.workspace = true
910
collision.workspace = true
1011
euclid.workspace = true

raydeon/src/camera.rs

Lines changed: 122 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,100 +3,48 @@ use path::SlicedSegment3D;
33

44
use crate::*;
55

6-
#[derive(Debug, Copy, Clone)]
7-
pub struct Observation {
8-
pub eye: WPoint3,
9-
pub center: WVec3,
10-
pub up: WVec3,
11-
}
12-
13-
impl Default for Observation {
14-
fn default() -> Self {
15-
Self::new((0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (0.0, 1.0, 0.0))
16-
}
17-
}
18-
19-
/// Type parameter for a camera that isn't yet looking anywhere
20-
#[derive(Debug, Copy, Clone)]
21-
pub struct NoObservation;
22-
23-
impl Observation {
24-
pub fn new(eye: impl Into<WPoint3>, center: impl Into<WVec3>, up: impl Into<WVec3>) -> Self {
25-
let eye = eye.into();
26-
let center = center.into();
27-
let up = up.into().normalize();
28-
29-
Self { eye, center, up }
30-
}
31-
32-
#[rustfmt::skip]
33-
pub fn look_matrix(&self) -> WCTransform {
34-
let Observation { eye, center, up, .. } = *self;
35-
let f = (center - eye.to_vector()).normalize();
36-
let s = f.cross(up).normalize();
37-
let u = s.cross(f).normalize();
38-
39-
CWTransform::from_array(
40-
// euclid used to let us specify things in column major order and now it doesn't.
41-
// So we're just transposing it here.
42-
CWTransform::new(
43-
s.x, u.x, -f.x, eye.x,
44-
s.y, u.y, -f.y, eye.y,
45-
s.z, u.z, -f.z, eye.z,
46-
0.0, 0.0, 0.0, 1.0,
47-
)
48-
.to_array_transposed(),
49-
)
50-
.inverse()
51-
.unwrap()
52-
}
6+
#[derive(Debug, Clone)]
7+
pub struct Camera<P, O> {
8+
pub observation: O,
9+
pub perspective: P,
10+
pub render_options: CameraOptions,
5311
}
5412

55-
#[derive(Debug, Copy, Clone)]
56-
pub struct Perspective {
57-
pub fovy: f64,
58-
pub width: usize,
59-
pub height: usize,
60-
pub aspect: f64,
61-
pub znear: f64,
62-
pub zfar: f64,
13+
#[derive(Debug, Clone)]
14+
pub struct CameraOptions {
15+
pub pen_px_size: f64,
16+
pub hatch_pixel_spacing: f64,
17+
pub hatch_pixel_chop_factor: f64,
18+
pub hatch_slice_forgiveness: usize,
19+
pub vert_hatch_brightness_scaling: f64,
20+
pub diag_hatch_brightness_scaling: f64,
6321
}
6422

65-
impl Default for Perspective {
23+
impl Default for CameraOptions {
6624
fn default() -> Self {
67-
Self::new(45.0, 1920, 1080, 0.1, 100.0)
25+
Self::defaults_for_pen_px_size(4.0)
6826
}
6927
}
7028

71-
/// Type parameter for a camera that doesn't yet have a defined perspective
72-
#[derive(Debug, Copy, Clone)]
73-
pub struct NoPerspective;
74-
75-
impl Perspective {
76-
pub fn new(fovy: f64, width: usize, height: usize, znear: f64, zfar: f64) -> Self {
77-
let aspect = width as f64 / height as f64;
29+
impl CameraOptions {
30+
pub fn defaults_for_pen_px_size(px_size: f64) -> Self {
7831
Self {
79-
fovy,
80-
width,
81-
height,
82-
aspect,
83-
znear,
84-
zfar,
32+
pen_px_size: px_size,
33+
hatch_pixel_spacing: px_size * 2.0,
34+
hatch_pixel_chop_factor: px_size / 3.0,
35+
hatch_slice_forgiveness: 1,
36+
vert_hatch_brightness_scaling: 0.8,
37+
diag_hatch_brightness_scaling: 0.46,
8538
}
8639
}
8740
}
8841

89-
#[derive(Debug, Copy, Clone)]
90-
pub struct Camera<P, O> {
91-
pub observation: O,
92-
pub perspective: P,
93-
}
94-
9542
impl Camera<NoPerspective, NoObservation> {
9643
pub fn new() -> Self {
9744
Self {
9845
observation: NoObservation,
9946
perspective: NoPerspective,
47+
render_options: Default::default(),
10048
}
10149
}
10250
}
@@ -106,6 +54,7 @@ impl Default for Camera<Perspective, Observation> {
10654
Self {
10755
observation: Observation::default(),
10856
perspective: Perspective::default(),
57+
render_options: Default::default(),
10958
}
11059
}
11160
}
@@ -117,11 +66,16 @@ impl<P, O> Camera<P, O> {
11766
center: impl Into<WVec3>,
11867
up: impl Into<WVec3>,
11968
) -> Camera<P, Observation> {
120-
let Camera { perspective, .. } = self;
69+
let Camera {
70+
perspective,
71+
render_options,
72+
..
73+
} = self;
12174
let observation = Observation::new(eye.into(), center.into(), up.into());
12275
Camera {
12376
observation,
12477
perspective,
78+
render_options,
12579
}
12680
}
12781

@@ -133,11 +87,16 @@ impl<P, O> Camera<P, O> {
13387
znear: f64,
13488
zfar: f64,
13589
) -> Camera<Perspective, O> {
136-
let Camera { observation, .. } = self;
90+
let Camera {
91+
observation,
92+
render_options,
93+
..
94+
} = self;
13795
let perspective = Perspective::new(fovy, width, height, znear, zfar);
13896
Camera {
13997
observation,
14098
perspective,
99+
render_options,
141100
}
142101
}
143102
}
@@ -185,7 +144,8 @@ impl Camera<Perspective, Observation> {
185144

186145
let chunk_count = canvas_points
187146
.map(|(p1t, p2t)| {
188-
let rough_chop_size = (p2t - p1t).length() / (PEN_PX_SIZE / 2.0);
147+
let rough_chop_size =
148+
(p2t - p1t).length() / (self.render_options.pen_px_size / 2.0);
189149
rough_chop_size.round_ties_even() as usize
190150
})
191151
.unwrap_or_else(|| {
@@ -233,6 +193,89 @@ impl<O> Camera<Perspective, O> {
233193
}
234194
}
235195

196+
#[derive(Debug, Copy, Clone)]
197+
pub struct Observation {
198+
pub eye: WPoint3,
199+
pub center: WVec3,
200+
pub up: WVec3,
201+
}
202+
203+
impl Default for Observation {
204+
fn default() -> Self {
205+
Self::new((0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (0.0, 1.0, 0.0))
206+
}
207+
}
208+
209+
/// Type parameter for a camera that isn't yet looking anywhere
210+
#[derive(Debug, Copy, Clone)]
211+
pub struct NoObservation;
212+
213+
impl Observation {
214+
pub fn new(eye: impl Into<WPoint3>, center: impl Into<WVec3>, up: impl Into<WVec3>) -> Self {
215+
let eye = eye.into();
216+
let center = center.into();
217+
let up = up.into().normalize();
218+
219+
Self { eye, center, up }
220+
}
221+
222+
#[rustfmt::skip]
223+
pub fn look_matrix(&self) -> WCTransform {
224+
let Observation { eye, center, up, .. } = *self;
225+
let f = (center - eye.to_vector()).normalize();
226+
let s = f.cross(up).normalize();
227+
let u = s.cross(f).normalize();
228+
229+
CWTransform::from_array(
230+
// euclid used to let us specify things in column major order and now it doesn't.
231+
// So we're just transposing it here.
232+
CWTransform::new(
233+
s.x, u.x, -f.x, eye.x,
234+
s.y, u.y, -f.y, eye.y,
235+
s.z, u.z, -f.z, eye.z,
236+
0.0, 0.0, 0.0, 1.0,
237+
)
238+
.to_array_transposed(),
239+
)
240+
.inverse()
241+
.unwrap()
242+
}
243+
}
244+
245+
#[derive(Debug, Copy, Clone)]
246+
pub struct Perspective {
247+
pub fovy: f64,
248+
pub width: usize,
249+
pub height: usize,
250+
pub aspect: f64,
251+
pub znear: f64,
252+
pub zfar: f64,
253+
}
254+
255+
impl Default for Perspective {
256+
fn default() -> Self {
257+
Self::new(45.0, 1920, 1080, 0.1, 100.0)
258+
}
259+
}
260+
261+
/// Type parameter for a camera that doesn't yet have a defined perspective
262+
#[derive(Debug, Copy, Clone)]
263+
pub struct NoPerspective;
264+
265+
impl Perspective {
266+
pub fn new(fovy: f64, width: usize, height: usize, znear: f64, zfar: f64) -> Self {
267+
let aspect = width as f64 / height as f64;
268+
Self {
269+
fovy,
270+
width,
271+
height,
272+
aspect,
273+
znear,
274+
zfar,
275+
}
276+
}
277+
}
278+
236279
#[rustfmt::skip]
237280
fn frustum(
238281
l: f64,

raydeon/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ pub use path::{LineSegment3D, PathMeta};
1717
pub use ray::{HitData, Ray};
1818
pub use scene::{Scene, SceneGeometry, SceneLighting};
1919

20-
// The pixel fidelity of the drawing instrument.
21-
// TODO: Make this configurable
22-
pub const PEN_PX_SIZE: f64 = 4.0;
23-
2420
#[cfg(test)]
2521
pub(crate) static EPSILON: f64 = 0.004;
2622

0 commit comments

Comments
 (0)