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
24 changes: 17 additions & 7 deletions crates/processing_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,24 +466,32 @@ pub extern "C" fn processing_reset_matrix(graphics_id: u64) {
/// - graphics_id is a valid ID returned from graphics_create.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_translate(graphics_id: u64, x: f32, y: f32) {
pub extern "C" fn processing_translate(graphics_id: u64, x: f32, y: f32, z: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| {
graphics_record_command(graphics_entity, DrawCommand::Translate(Vec2::new(x, y)))
graphics_record_command(graphics_entity, DrawCommand::Translate(Vec3::new(x, y, z)))
});
}

/// Rotate the coordinate system.
/// Rotate the coordinate system by `angle` about the axis (x, y, z).
///
/// SAFETY:
/// - graphics_id is a valid ID returned from graphics_create.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_rotate(graphics_id: u64, angle: f32) {
pub extern "C" fn processing_rotate(graphics_id: u64, angle: f32, x: f32, y: f32, z: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_record_command(graphics_entity, DrawCommand::Rotate { angle }));
error::check(|| {
graphics_record_command(
graphics_entity,
DrawCommand::Rotate {
angle,
axis: Vec3::new(x, y, z),
},
)
});
}

/// Scale the coordinate system.
Expand All @@ -492,10 +500,12 @@ pub extern "C" fn processing_rotate(graphics_id: u64, angle: f32) {
/// - graphics_id is a valid ID returned from graphics_create.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_scale(graphics_id: u64, x: f32, y: f32) {
pub extern "C" fn processing_scale(graphics_id: u64, x: f32, y: f32, z: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_record_command(graphics_entity, DrawCommand::Scale(Vec2::new(x, y))));
error::check(|| {
graphics_record_command(graphics_entity, DrawCommand::Scale(Vec3::new(x, y, z)))
});
}

/// Shear along the X axis.
Expand Down
64 changes: 54 additions & 10 deletions crates/processing_pyo3/src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::input;
use crate::math::{extract_vec2, extract_vec3, extract_vec4};
use bevy::{
color::{ColorToPacked, Srgba},
math::Vec4,
math::{Vec3, Vec4},
prelude::Entity,
render::render_resource::{Extent3d, TextureFormat},
};
Expand Down Expand Up @@ -1368,28 +1368,63 @@ impl Graphics {

#[pyo3(signature = (*args))]
pub fn translate(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> {
let v = extract_vec2(args)?;
let v = if args.len() == 3 {
extract_vec3(args)?
} else {
extract_vec2(args)?.extend(0.0)
};
graphics_record_command(self.entity, DrawCommand::Translate(v))
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

pub fn rotate(&self, angle: f32) -> PyResult<()> {
graphics_record_command(self.entity, DrawCommand::Rotate { angle })
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
graphics_record_command(
self.entity,
DrawCommand::Rotate {
angle,
axis: Vec3::Z,
},
)
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

pub fn rotate_x(&self, angle: f32) -> PyResult<()> {
graphics_record_command(self.entity, DrawCommand::RotateX { angle })
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
graphics_record_command(
self.entity,
DrawCommand::Rotate {
angle,
axis: Vec3::X,
},
)
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

pub fn rotate_y(&self, angle: f32) -> PyResult<()> {
graphics_record_command(self.entity, DrawCommand::RotateY { angle })
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
graphics_record_command(
self.entity,
DrawCommand::Rotate {
angle,
axis: Vec3::Y,
},
)
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

pub fn rotate_z(&self, angle: f32) -> PyResult<()> {
graphics_record_command(self.entity, DrawCommand::RotateZ { angle })
graphics_record_command(
self.entity,
DrawCommand::Rotate {
angle,
axis: Vec3::Z,
},
)
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

#[pyo3(signature = (angle, *args))]
pub fn rotate_axis(&self, angle: f32, args: &Bound<'_, PyTuple>) -> PyResult<()> {
let axis = extract_vec3(args)?;
graphics_record_command(self.entity, DrawCommand::Rotate { angle, axis })
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}

Expand Down Expand Up @@ -1554,7 +1589,16 @@ impl Graphics {

#[pyo3(signature = (*args))]
pub fn scale(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> {
let v = extract_vec2(args)?;
let v = if args.len() == 3 {
extract_vec3(args)?
} else if args.len() == 1 {
match args.get_item(0)?.extract::<f32>() {
Ok(s) => Vec3::splat(s),
Err(_) => extract_vec2(args)?.extend(1.0),
}
} else {
extract_vec2(args)?.extend(1.0)
};
graphics_record_command(self.entity, DrawCommand::Scale(v))
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
}
Expand Down
10 changes: 10 additions & 0 deletions crates/processing_pyo3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,16 @@ mod mewnala {
graphics!(module).rotate_z(angle)
}

#[pyfunction]
#[pyo3(pass_module, signature = (angle, *args))]
fn rotate_axis(
module: &Bound<'_, PyModule>,
angle: f32,
args: &Bound<'_, PyTuple>,
) -> PyResult<()> {
graphics!(module).rotate_axis(angle, args)
}

#[pyfunction(name = "box")]
#[pyo3(pass_module, signature = (*args))]
fn draw_box(module: &Bound<'_, PyModule>, args: &Bound<'_, PyTuple>) -> PyResult<()> {
Expand Down
14 changes: 3 additions & 11 deletions crates/processing_render/src/render/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,20 +511,12 @@ pub enum DrawCommand {
PushMatrix,
PopMatrix,
ResetMatrix,
Translate(Vec2),
Translate(Vec3),
Rotate {
angle: f32,
axis: Vec3,
},
RotateX {
angle: f32,
},
RotateY {
angle: f32,
},
RotateZ {
angle: f32,
},
Scale(Vec2),
Scale(Vec3),
ShearX {
angle: f32,
},
Expand Down
23 changes: 13 additions & 10 deletions crates/processing_render/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,14 +929,13 @@ pub fn flush_draw_commands(
DrawCommand::PushMatrix => state.transform.push(),
DrawCommand::PopMatrix => state.transform.pop(),
DrawCommand::ResetMatrix => state.transform.reset(),
DrawCommand::Translate(v) => state.transform.translate(v.x, v.y),
DrawCommand::Rotate { angle } => state.transform.rotate(angle),
DrawCommand::RotateX { angle } => state.transform.rotate_x(angle),
DrawCommand::RotateY { angle } => state.transform.rotate_y(angle),
DrawCommand::RotateZ { angle } => state.transform.rotate_z(angle),
DrawCommand::Scale(v) => state.transform.scale(v.x, v.y),
DrawCommand::ShearX { angle } => state.transform.shear_x(angle),
DrawCommand::ShearY { angle } => state.transform.shear_y(angle),
DrawCommand::Translate(v) => state.transform.apply(Affine3A::from_translation(v)),
DrawCommand::Rotate { angle, axis } => state
.transform
.apply(Affine3A::from_axis_angle(axis.normalize(), angle)),
DrawCommand::Scale(v) => state.transform.apply(Affine3A::from_scale(v)),
DrawCommand::ShearX { angle } => state.transform.apply(transform::shear_x(angle)),
DrawCommand::ShearY { angle } => state.transform.apply(transform::shear_y(angle)),
DrawCommand::Geometry(entity) => {
let Some((geometry, node_transform)) = p_geometries.get(entity).ok() else {
warn!("Could not find Geometry for entity {:?}", entity);
Expand Down Expand Up @@ -1268,7 +1267,9 @@ pub fn flush_draw_commands(
let text_cx = text_cx.clone();

if z != 0.0 {
state.transform.translate_3d(0.0, 0.0, z);
state
.transform
.apply(Affine3A::from_translation(Vec3::new(0.0, 0.0, z)));
}

add_fill(
Expand Down Expand Up @@ -1305,7 +1306,9 @@ pub fn flush_draw_commands(
);

if z != 0.0 {
state.transform.translate_3d(0.0, 0.0, -z);
state
.transform
.apply(Affine3A::from_translation(Vec3::new(0.0, 0.0, -z)));
}
}
}
Expand Down
94 changes: 23 additions & 71 deletions crates/processing_render/src/render/transform.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bevy::math::{Affine3A, Mat3, Quat, Vec3};
use bevy::math::{Affine3A, Mat3, Vec3};

#[derive(Debug, Clone, Default)]
pub struct TransformStack {
Expand Down Expand Up @@ -34,70 +34,6 @@ impl TransformStack {
self.stack.clear();
}

pub fn translate(&mut self, x: f32, y: f32) {
self.translate_3d(x, y, 0.0);
}

pub fn rotate(&mut self, angle: f32) {
self.rotate_z(angle);
}

pub fn scale_uniform(&mut self, s: f32) {
self.scale(s, s);
}

pub fn scale(&mut self, sx: f32, sy: f32) {
self.scale_3d(sx, sy, 1.0);
}

pub fn shear_x(&mut self, angle: f32) {
let shear = Affine3A::from_mat3(Mat3::from_cols(
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(angle.tan(), 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
));
self.current *= shear;
}

pub fn shear_y(&mut self, angle: f32) {
let shear = Affine3A::from_mat3(Mat3::from_cols(
Vec3::new(1.0, angle.tan(), 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
));
self.current *= shear;
}

pub fn translate_3d(&mut self, x: f32, y: f32, z: f32) {
let t = Affine3A::from_translation(Vec3::new(x, y, z));
self.current *= t;
}

pub fn rotate_x(&mut self, angle: f32) {
let r = Affine3A::from_quat(Quat::from_rotation_x(angle));
self.current *= r;
}

pub fn rotate_y(&mut self, angle: f32) {
let r = Affine3A::from_quat(Quat::from_rotation_y(angle));
self.current *= r;
}

pub fn rotate_z(&mut self, angle: f32) {
let r = Affine3A::from_quat(Quat::from_rotation_z(angle));
self.current *= r;
}

pub fn rotate_axis(&mut self, angle: f32, axis: Vec3) {
let r = Affine3A::from_quat(Quat::from_axis_angle(axis.normalize(), angle));
self.current *= r;
}

pub fn scale_3d(&mut self, sx: f32, sy: f32, sz: f32) {
let s = Affine3A::from_scale(Vec3::new(sx, sy, sz));
self.current *= s;
}

pub fn apply(&mut self, transform: Affine3A) {
self.current *= transform;
}
Expand All @@ -121,6 +57,22 @@ impl TransformStack {
}
}

pub fn shear_x(angle: f32) -> Affine3A {
Affine3A::from_mat3(Mat3::from_cols(
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(angle.tan(), 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
))
}

pub fn shear_y(angle: f32) -> Affine3A {
Affine3A::from_mat3(Mat3::from_cols(
Vec3::new(1.0, angle.tan(), 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
))
}

#[cfg(test)]
mod tests {
use std::f32::consts::PI;
Expand All @@ -144,7 +96,7 @@ mod tests {
#[test]
fn test_translate() {
let mut stack = TransformStack::new();
stack.translate(100.0, 50.0);
stack.apply(Affine3A::from_translation(Vec3::new(100.0, 50.0, 0.0)));
let (x, y) = stack.transform_point_2d(10.0, 20.0);
assert!(approx_eq(x, 110.0));
assert!(approx_eq(y, 70.0));
Expand All @@ -153,7 +105,7 @@ mod tests {
#[test]
fn test_scale() {
let mut stack = TransformStack::new();
stack.scale(2.0, 3.0);
stack.apply(Affine3A::from_scale(Vec3::new(2.0, 3.0, 1.0)));
let (x, y) = stack.transform_point_2d(10.0, 10.0);
assert!(approx_eq(x, 20.0));
assert!(approx_eq(y, 30.0));
Expand All @@ -162,7 +114,7 @@ mod tests {
#[test]
fn test_rotate_90() {
let mut stack = TransformStack::new();
stack.rotate(PI / 2.0);
stack.apply(Affine3A::from_rotation_z(PI / 2.0));
let (x, y) = stack.transform_point_2d(10.0, 0.0);
assert!(approx_eq(x, 0.0));
assert!(approx_eq(y, 10.0));
Expand All @@ -171,9 +123,9 @@ mod tests {
#[test]
fn test_push_pop() {
let mut stack = TransformStack::new();
stack.translate(100.0, 100.0);
stack.apply(Affine3A::from_translation(Vec3::new(100.0, 100.0, 0.0)));
stack.push();
stack.translate(50.0, 50.0);
stack.apply(Affine3A::from_translation(Vec3::new(50.0, 50.0, 0.0)));

let (x, y) = stack.transform_point_2d(0.0, 0.0);
assert!(approx_eq(x, 150.0));
Expand All @@ -189,7 +141,7 @@ mod tests {
#[test]
fn test_pop_empty_is_noop() {
let mut stack = TransformStack::new();
stack.translate(50.0, 50.0);
stack.apply(Affine3A::from_translation(Vec3::new(50.0, 50.0, 0.0)));
stack.pop();
let (x, y) = stack.transform_point_2d(0.0, 0.0);
assert!(approx_eq(x, 50.0));
Expand Down
Loading
Loading