Skip to content

Commit 4af8d5d

Browse files
authored
background_image example in python (#46)
1 parent f77cc47 commit 4af8d5d

File tree

14 files changed

+141
-17
lines changed

14 files changed

+141
-17
lines changed

crates/processing_ffi/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod error;
1717
#[unsafe(no_mangle)]
1818
pub extern "C" fn processing_init() {
1919
error::clear_error();
20-
error::check(init);
20+
error::check(|| init(Config::default()));
2121
}
2222

2323
/// Create a WebGPU surface from a macOS NSWindow handle.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from processing import *
2+
3+
def setup():
4+
size(800, 600)
5+
6+
def draw():
7+
background(220)
8+
image("images/logo.png")
9+
10+
11+
# TODO: this should happen implicitly on module load somehow
12+
run()

crates/processing_pyo3/src/graphics.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ impl Drop for Graphics {
3838
#[pymethods]
3939
impl Graphics {
4040
#[new]
41-
pub fn new(width: u32, height: u32) -> PyResult<Self> {
41+
pub fn new(width: u32, height: u32, asset_path: &str) -> PyResult<Self> {
4242
let glfw_ctx =
4343
GlfwContext::new(width, height).map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
4444

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

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

127+
pub fn image(&self, file: &str) -> PyResult<()> {
128+
let image = image_load(file).unwrap();
129+
graphics_record_command(self.entity, DrawCommand::BackgroundImage(image))
130+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
131+
}
132+
125133
pub fn push_matrix(&self) -> PyResult<()> {
126134
graphics_record_command(self.entity, DrawCommand::PushMatrix)
127135
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))

crates/processing_pyo3/src/lib.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
//! receiver.
88
//!
99
//! To allow Python users to create a similar experience, we provide module-level
10-
//! functions that forward to a singleton Graphics object bepub(crate) pub(crate) hind the scenes.
10+
//! functions that forward to a singleton Graphics object pub(crate) behind the scenes.
1111
mod glfw;
1212
mod graphics;
1313

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

17+
use std::env;
18+
1719
#[pymodule]
1820
fn processing(m: &Bound<'_, PyModule>) -> PyResult<()> {
1921
m.add_class::<Graphics>()?;
@@ -26,13 +28,36 @@ fn processing(m: &Bound<'_, PyModule>) -> PyResult<()> {
2628
m.add_function(wrap_pyfunction!(no_stroke, m)?)?;
2729
m.add_function(wrap_pyfunction!(stroke_weight, m)?)?;
2830
m.add_function(wrap_pyfunction!(rect, m)?)?;
31+
m.add_function(wrap_pyfunction!(image, m)?)?;
2932
Ok(())
3033
}
3134

35+
fn get_asset_root() -> PyResult<String> {
36+
if let Ok(val) = env::var("PROCESSING_ASSET_ROOT") {
37+
return Ok(val);
38+
}
39+
40+
Python::attach(|py| {
41+
let sys = PyModule::import(py, "sys")?;
42+
let argv: Vec<String> = sys.getattr("argv")?.extract()?;
43+
let filename: &str = argv[0].as_str();
44+
let os = PyModule::import(py, "os")?;
45+
let path = os.getattr("path")?;
46+
let dirname = path.getattr("dirname")?.call1((filename,))?;
47+
let abspath = path.getattr("abspath")?.call1((dirname,))?;
48+
let asset_root = path
49+
.getattr("join")?
50+
.call1((abspath, "assets"))?
51+
.to_string();
52+
Ok(asset_root)
53+
})
54+
}
55+
3256
#[pyfunction]
3357
#[pyo3(pass_module)]
3458
fn size(module: &Bound<'_, PyModule>, width: u32, height: u32) -> PyResult<()> {
35-
let graphics = Graphics::new(width, height)?;
59+
let asset_path: String = get_asset_root()?;
60+
let graphics = Graphics::new(width, height, asset_path.as_str())?;
3661
module.setattr("_graphics", graphics)?;
3762
Ok(())
3863
}
@@ -122,3 +147,9 @@ fn rect(
122147
) -> PyResult<()> {
123148
get_graphics(module)?.rect(x, y, w, h, tl, tr, br, bl)
124149
}
150+
151+
#[pyfunction]
152+
#[pyo3(pass_module, signature = (image_file))]
153+
fn image(module: &Bound<'_, PyModule>, image_file: &str) -> PyResult<()> {
154+
get_graphics(module)?.image(image_file)
155+
}

crates/processing_render/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ windows = { version = "0.58", features = ["Win32_Foundation", "Win32_System_Libr
3232
wasm-bindgen = "0.2"
3333
wasm-bindgen-futures = "0.4"
3434
js-sys = "0.3"
35-
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement"] }
35+
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement"] }
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Options object for configuring various aspects of libprocessing.
2+
//!
3+
//! To add a new Config just add a new enum with associated value
4+
5+
use bevy::prelude::Resource;
6+
use std::collections::HashMap;
7+
8+
#[derive(Clone, Hash, Eq, PartialEq)]
9+
pub enum ConfigKey {
10+
AssetRootPath,
11+
}
12+
13+
// TODO: Consider Box<dyn Any> instead of String
14+
#[derive(Resource)]
15+
pub struct Config {
16+
map: HashMap<ConfigKey, String>,
17+
}
18+
19+
impl Clone for Config {
20+
fn clone(&self) -> Self {
21+
Config {
22+
map: self.map.clone(),
23+
}
24+
}
25+
}
26+
27+
impl Config {
28+
pub fn new() -> Self {
29+
// TODO consider defaults
30+
Config {
31+
map: HashMap::new(),
32+
}
33+
}
34+
35+
pub fn get(&self, k: ConfigKey) -> Option<&String> {
36+
self.map.get(&k)
37+
}
38+
39+
pub fn set(&mut self, k: ConfigKey, v: String) {
40+
self.map.insert(k, v);
41+
}
42+
}
43+
44+
impl Default for Config {
45+
fn default() -> Self {
46+
Self::new()
47+
}
48+
}

crates/processing_render/src/image.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::path::PathBuf;
55

66
use bevy::{
77
asset::{
8-
LoadState, RenderAssetUsages, handle_internal_asset_events, io::embedded::GetAssetServer,
8+
AssetPath, LoadState, RenderAssetUsages, handle_internal_asset_events,
9+
io::{AssetSourceId, embedded::GetAssetServer},
910
},
1011
ecs::{entity::EntityHashMap, system::RunSystemOnce},
1112
prelude::*,
@@ -23,6 +24,7 @@ use bevy::{
2324
};
2425
use half::f16;
2526

27+
use crate::config::{Config, ConfigKey};
2628
use crate::error::{ProcessingError, Result};
2729

2830
pub struct ImagePlugin;
@@ -138,7 +140,16 @@ pub fn from_handle(
138140
}
139141

140142
pub fn load(In(path): In<PathBuf>, world: &mut World) -> Result<Entity> {
143+
let config = world.resource_mut::<Config>();
144+
let path: AssetPath = match config.get(ConfigKey::AssetRootPath) {
145+
Some(_) => {
146+
AssetPath::from_path_buf(path).with_source(AssetSourceId::from("assets_directory"))
147+
}
148+
None => AssetPath::from_path_buf(path),
149+
};
150+
141151
let handle: Handle<bevy::image::Image> = world.get_asset_server().load(path);
152+
142153
while let LoadState::Loading = world.get_asset_server().load_state(&handle) {
143154
world.run_system_once(handle_internal_asset_events).unwrap();
144155
}

crates/processing_render/src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod config;
12
pub mod error;
23
mod graphics;
34
pub mod image;
@@ -6,11 +7,13 @@ mod surface;
67

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

10+
use config::*;
11+
912
#[cfg(not(target_arch = "wasm32"))]
1013
use bevy::log::tracing_subscriber;
1114
use bevy::{
1215
app::{App, AppExit},
13-
asset::AssetEventSystems,
16+
asset::{AssetEventSystems, io::AssetSourceBuilder},
1417
prelude::*,
1518
render::render_resource::{Extent3d, TextureFormat},
1619
};
@@ -202,9 +205,11 @@ pub fn surface_resize(graphics_entity: Entity, width: u32, height: u32) -> error
202205
})
203206
}
204207

205-
fn create_app() -> App {
208+
fn create_app(config: Config) -> App {
206209
let mut app = App::new();
207210

211+
app.insert_resource(config.clone());
212+
208213
#[cfg(not(target_arch = "wasm32"))]
209214
let plugins = DefaultPlugins
210215
.build()
@@ -227,6 +232,13 @@ fn create_app() -> App {
227232
..default()
228233
});
229234

235+
if let Some(asset_path) = config.get(ConfigKey::AssetRootPath) {
236+
app.register_asset_source(
237+
"assets_directory",
238+
AssetSourceBuilder::platform_default(asset_path, None),
239+
);
240+
}
241+
230242
app.add_plugins(plugins);
231243
app.add_plugins((ImagePlugin, GraphicsPlugin, SurfacePlugin));
232244
app.add_systems(First, (clear_transient_meshes, activate_cameras))
@@ -258,13 +270,13 @@ fn set_app(app: App) {
258270
/// Initialize the app, if not already initialized. Must be called from the main thread and cannot
259271
/// be called concurrently from multiple threads.
260272
#[cfg(not(target_arch = "wasm32"))]
261-
pub fn init() -> error::Result<()> {
273+
pub fn init(config: Config) -> error::Result<()> {
262274
setup_tracing()?;
263275
if is_already_init()? {
264276
return Ok(());
265277
}
266278

267-
let mut app = create_app();
279+
let mut app = create_app(config);
268280
app.finish();
269281
app.cleanup();
270282
set_app(app);

examples/background_image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn main() {
1919

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

2424
let width = 400;
2525
let height = 400;

examples/rectangle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn main() {
1919

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

2424
let width = 400;
2525
let height = 400;

0 commit comments

Comments
 (0)