Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/processing_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod error;
#[unsafe(no_mangle)]
pub extern "C" fn processing_init() {
error::clear_error();
error::check(init);
error::check(|| init(Config::default()));
}

/// Create a WebGPU surface from a macOS NSWindow handle.
Expand Down
12 changes: 12 additions & 0 deletions crates/processing_pyo3/examples/background_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from processing import *

def setup():
size(800, 600)

def draw():
background(220)
image("images/logo.png")


# TODO: this should happen implicitly on module load somehow
run()
12 changes: 10 additions & 2 deletions crates/processing_pyo3/src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ impl Drop for Graphics {
#[pymethods]
impl Graphics {
#[new]
pub fn new(width: u32, height: u32) -> PyResult<Self> {
pub fn new(width: u32, height: u32, asset_path: &str) -> PyResult<Self> {
let glfw_ctx =
GlfwContext::new(width, height).map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;

init().map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
let mut config = Config::new();
config.set(ConfigKey::AssetRootPath, asset_path.to_string());
init(config).map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;

let surface = glfw_ctx
.create_surface(width, height, 1.0)
Expand Down Expand Up @@ -122,6 +124,12 @@ impl Graphics {
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

pub fn image(&self, file: &str) -> PyResult<()> {
let image = image_load(file).unwrap();
graphics_record_command(self.entity, DrawCommand::BackgroundImage(image))
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

pub fn push_matrix(&self) -> PyResult<()> {
graphics_record_command(self.entity, DrawCommand::PushMatrix)
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
Expand Down
35 changes: 33 additions & 2 deletions crates/processing_pyo3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
//! receiver.
//!
//! To allow Python users to create a similar experience, we provide module-level
//! functions that forward to a singleton Graphics object bepub(crate) pub(crate) hind the scenes.
//! functions that forward to a singleton Graphics object pub(crate) behind the scenes.
mod glfw;
mod graphics;

use graphics::{Graphics, get_graphics, get_graphics_mut};
use pyo3::{exceptions::PyRuntimeError, prelude::*};

use std::env;

#[pymodule]
fn processing(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Graphics>()?;
Expand All @@ -26,13 +28,36 @@ fn processing(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(no_stroke, m)?)?;
m.add_function(wrap_pyfunction!(stroke_weight, m)?)?;
m.add_function(wrap_pyfunction!(rect, m)?)?;
m.add_function(wrap_pyfunction!(image, m)?)?;
Ok(())
}

fn get_asset_root() -> PyResult<String> {
if let Ok(val) = env::var("PROCESSING_ASSET_ROOT") {
return Ok(val);
}

Python::attach(|py| {
let sys = PyModule::import(py, "sys")?;
let argv: Vec<String> = sys.getattr("argv")?.extract()?;
let filename: &str = argv[0].as_str();
let os = PyModule::import(py, "os")?;
let path = os.getattr("path")?;
let dirname = path.getattr("dirname")?.call1((filename,))?;
let abspath = path.getattr("abspath")?.call1((dirname,))?;
let asset_root = path
.getattr("join")?
.call1((abspath, "assets"))?
.to_string();
Ok(asset_root)
})
}

#[pyfunction]
#[pyo3(pass_module)]
fn size(module: &Bound<'_, PyModule>, width: u32, height: u32) -> PyResult<()> {
let graphics = Graphics::new(width, height)?;
let asset_path: String = get_asset_root()?;
let graphics = Graphics::new(width, height, asset_path.as_str())?;
module.setattr("_graphics", graphics)?;
Ok(())
}
Expand Down Expand Up @@ -122,3 +147,9 @@ fn rect(
) -> PyResult<()> {
get_graphics(module)?.rect(x, y, w, h, tl, tr, br, bl)
}

#[pyfunction]
#[pyo3(pass_module, signature = (image_file))]
fn image(module: &Bound<'_, PyModule>, image_file: &str) -> PyResult<()> {
get_graphics(module)?.image(image_file)
}
2 changes: 1 addition & 1 deletion crates/processing_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ windows = { version = "0.58", features = ["Win32_Foundation", "Win32_System_Libr
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement"] }
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement"] }
48 changes: 48 additions & 0 deletions crates/processing_render/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Options object for configuring various aspects of libprocessing.
//!
//! To add a new Config just add a new enum with associated value

use bevy::prelude::Resource;
use std::collections::HashMap;

#[derive(Clone, Hash, Eq, PartialEq)]
pub enum ConfigKey {
AssetRootPath,
}

// TODO: Consider Box<dyn Any> instead of String
#[derive(Resource)]
pub struct Config {
map: HashMap<ConfigKey, String>,
}

impl Clone for Config {
fn clone(&self) -> Self {
Config {
map: self.map.clone(),
}
}
}

impl Config {
pub fn new() -> Self {
// TODO consider defaults
Config {
map: HashMap::new(),
}
}

pub fn get(&self, k: ConfigKey) -> Option<&String> {
self.map.get(&k)
}

pub fn set(&mut self, k: ConfigKey, v: String) {
self.map.insert(k, v);
}
}

impl Default for Config {
fn default() -> Self {
Self::new()
}
}
13 changes: 12 additions & 1 deletion crates/processing_render/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::path::PathBuf;

use bevy::{
asset::{
LoadState, RenderAssetUsages, handle_internal_asset_events, io::embedded::GetAssetServer,
AssetPath, LoadState, RenderAssetUsages, handle_internal_asset_events,
io::{AssetSourceId, embedded::GetAssetServer},
},
ecs::{entity::EntityHashMap, system::RunSystemOnce},
prelude::*,
Expand All @@ -23,6 +24,7 @@ use bevy::{
};
use half::f16;

use crate::config::{Config, ConfigKey};
use crate::error::{ProcessingError, Result};

pub struct ImagePlugin;
Expand Down Expand Up @@ -138,7 +140,16 @@ pub fn from_handle(
}

pub fn load(In(path): In<PathBuf>, world: &mut World) -> Result<Entity> {
let config = world.resource_mut::<Config>();
let path: AssetPath = match config.get(ConfigKey::AssetRootPath) {
Some(_) => {
AssetPath::from_path_buf(path).with_source(AssetSourceId::from("assets_directory"))
}
None => AssetPath::from_path_buf(path),
};

let handle: Handle<bevy::image::Image> = world.get_asset_server().load(path);

while let LoadState::Loading = world.get_asset_server().load_state(&handle) {
world.run_system_once(handle_internal_asset_events).unwrap();
}
Expand Down
20 changes: 16 additions & 4 deletions crates/processing_render/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod config;
pub mod error;
mod graphics;
pub mod image;
Expand All @@ -6,11 +7,13 @@ mod surface;

use std::{cell::RefCell, num::NonZero, path::PathBuf, sync::OnceLock};

use config::*;

#[cfg(not(target_arch = "wasm32"))]
use bevy::log::tracing_subscriber;
use bevy::{
app::{App, AppExit},
asset::AssetEventSystems,
asset::{AssetEventSystems, io::AssetSourceBuilder},
prelude::*,
render::render_resource::{Extent3d, TextureFormat},
};
Expand Down Expand Up @@ -202,9 +205,11 @@ pub fn surface_resize(graphics_entity: Entity, width: u32, height: u32) -> error
})
}

fn create_app() -> App {
fn create_app(config: Config) -> App {
let mut app = App::new();

app.insert_resource(config.clone());

#[cfg(not(target_arch = "wasm32"))]
let plugins = DefaultPlugins
.build()
Expand All @@ -227,6 +232,13 @@ fn create_app() -> App {
..default()
});

if let Some(asset_path) = config.get(ConfigKey::AssetRootPath) {
app.register_asset_source(
"assets_directory",
AssetSourceBuilder::platform_default(asset_path, None),
);
}

app.add_plugins(plugins);
app.add_plugins((ImagePlugin, GraphicsPlugin, SurfacePlugin));
app.add_systems(First, (clear_transient_meshes, activate_cameras))
Expand Down Expand Up @@ -258,13 +270,13 @@ fn set_app(app: App) {
/// Initialize the app, if not already initialized. Must be called from the main thread and cannot
/// be called concurrently from multiple threads.
#[cfg(not(target_arch = "wasm32"))]
pub fn init() -> error::Result<()> {
pub fn init(config: Config) -> error::Result<()> {
setup_tracing()?;
if is_already_init()? {
return Ok(());
}

let mut app = create_app();
let mut app = create_app(config);
app.finish();
app.cleanup();
set_app(app);
Expand Down
2 changes: 1 addition & 1 deletion examples/background_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() {

fn sketch() -> error::Result<()> {
let mut glfw_ctx = GlfwContext::new(400, 400)?;
init()?;
init(Config::default())?;

let width = 400;
let height = 400;
Expand Down
2 changes: 1 addition & 1 deletion examples/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() {

fn sketch() -> error::Result<()> {
let mut glfw_ctx = GlfwContext::new(400, 400)?;
init()?;
init(Config::default())?;

let width = 400;
let height = 400;
Expand Down
2 changes: 1 addition & 1 deletion examples/transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() {

fn sketch() -> error::Result<()> {
let mut glfw_ctx = GlfwContext::new(400, 400)?;
init()?;
init(Config::default())?;

let surface = glfw_ctx.create_surface(400, 400, 1.0)?;
let graphics = graphics_create(surface, 400, 400)?;
Expand Down
2 changes: 1 addition & 1 deletion examples/update_pixels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() {

fn sketch() -> error::Result<()> {
let mut glfw_ctx = GlfwContext::new(100, 100)?;
init()?;
init(Config::default())?;

let width = 100;
let height = 100;
Expand Down
4 changes: 3 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export PROCESSING_ASSET_ROOT := `realpath ./assets`

default:
@just --list

py-build:
cd crates/processing_pyo3 && uv run maturin develop

py-run file: py-build
cd crates/processing_pyo3 && uv run python ../../{{file}}
cd crates/processing_pyo3 && uv run python ./examples/{{file}}

wasm-build:
wasm-pack build crates/processing_wasm --target web --out-dir ../../target/wasm
Expand Down
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub use bevy::prelude::default;
pub use processing_render::{render::command::DrawCommand, *};
pub use processing_render::{config::*, render::command::DrawCommand, *};