From 378d807a3fed4716281e249418c7a87de60eab03 Mon Sep 17 00:00:00 2001 From: Jose Llamas Date: Tue, 9 Jun 2026 14:23:29 -0700 Subject: [PATCH 1/3] Add Processing math functions (closes #135, closes #140) --- crates/processing_pyo3/src/lib.rs | 79 +++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index 59024d4..0c1fab6 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -756,6 +756,85 @@ mod mewnala { PyColor::xyz(x, y, z, a) } + + // ── Processing math functions (issues #135, #140) ──────────────────────── + + // Trig + #[pyfunction] fn sin(x: f32) -> f32 { x.sin() } + #[pyfunction] fn cos(x: f32) -> f32 { x.cos() } + #[pyfunction] fn tan(x: f32) -> f32 { x.tan() } + #[pyfunction] fn asin(x: f32) -> f32 { x.asin() } + #[pyfunction] fn acos(x: f32) -> f32 { x.acos() } + #[pyfunction] fn atan(x: f32) -> f32 { x.atan() } + #[pyfunction] fn atan2(y: f32, x: f32) -> f32 { y.atan2(x) } + + // Math + #[pyfunction] fn sqrt(x: f32) -> f32 { x.sqrt() } + #[pyfunction] fn sq(x: f32) -> f32 { x * x } + #[pyfunction] fn pow(x: f32, e: f32) -> f32 { x.powf(e) } + #[pyfunction] fn exp(x: f32) -> f32 { x.exp() } + #[pyfunction] fn log(x: f32) -> f32 { x.ln() } + + // Rounding + #[pyfunction] fn floor(x: f32) -> f32 { x.floor() } + #[pyfunction] fn ceil(x: f32) -> f32 { x.ceil() } + #[pyfunction] fn round(x: f32) -> f32 { x.round() } + + // Abs / sign + #[pyfunction] + fn abs(x: &Bound<'_, PyAny>) -> PyResult { + if let Ok(v) = x.extract::() { return Ok(v.abs()); } + if let Ok(v) = x.extract::() { return Ok((v.abs()) as f32); } + Err(pyo3::exceptions::PyTypeError::new_err("abs() requires a number")) + } + + // Min / max (handle int and float) + #[pyfunction] + fn min(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>) -> PyResult { + let a: f32 = a.extract::().or_else(|_| a.extract::().map(|v| v as f32))?; + let b: f32 = b.extract::().or_else(|_| b.extract::().map(|v| v as f32))?; + Ok(a.min(b)) + } + #[pyfunction] + fn max(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>) -> PyResult { + let a: f32 = a.extract::().or_else(|_| a.extract::().map(|v| v as f32))?; + let b: f32 = b.extract::().or_else(|_| b.extract::().map(|v| v as f32))?; + Ok(a.max(b)) + } + + // Constrain / clamp + #[pyfunction] fn constrain(x: f32, lo: f32, hi: f32) -> f32 { x.clamp(lo, hi) } + + // Map range + #[pyfunction] + fn map(value: f32, start1: f32, stop1: f32, start2: f32, stop2: f32) -> f32 { + start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)) + } + + // Lerp + #[pyfunction] fn lerp(start: f32, stop: f32, t: f32) -> f32 { start + (stop - start) * t } + + // Norm + #[pyfunction] fn norm(value: f32, start: f32, stop: f32) -> f32 { (value - start) / (stop - start) } + + // Distance + #[pyfunction] + #[pyo3(signature = (x1, y1, x2, y2, z1=0.0, z2=0.0))] + fn dist(x1: f32, y1: f32, x2: f32, y2: f32, z1: f32, z2: f32) -> f32 { + let dx = x2 - x1; let dy = y2 - y1; let dz = z2 - z1; + (dx*dx + dy*dy + dz*dz).sqrt() + } + + // Mag + #[pyfunction] + #[pyo3(signature = (x, y, z=0.0))] + fn mag(x: f32, y: f32, z: f32) -> f32 { (x*x + y*y + z*z).sqrt() } + + // Degrees / radians + #[pyfunction] fn degrees(r: f32) -> f32 { r.to_degrees() } + #[pyfunction] fn radians(d: f32) -> f32 { d.to_radians() } + + // ───────────────────────────────────────────────────────────────────────── #[cfg(feature = "webcam")] #[pymodule_export] use super::webcam::Webcam; From 41af34fbe294666e78cebb278c2d73698d0acf0e Mon Sep 17 00:00:00 2001 From: Jose Llamas Date: Tue, 9 Jun 2026 14:32:42 -0700 Subject: [PATCH 2/3] Revise: remove stdlib duplicates, rename map to remap --- crates/processing_pyo3/src/lib.rs | 66 ++++++------------------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index 0c1fab6..c229714 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -757,67 +757,29 @@ mod mewnala { } - // ── Processing math functions (issues #135, #140) ──────────────────────── - - // Trig - #[pyfunction] fn sin(x: f32) -> f32 { x.sin() } - #[pyfunction] fn cos(x: f32) -> f32 { x.cos() } - #[pyfunction] fn tan(x: f32) -> f32 { x.tan() } - #[pyfunction] fn asin(x: f32) -> f32 { x.asin() } - #[pyfunction] fn acos(x: f32) -> f32 { x.acos() } - #[pyfunction] fn atan(x: f32) -> f32 { x.atan() } - #[pyfunction] fn atan2(y: f32, x: f32) -> f32 { y.atan2(x) } - - // Math - #[pyfunction] fn sqrt(x: f32) -> f32 { x.sqrt() } - #[pyfunction] fn sq(x: f32) -> f32 { x * x } - #[pyfunction] fn pow(x: f32, e: f32) -> f32 { x.powf(e) } - #[pyfunction] fn exp(x: f32) -> f32 { x.exp() } - #[pyfunction] fn log(x: f32) -> f32 { x.ln() } - - // Rounding - #[pyfunction] fn floor(x: f32) -> f32 { x.floor() } - #[pyfunction] fn ceil(x: f32) -> f32 { x.ceil() } - #[pyfunction] fn round(x: f32) -> f32 { x.round() } - - // Abs / sign - #[pyfunction] - fn abs(x: &Bound<'_, PyAny>) -> PyResult { - if let Ok(v) = x.extract::() { return Ok(v.abs()); } - if let Ok(v) = x.extract::() { return Ok((v.abs()) as f32); } - Err(pyo3::exceptions::PyTypeError::new_err("abs() requires a number")) - } + // ── Processing-specific math functions (issues #135, #140) ───────────── + // Note: stdlib duplicates (sin, cos, abs, min, max, floor, etc.) are + // intentionally omitted -- users should use Python's math module instead. - // Min / max (handle int and float) - #[pyfunction] - fn min(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>) -> PyResult { - let a: f32 = a.extract::().or_else(|_| a.extract::().map(|v| v as f32))?; - let b: f32 = b.extract::().or_else(|_| b.extract::().map(|v| v as f32))?; - Ok(a.min(b)) - } - #[pyfunction] - fn max(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>) -> PyResult { - let a: f32 = a.extract::().or_else(|_| a.extract::().map(|v| v as f32))?; - let b: f32 = b.extract::().or_else(|_| b.extract::().map(|v| v as f32))?; - Ok(a.max(b)) - } + // sq(x) = x squared + #[pyfunction] fn sq(x: f32) -> f32 { x * x } - // Constrain / clamp + // constrain: clamp a value to [lo, hi] #[pyfunction] fn constrain(x: f32, lo: f32, hi: f32) -> f32 { x.clamp(lo, hi) } - // Map range + // remap: map a value from one range to another (renamed from map() to avoid collision with Python builtin) #[pyfunction] - fn map(value: f32, start1: f32, stop1: f32, start2: f32, stop2: f32) -> f32 { + fn remap(value: f32, start1: f32, stop1: f32, start2: f32, stop2: f32) -> f32 { start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)) } - // Lerp + // lerp: linear interpolation #[pyfunction] fn lerp(start: f32, stop: f32, t: f32) -> f32 { start + (stop - start) * t } - // Norm + // norm: normalize a value to [0, 1] within a range #[pyfunction] fn norm(value: f32, start: f32, stop: f32) -> f32 { (value - start) / (stop - start) } - // Distance + // dist: Euclidean distance between two points (2D or 3D) #[pyfunction] #[pyo3(signature = (x1, y1, x2, y2, z1=0.0, z2=0.0))] fn dist(x1: f32, y1: f32, x2: f32, y2: f32, z1: f32, z2: f32) -> f32 { @@ -825,15 +787,11 @@ mod mewnala { (dx*dx + dy*dy + dz*dz).sqrt() } - // Mag + // mag: magnitude of a vector #[pyfunction] #[pyo3(signature = (x, y, z=0.0))] fn mag(x: f32, y: f32, z: f32) -> f32 { (x*x + y*y + z*z).sqrt() } - // Degrees / radians - #[pyfunction] fn degrees(r: f32) -> f32 { r.to_degrees() } - #[pyfunction] fn radians(d: f32) -> f32 { d.to_radians() } - // ───────────────────────────────────────────────────────────────────────── #[cfg(feature = "webcam")] #[pymodule_export] From 8ff5e92f4e60152be58dd840d103dcd6f82b21d0 Mon Sep 17 00:00:00 2001 From: Jose Llamas Date: Tue, 9 Jun 2026 14:33:24 -0700 Subject: [PATCH 3/3] Add math constants (closes #128) --- crates/processing_pyo3/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index c229714..76ac60b 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -757,6 +757,15 @@ mod mewnala { } + // ── Processing math constants (issue #128) ────────────────────────────── + #[pyfunction] fn PI() -> f32 { std::f32::consts::PI } + #[pyfunction] fn TWO_PI() -> f32 { std::f32::consts::TAU } + #[pyfunction] fn TAU() -> f32 { std::f32::consts::TAU } + #[pyfunction] fn HALF_PI() -> f32 { std::f32::consts::FRAC_PI_2 } + #[pyfunction] fn QUARTER_PI() -> f32 { std::f32::consts::FRAC_PI_4 } + #[pyfunction] fn DEG_TO_RAD() -> f32 { std::f32::consts::PI / 180.0 } + #[pyfunction] fn RAD_TO_DEG() -> f32 { 180.0 / std::f32::consts::PI } + // ── Processing-specific math functions (issues #135, #140) ───────────── // Note: stdlib duplicates (sin, cos, abs, min, max, floor, etc.) are // intentionally omitted -- users should use Python's math module instead.