1 // Copyright 2020-2021 Ian Jackson and contributors to Otter
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
7 use std::ops::{Add,Sub,Mul,Neg};
9 use num_traits::NumCast;
11 //---------- common types ----------
15 #[derive(Clone,Copy,Serialize,Deserialize,Hash)]
16 #[derive(Eq,PartialEq,Ord,PartialOrd)]
18 pub struct PosC<T>{ pub coords: [T; 2] }
19 pub type Pos = PosC<Coord>;
21 #[derive(Clone,Copy,Serialize,Deserialize,Hash)]
22 #[derive(Eq,PartialEq,Ord,PartialOrd)]
24 pub struct RectC<T>{ pub corners: [PosC<T>; 2] }
25 pub type Rect = RectC<Coord>;
27 // ---------- CheckedArith ----------
29 #[derive(Error,Clone,Copy,Debug,Serialize,Deserialize)]
30 #[error("error parsing Z coordinate")]
31 pub struct CoordinateOverflow;
33 pub trait CheckedArith: Copy + Clone + Debug + 'static {
34 fn checked_add(self, rhs: Self) -> Result<Self, CoordinateOverflow>;
35 fn checked_sub(self, rhs: Self) -> Result<Self, CoordinateOverflow>;
36 fn checked_neg(self) -> Result<Self, CoordinateOverflow>;
38 pub trait CheckedArithMul<RHS: Copy + Clone + Debug + 'static>:
39 Copy + Clone + Debug + 'static {
40 fn checked_mul(self, rhs: RHS) -> Result<Self, CoordinateOverflow>;
43 macro_rules! checked_inherent { {$n:ident($($formal:tt)*) $($actual:tt)*} => {
44 fn $n(self $($formal)*) -> Result<Self, CoordinateOverflow> {
45 self.$n($($actual)*).ok_or(CoordinateOverflow)
49 #[allow(clippy::only_used_in_recursion)] // FP nightly (1bfe40d11 2022-03-18
50 impl CheckedArith for i32 {
51 checked_inherent!{checked_add(, rhs: Self) rhs}
52 checked_inherent!{checked_sub(, rhs: Self) rhs}
53 checked_inherent!{checked_neg( ) }
55 impl CheckedArithMul<i32> for i32 {
56 checked_inherent!{checked_mul(, rhs: Self) rhs}
58 impl CheckedArithMul<f64> for i32 {
59 fn checked_mul(self, rhs: f64) -> Result<Self, CoordinateOverflow> {
60 let lhs: f64 = self.into();
61 let out: f64 = lhs.checked_mul(rhs)?;
62 let out: Self = NumCast::from(out).ok_or(CoordinateOverflow)?;
67 macro_rules! checked_float { {$n:ident($($formal:tt)*) $($modify:tt)*} => {
68 fn $n(self $($formal)*) -> Result<Self, CoordinateOverflow> {
69 let out = self $($modify)*;
70 if out.is_finite() { Ok(out) } else { Err(CoordinateOverflow) }
74 impl CheckedArith for f64 {
75 checked_float!{checked_add(, rhs: Self) + rhs }
76 checked_float!{checked_sub(, rhs: Self) - rhs }
77 checked_float!{checked_neg() .neg()}
79 impl CheckedArithMul<f64> for f64 {
80 checked_float!{checked_mul(, rhs: Self) * rhs }
83 pub trait Mean { fn mean(&self, other: &Self) -> Self; }
85 impl Mean for i32 { fn mean(&self, other: &Self) -> Self {
86 ((*self as i64 + *other as i64) / 2) as i32
88 impl Mean for f64 { fn mean(&self, other: &Self) -> Self {
89 self * 0.5 + other * 0.5
92 //---------- Pos ----------
94 pub trait PosPromote {
95 fn promote(&self) -> PosC<f64>;
97 impl<T> PosPromote for PosC<T> where T: Into<f64> + Copy + Debug {
98 fn promote(&self) -> PosC<f64> { self.map(|v| v.into()) }
101 #[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)]
102 pub struct PosCFromIteratorError;
103 display_as_debug!{PosCFromIteratorError}
106 macro_rules! pos_zip_try_map { {
107 $( $input:expr ),* => $closure:expr
109 PosC::try_from_iter_2(
110 izip!($( $input .coords(), )*)
115 macro_rules! pos_zip_map { {
116 $( $input:expr ),* => $closure:expr
119 izip!($( $input .coords(), )*)
125 pub const fn new(x: T, y: T) -> Self { PosC{ coords: [x,y] } }
126 pub fn both(v: T) -> Self where T: Copy { PosC::new(v,v) }
127 pub fn zero() -> Self where T: num_traits::Zero + Copy {
128 PosC::both(<T as num_traits::Zero>::zero())
131 pub fn coords(self) -> impl ExactSizeIterator<Item=T> + FusedIterator {
132 self.coords.into_iter()
135 #[throws(CoordinateOverflow)]
136 pub fn len2(self) -> f64 where PosC<T>: PosPromote {
137 self.promote().coords()
138 .try_fold(0., |b, c| {
139 let c2 = c.checked_mul(c)?;
144 #[throws(CoordinateOverflow)]
145 pub fn len(self) -> f64 where PosC<T>: PosPromote {
146 let d2 = self.len2()?;
148 if !d.is_finite() { throw!(CoordinateOverflow) }
152 impl<T> PosC<T> where T: Copy {
153 pub fn x(self) -> T { self.coords[0] }
154 pub fn y(self) -> T { self.coords[1] }
157 #[allow(clippy::should_implement_trait)] // this one is fallible, which is a bit odd
159 #[throws(PosCFromIteratorError)]
160 pub fn from_iter<I: Iterator<Item=T>>(i: I) -> Self { PosC{ coords:
162 .collect::<ArrayVec<_,2>>()
164 .map_err(|_| PosCFromIteratorError)?
168 impl<T> PosC<T> where T: Debug {
169 pub fn from_iter_2<I: Iterator<Item=T>>(i: I) -> Self { PosC{ coords:
171 .collect::<ArrayVec<_,2>>()
177 impl<T> Debug for PosC<T> where T: Debug + Copy {
178 #[throws(fmt::Error)]
179 fn fmt(&self, f: &mut Formatter) {
180 write!(f, "[{:?},{:?}]", self.x(), self.y())?;
184 impl<T:Debug> PosC<T> {
185 /// Panics if the iterator doesn't yield exactly 2 elements
187 pub fn try_from_iter_2<
189 I: Iterator<Item=Result<T,E>>
190 >(i: I) -> Self { PosC{ coords:
192 .collect::<Result<ArrayVec<_,2>,E>>()?
193 .into_inner().unwrap()
197 impl<T:CheckedArith> Add<PosC<T>> for PosC<T> {
198 type Output = Result<Self, CoordinateOverflow>;
199 #[throws(CoordinateOverflow)]
200 fn add(self, rhs: PosC<T>) -> PosC<T> {
201 pos_zip_try_map!( self, rhs => |(a,b)| a.checked_add(b) )?
205 impl<T:CheckedArith> Sub<PosC<T>> for PosC<T> {
206 type Output = Result<Self, CoordinateOverflow>;
207 #[throws(CoordinateOverflow)]
208 fn sub(self, rhs: PosC<T>) -> PosC<T> {
209 pos_zip_try_map!( self, rhs => |(a,b)| a.checked_sub(b) )?
213 impl<S:Copy+Debug+Clone+'static,T:CheckedArithMul<S>> Mul<S> for PosC<T> {
214 type Output = Result<Self, CoordinateOverflow>;
215 #[throws(CoordinateOverflow)]
216 fn mul(self, rhs: S) -> PosC<T> {
217 pos_zip_try_map!( self => |a| a.checked_mul(rhs) )?
221 impl<T:CheckedArith> Neg for PosC<T> {
222 type Output = Result<Self, CoordinateOverflow>;
223 #[throws(CoordinateOverflow)]
224 fn neg(self) -> Self {
225 pos_zip_try_map!( self => |a| a.checked_neg() )?
229 impl<T:Copy+Clone+Debug> PosC<T> {
230 pub fn map<U:Copy+Clone+Debug, F: FnMut(T) -> U>(self, f: F) -> PosC<U> {
231 pos_zip_map!( self => f )
235 impl<T:Copy+Clone+Debug> PosC<T> {
236 pub fn try_map<E:Debug, U:Copy+Clone+Debug, F: FnMut(T) -> Result<U,E>>
237 (self, f: F) -> Result<PosC<U>,E>
239 pos_zip_try_map!( self => f )
243 impl<T> Mean for PosC<T> where T: Mean + Debug + Copy {
244 fn mean(&self, other: &Self) -> Self where T: Mean {
245 pos_zip_map!( self, other => |(a,b)| a.mean(&b) )
249 // ---------- Rect ----------
251 impl<T> RectC<T> where T: Copy {
252 pub fn tl(&self) -> PosC<T> { self.corners[0] }
253 pub fn br(&self) -> PosC<T> { self.corners[1] }
256 impl<T> Debug for RectC<T> where T: Debug + Copy {
257 #[throws(fmt::Error)]
258 fn fmt(&self, f: &mut Formatter) {
259 write!(f, "Rect[{:?},{:?}]", self.tl(), self.br())?;
264 pub fn contains(&self, p: PosC<T>) -> bool where T: PartialOrd + Copy {
266 p.coords[i] >= self.tl().coords[i] &&
267 p.coords[i] <= self.br().coords[i]
271 pub fn overlaps(&self, other: &RectC<T>) -> bool where T: PartialOrd + Copy {
273 other.br().coords[i] < self .tl().coords[i] ||
274 self .br().coords[i] < other.tl().coords[i]
278 pub fn empty() -> Self where T: num_traits::Zero + num_traits::One + Copy {
280 PosC::both( <T as num_traits::One >::one() ),
281 PosC::both( <T as num_traits::Zero>::zero() ),
286 impl<T> RectC<T> where T: Mean + Debug + Copy {
287 pub fn middle(&self) -> PosC<T> {
288 Mean::mean(&self.tl(),
293 impl<T> RectC<T> where T: CheckedArith + Debug + Copy {
294 #[throws(CoordinateOverflow)]
295 pub fn size(&self) -> PosC<T> {
296 (self.br() - self.tl())?
302 let empty = Rect::empty();
303 for x in -3..3 { for y in -3..3 {
305 assert!(! empty.contains(PosC::new(x,y)));
309 // ---------- Region ----------
311 #[derive(Clone,Debug,Serialize,Deserialize)]
312 #[derive(Ord,PartialOrd,Eq,PartialEq)]
313 pub enum RegionC<T:Copy> {
316 pub type Region = RegionC<Coord>;
318 impl<T:Copy> RegionC<T> {
319 pub fn contains(&self, pos: PosC<T>) -> bool where T: PartialOrd {
322 Rect(a) => a.contains(pos),
326 pub fn overlaps(&self, other: &RegionC<T>) -> bool where T: PartialOrd {
328 match (self, other) {
329 (Rect(a), Rect(b)) => a.overlaps(b)
333 pub fn empty() -> Self where T: Copy + num_traits::Zero + num_traits::One {
334 RegionC::Rect(RectC::empty())