@@ -756,6 +756,85 @@ mod mewnala {
756756 PyColor :: xyz ( x, y, z, a)
757757 }
758758
759+
760+ // ── Processing math functions (issues #135, #140) ────────────────────────
761+
762+ // Trig
763+ #[ pyfunction] fn sin ( x : f32 ) -> f32 { x. sin ( ) }
764+ #[ pyfunction] fn cos ( x : f32 ) -> f32 { x. cos ( ) }
765+ #[ pyfunction] fn tan ( x : f32 ) -> f32 { x. tan ( ) }
766+ #[ pyfunction] fn asin ( x : f32 ) -> f32 { x. asin ( ) }
767+ #[ pyfunction] fn acos ( x : f32 ) -> f32 { x. acos ( ) }
768+ #[ pyfunction] fn atan ( x : f32 ) -> f32 { x. atan ( ) }
769+ #[ pyfunction] fn atan2 ( y : f32 , x : f32 ) -> f32 { y. atan2 ( x) }
770+
771+ // Math
772+ #[ pyfunction] fn sqrt ( x : f32 ) -> f32 { x. sqrt ( ) }
773+ #[ pyfunction] fn sq ( x : f32 ) -> f32 { x * x }
774+ #[ pyfunction] fn pow ( x : f32 , e : f32 ) -> f32 { x. powf ( e) }
775+ #[ pyfunction] fn exp ( x : f32 ) -> f32 { x. exp ( ) }
776+ #[ pyfunction] fn log ( x : f32 ) -> f32 { x. ln ( ) }
777+
778+ // Rounding
779+ #[ pyfunction] fn floor ( x : f32 ) -> f32 { x. floor ( ) }
780+ #[ pyfunction] fn ceil ( x : f32 ) -> f32 { x. ceil ( ) }
781+ #[ pyfunction] fn round ( x : f32 ) -> f32 { x. round ( ) }
782+
783+ // Abs / sign
784+ #[ pyfunction]
785+ fn abs ( x : & Bound < ' _ , PyAny > ) -> PyResult < f32 > {
786+ if let Ok ( v) = x. extract :: < f32 > ( ) { return Ok ( v. abs ( ) ) ; }
787+ if let Ok ( v) = x. extract :: < i64 > ( ) { return Ok ( ( v. abs ( ) ) as f32 ) ; }
788+ Err ( pyo3:: exceptions:: PyTypeError :: new_err ( "abs() requires a number" ) )
789+ }
790+
791+ // Min / max (handle int and float)
792+ #[ pyfunction]
793+ fn min ( a : & Bound < ' _ , PyAny > , b : & Bound < ' _ , PyAny > ) -> PyResult < f32 > {
794+ let a: f32 = a. extract :: < f32 > ( ) . or_else ( |_| a. extract :: < i64 > ( ) . map ( |v| v as f32 ) ) ?;
795+ let b: f32 = b. extract :: < f32 > ( ) . or_else ( |_| b. extract :: < i64 > ( ) . map ( |v| v as f32 ) ) ?;
796+ Ok ( a. min ( b) )
797+ }
798+ #[ pyfunction]
799+ fn max ( a : & Bound < ' _ , PyAny > , b : & Bound < ' _ , PyAny > ) -> PyResult < f32 > {
800+ let a: f32 = a. extract :: < f32 > ( ) . or_else ( |_| a. extract :: < i64 > ( ) . map ( |v| v as f32 ) ) ?;
801+ let b: f32 = b. extract :: < f32 > ( ) . or_else ( |_| b. extract :: < i64 > ( ) . map ( |v| v as f32 ) ) ?;
802+ Ok ( a. max ( b) )
803+ }
804+
805+ // Constrain / clamp
806+ #[ pyfunction] fn constrain ( x : f32 , lo : f32 , hi : f32 ) -> f32 { x. clamp ( lo, hi) }
807+
808+ // Map range
809+ #[ pyfunction]
810+ fn map ( value : f32 , start1 : f32 , stop1 : f32 , start2 : f32 , stop2 : f32 ) -> f32 {
811+ start2 + ( stop2 - start2) * ( ( value - start1) / ( stop1 - start1) )
812+ }
813+
814+ // Lerp
815+ #[ pyfunction] fn lerp ( start : f32 , stop : f32 , t : f32 ) -> f32 { start + ( stop - start) * t }
816+
817+ // Norm
818+ #[ pyfunction] fn norm ( value : f32 , start : f32 , stop : f32 ) -> f32 { ( value - start) / ( stop - start) }
819+
820+ // Distance
821+ #[ pyfunction]
822+ #[ pyo3( signature = ( x1, y1, x2, y2, z1=0.0 , z2=0.0 ) ) ]
823+ fn dist ( x1 : f32 , y1 : f32 , x2 : f32 , y2 : f32 , z1 : f32 , z2 : f32 ) -> f32 {
824+ let dx = x2 - x1; let dy = y2 - y1; let dz = z2 - z1;
825+ ( dx* dx + dy* dy + dz* dz) . sqrt ( )
826+ }
827+
828+ // Mag
829+ #[ pyfunction]
830+ #[ pyo3( signature = ( x, y, z=0.0 ) ) ]
831+ fn mag ( x : f32 , y : f32 , z : f32 ) -> f32 { ( x* x + y* y + z* z) . sqrt ( ) }
832+
833+ // Degrees / radians
834+ #[ pyfunction] fn degrees ( r : f32 ) -> f32 { r. to_degrees ( ) }
835+ #[ pyfunction] fn radians ( d : f32 ) -> f32 { d. to_radians ( ) }
836+
837+ // ─────────────────────────────────────────────────────────────────────────
759838 #[ cfg( feature = "webcam" ) ]
760839 #[ pymodule_export]
761840 use super :: webcam:: Webcam ;
0 commit comments