render basic test pattern

This commit is contained in:
wires 2025-04-22 08:52:10 -04:00
parent 9c7b1f9939
commit 845e1c9a5a
Signed by: wires
SSH key fingerprint: SHA256:9GtP+M3O2IivPDlw1UY872UPUuJH2gI0yG6ExBxaaiM
9 changed files with 129 additions and 16 deletions

3
.gitignore vendored
View file

@ -1,5 +1,8 @@
/target
# test artifacts
*.png
# Byte-compiled / optimized / DLL files
__pycache__/
.pytest_cache/

20
Cargo.lock generated
View file

@ -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",
]

View file

@ -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
View 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()
}
}

View file

@ -0,0 +1,6 @@
use bytemuck::{Pod, Zeroable};
use crate::color::Rgba;
unsafe impl Pod for Rgba {}
unsafe impl Zeroable for Rgba {}

View file

@ -1,14 +1,38 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(feature = "bytemuck")]
pub mod impl_bytemuck;
#[cfg(test)]
mod tests {
use super::*;
pub mod color;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
use color::Rgba;
pub struct Scene;
impl Scene {
pub fn new() -> Self {
Self
}
pub fn render(&self, width: usize, height: usize) -> Box<[Rgba]> {
let mut data = Box::new_uninit_slice(width * height);
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
View 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")

View file

@ -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"] }

View file

@ -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(())
}