render basic test pattern
This commit is contained in:
parent
9c7b1f9939
commit
845e1c9a5a
9 changed files with 129 additions and 16 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,8 @@
|
|||
/target
|
||||
|
||||
# test artifacts
|
||||
*.png
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
|
|
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -8,12 +8,27 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.30.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0e9b6647e9b41d3a5ef02964c6be01311a7f2472fea40595c635c6d046c259e"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
@ -35,11 +50,16 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
|||
[[package]]
|
||||
name = "mechthild_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mechthild_py"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"mechthild_core",
|
||||
"pyo3",
|
||||
]
|
||||
|
|
|
@ -7,3 +7,9 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bytemuck = { version = "1.22.0", optional = true }
|
||||
glam = "0.30.2"
|
||||
|
||||
[features]
|
||||
fast-math = ["glam/fast-math"]
|
||||
bytemuck = ["dep:bytemuck", "glam/bytemuck"]
|
||||
|
|
29
core/src/color.rs
Normal file
29
core/src/color.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Rgba {
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
}
|
||||
|
||||
impl Rgba {
|
||||
pub const BLACK: Self = Self::new(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Rgba {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple(stringify!(Rgba))
|
||||
.field(&self.r)
|
||||
.field(&self.g)
|
||||
.field(&self.b)
|
||||
.field(&self.a)
|
||||
.finish()
|
||||
}
|
||||
}
|
6
core/src/impl_bytemuck.rs
Normal file
6
core/src/impl_bytemuck.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::color::Rgba;
|
||||
|
||||
unsafe impl Pod for Rgba {}
|
||||
unsafe impl Zeroable for Rgba {}
|
|
@ -1,14 +1,38 @@
|
|||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
#[cfg(feature = "bytemuck")]
|
||||
pub mod impl_bytemuck;
|
||||
|
||||
pub mod color;
|
||||
|
||||
use color::Rgba;
|
||||
|
||||
pub struct Scene;
|
||||
|
||||
impl Scene {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
pub fn render(&self, width: usize, height: usize) -> Box<[Rgba]> {
|
||||
let mut data = Box::new_uninit_slice(width * height);
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
for (i, pixel) in data.iter_mut().enumerate() {
|
||||
let x = (i % width) as f32;
|
||||
let y = (i / width) as f32;
|
||||
|
||||
pixel.write(Rgba::new(
|
||||
x / (width - 1) as f32,
|
||||
y / (height - 1) as f32,
|
||||
0.5,
|
||||
1.0,
|
||||
));
|
||||
}
|
||||
|
||||
unsafe { data.assume_init() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Scene {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
11
examples/basic.py
Normal file
11
examples/basic.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from PIL import Image
|
||||
from mechthild import Scene
|
||||
|
||||
WIDTH = 800
|
||||
HEIGHT = 600
|
||||
|
||||
render_result = Scene().render(WIDTH, HEIGHT)
|
||||
rgba = bytes([int(v * 255) for v in render_result])
|
||||
|
||||
img = Image.frombuffer('RGBA', (WIDTH, HEIGHT), rgba)
|
||||
img.save("test.png")
|
|
@ -13,4 +13,5 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
pyo3 = "0.24.0"
|
||||
mechthild_core = { path = "../core" }
|
||||
mechthild_core = { path = "../core", features = ["bytemuck"] }
|
||||
bytemuck = { version = "1.22.0", features = ["extern_crate_alloc"] }
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
use bytemuck::allocation::cast_slice_box;
|
||||
use mechthild_core::{Scene, color::Rgba};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// Formats the sum of two numbers as string.
|
||||
#[pyfunction]
|
||||
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
|
||||
Ok(mechthild_core::add(a, b).to_string())
|
||||
#[pyclass(name = "Scene")]
|
||||
struct PyScene(Scene);
|
||||
|
||||
#[pymethods]
|
||||
impl PyScene {
|
||||
#[new]
|
||||
pub fn new() -> Self {
|
||||
Self(Scene::new())
|
||||
}
|
||||
|
||||
pub fn render(&self, width: usize, height: usize) -> Vec<f32> {
|
||||
let result = self.0.render(width, height);
|
||||
|
||||
cast_slice_box::<Rgba, f32>(result).into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Python module implemented in Rust.
|
||||
#[pymodule]
|
||||
fn mechthild(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
|
||||
m.add_class::<PyScene>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue