use std::ops::Range;
use pybevy_core::{FieldStorage, FromBorrowedStorage};
use pyo3::prelude::*;
#[pyclass(name = "Range")]
#[derive(Debug, Clone)]
pub struct PyRange {
storage: FieldStorage<Range<f32>>,
}
impl Default for PyRange {
fn default() -> Self {
Self {
storage: FieldStorage::owned(0.0..1.0),
}
}
}
impl FromBorrowedStorage<FieldStorage<Range<f32>>> for PyRange {
fn from_borrowed(storage: FieldStorage<Range<f32>>) -> Self {
PyRange { storage }
}
}
impl PyRange {
#[inline(always)]
pub fn from_range(start: f32, end: f32) -> Self {
PyRange {
storage: FieldStorage::owned(start..end),
}
}
#[inline(always)]
fn as_ref(&self) -> PyResult<&Range<f32>> {
Ok(self.storage.as_ref()?)
}
#[inline(always)]
fn as_mut(&mut self) -> PyResult<&mut Range<f32>> {
Ok(self.storage.as_mut()?)
}
}
#[pymethods]
impl PyRange {
#[new]
pub fn new(start: f32, end: f32) -> Self {
PyRange::from_range(start, end)
}
#[getter]
pub fn start(&self) -> PyResult<f32> {
Ok(self.as_ref()?.start)
}
#[setter]
pub fn set_start(&mut self, value: f32) -> PyResult<()> {
self.as_mut()?.start = value;
Ok(())
}
#[getter]
pub fn end(&self) -> PyResult<f32> {
Ok(self.as_ref()?.end)
}
#[setter]
pub fn set_end(&mut self, value: f32) -> PyResult<()> {
self.as_mut()?.end = value;
Ok(())
}
pub fn is_empty(&self) -> PyResult<bool> {
let r = self.as_ref()?;
Ok(r.start >= r.end)
}
pub fn contains(&self, value: f32) -> PyResult<bool> {
let r = self.as_ref()?;
Ok(value >= r.start && value < r.end)
}
pub fn __repr__(&self) -> PyResult<String> {
let r = self.as_ref()?;
Ok(format!("Range({}, {})", r.start, r.end))
}
pub fn __str__(&self) -> PyResult<String> {
let r = self.as_ref()?;
Ok(format!("{}..{}", r.start, r.end))
}
}
impl From<Range<f32>> for PyRange {
fn from(range: Range<f32>) -> Self {
PyRange::from_range(range.start, range.end)
}
}
impl From<PyRange> for Range<f32> {
fn from(range: PyRange) -> Self {
let r = range.storage.get().unwrap();
r.start..r.end
}
}
impl From<&PyRange> for Range<f32> {
fn from(range: &PyRange) -> Self {
let r = range.storage.get().unwrap();
r.start..r.end
}
}
impl From<(f32, f32)> for PyRange {
fn from(tuple: (f32, f32)) -> Self {
PyRange::from_range(tuple.0, tuple.1)
}
}
impl From<PyRange> for (f32, f32) {
fn from(range: PyRange) -> Self {
let r = range.storage.get().unwrap();
(r.start, r.end)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_range_creation() {
let range = PyRange::new(0.2, 0.7);
assert_eq!(range.start().unwrap(), 0.2);
assert_eq!(range.end().unwrap(), 0.7);
}
#[test]
fn test_range_contains() {
let range = PyRange::new(0.0, 1.0);
assert!(range.contains(0.5).unwrap());
assert!(range.contains(0.0).unwrap());
assert!(!range.contains(1.0).unwrap()); assert!(!range.contains(-0.1).unwrap());
}
#[test]
fn test_range_is_empty() {
assert!(!PyRange::new(0.0, 1.0).is_empty().unwrap());
assert!(PyRange::new(1.0, 1.0).is_empty().unwrap());
assert!(PyRange::new(1.0, 0.0).is_empty().unwrap());
}
#[test]
fn test_range_from_bevy() {
let bevy_range: Range<f32> = 0.2..0.7;
let py_range: PyRange = bevy_range.into();
assert_eq!(py_range.start().unwrap(), 0.2);
assert_eq!(py_range.end().unwrap(), 0.7);
}
#[test]
fn test_range_to_bevy() {
let py_range = PyRange::new(0.2, 0.7);
let bevy_range: Range<f32> = py_range.into();
assert_eq!(bevy_range.start, 0.2);
assert_eq!(bevy_range.end, 0.7);
}
#[test]
fn test_range_mutation() {
let mut range = PyRange::new(0.0, 1.0);
range.set_start(0.5).unwrap();
range.set_end(2.0).unwrap();
assert_eq!(range.start().unwrap(), 0.5);
assert_eq!(range.end().unwrap(), 2.0);
}
}