From: Ian Jackson Date: Wed, 24 Mar 2021 23:57:08 +0000 (+0000) Subject: geometry: Move to new module X-Git-Tag: otter-0.5.0~437 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=e630bc8718f051e7728ac641334630cde9d05ec6;p=otter.git geometry: Move to new module Signed-off-by: Ian Jackson --- diff --git a/src/geometry.rs b/src/geometry.rs new file mode 100644 index 00000000..4b58c7ce --- /dev/null +++ b/src/geometry.rs @@ -0,0 +1,182 @@ +// Copyright 2020-2021 Ian Jackson and contributors to Otter +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use crate::imports::*; +use crate::prelude::*; + +use std::ops::{Add,Sub,Mul,Neg}; + +//---------- common types ---------- + +pub type Coord = i32; + +#[derive(Clone,Copy,Debug,Serialize,Deserialize,Hash)] +#[derive(Eq,PartialEq,Ord,PartialOrd)] +#[serde(transparent)] +pub struct PosC(pub [T; 2]); +pub type Pos = PosC; + +#[derive(Clone,Copy,Debug,Serialize,Deserialize,Hash)] +#[derive(Eq,PartialEq)] +#[serde(transparent)] +pub struct AreaC(pub [PosC; 2]); +pub type Area = AreaC; + +//---------- Pos ---------- + +pub trait Mean { fn mean(&self, other: &Self) -> Self; } + +impl Mean for i32 { fn mean(&self, other: &Self) -> Self { + ((*self as i64 + *other as i64) / 2) as i32 +} } + +impl Add> for PosC { + type Output = Result; + #[throws(CoordinateOverflow)] + fn add(self, rhs: PosC) -> PosC { + PosC::try_from_iter_2( + itertools::zip_eq( + self.0.iter().cloned(), + rhs .0.iter().cloned(), + ).map( + |(a,b)| a.checked_add(b) + ) + )? + } +} + +impl Sub> for PosC { + type Output = Result; + #[throws(CoordinateOverflow)] + fn sub(self, rhs: PosC) -> PosC { + PosC::try_from_iter_2( + itertools::zip_eq( + self.0.iter().cloned(), + rhs .0.iter().cloned(), + ).map(|(a,b)| a.checked_sub(b)) + )? + } +} + +impl> Mul for PosC { + type Output = Result; + #[throws(CoordinateOverflow)] + fn mul(self, rhs: S) -> PosC { + PosC::try_from_iter_2( + self.0.iter().cloned().map( + |a| a.checked_mul(rhs) + ) + )? + } +} + +impl Neg for PosC { + type Output = Result; + #[throws(CoordinateOverflow)] + fn neg(self) -> Self { + PosC::try_from_iter_2( + self.0.iter().cloned().map(|a| a.checked_neg()) + )? + } +} + +impl PosC { + pub fn map U>(self, f: F) -> PosC { + PosC::from_iter( + self.0.iter().cloned().map(f) + ).unwrap() + } +} + +impl PosC { + pub fn try_map Result> + (self, f: F) -> Result,E> + { + PosC::try_from_iter_2( + self.0.iter().cloned().map(f) + ) + } +} + +impl Mean for PosC where T: Mean + Debug { + fn mean(&self, other: &Self) -> Self where T: Mean { + PosC::try_from_iter_2( + izip!(&self.0, &other.0) + .map(|(a,b)| Ok::<_,Void>(a.mean(b))) + ).unwrap_or_else(|v| match v { }) + } +} + +impl PosC { + pub fn promote(&self) -> PosC { self.map(|v| v as f64) } +} + +#[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)] +pub struct PosCFromIteratorError; +display_as_debug!{PosCFromIteratorError} + +impl PosC { + #[throws(PosCFromIteratorError)] + pub fn from_iter>(i: I) -> Self { PosC( + i + .collect::>() + .into_inner() + .map_err(|_| PosCFromIteratorError)? + )} +} + +impl PosC { + /// Panics if the iterator doesn't yield exactly 2 elements + #[throws(E)] + pub fn try_from_iter_2< + E: Debug, + I: Iterator> + >(i: I) -> Self { PosC( + i + .collect::,E>>()? + .into_inner().unwrap() + )} +} + +// ---------- Area ---------- + +impl AreaC { + pub fn contains(&self, p: PosC) -> bool where T: Ord { + (0..2).all(|i| { + p.0[i] >= self.0[0].0[i] && + p.0[i] <= self.0[1].0[i] + }) + } + + pub fn overlaps(&self, other: &AreaC) -> bool where T: Ord { + ! (0..2).any(|i| ( + other.0[1].0[i] < self .0[0].0[i] || + self .0[1].0[i] < other.0[0].0[i] + )) + } + + pub fn empty() -> Self where T: Copy + num_traits::Zero + num_traits::One { + let zero = ::zero(); + let one = ::one(); + AreaC([ + PosC([ one, one ]), + PosC([ zero, zero ]), + ]) + } +} + +impl AreaC where T: Mean + Debug { + pub fn middle(&self) -> PosC { + Mean::mean(&self.0[0], + &self.0[1]) + } +} + +#[test] +fn empty_area() { + let empty = Area::empty(); + for x in -3..3 { for y in -3..3 { + assert!(! empty.contains(PosC([x,y]))); + } } +} diff --git a/src/lib.rs b/src/lib.rs index 5ddbdc10..66ad53da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,7 @@ pub mod config; pub mod deck; pub mod debugreader; pub mod error; +pub mod geometry; pub mod gamestate; pub mod global; pub mod hand; diff --git a/src/prelude.rs b/src/prelude.rs index 975f7019..657ad455 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -125,6 +125,7 @@ pub use crate::debugreader::DebugReader; pub use crate::error::*; pub use crate::fake_rng::*; pub use crate::gamestate::*; +pub use crate::geometry::{Coord,Pos,PosC,Area,AreaC}; pub use crate::global::*; pub use crate::hidden::*; pub use crate::keydata::*; @@ -136,7 +137,6 @@ pub use crate::pieces::*; pub use crate::shapelib; pub use crate::slotmap_slot_idx::*; pub use crate::spec::*; -pub use crate::spec::pos_traits::*; pub use crate::spec::piece_specs::{FaceColourSpecs, SimpleCommon}; pub use crate::sse; pub use crate::toml_de; diff --git a/src/spec.rs b/src/spec.rs index d5735804..10c819d0 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -10,9 +10,10 @@ use std::collections::hash_map::HashMap; use std::collections::hash_set::HashSet; use std::fmt::Debug; use std::hash::Hash; +use std::convert::TryFrom; use enum_map::Enum; -use fehler::throws; +use fehler::{throw,throws}; use index_vec::{define_index_type, IndexVec}; use num_derive::{FromPrimitive, ToPrimitive}; use serde::{Deserialize, Serialize}; @@ -22,24 +23,14 @@ use thiserror::Error; use crate::accounts::AccountName; use crate::error::display_as_debug; use crate::gamestate::PieceSpec; +use crate::geometry::{Coord,Pos}; +use crate::prelude::default; pub use implementation::PlayerAccessSpec; -//---------- common types ---------- - -pub type Coord = i32; - -#[derive(Clone,Copy,Debug,Serialize,Deserialize,Hash)] -#[derive(Eq,PartialEq,Ord,PartialOrd)] -#[serde(transparent)] -pub struct PosC(pub [T; 2]); -pub type Pos = PosC; +type SpE = SpecError; -#[derive(Clone,Copy,Debug,Serialize,Deserialize,Hash)] -#[derive(Eq,PartialEq)] -#[serde(transparent)] -pub struct AreaC(pub [PosC; 2]); -pub type Area = AreaC; +//---------- common types ---------- #[derive(Clone,Eq,PartialEq,Ord,PartialOrd,Hash,Serialize,Deserialize)] #[serde(transparent)] @@ -271,195 +262,36 @@ pub mod piece_specs { } } -//---------- Pos ---------- - -pub mod pos_traits { - use std::ops::{Add,Sub,Mul,Neg}; - use crate::prelude::*; - - pub trait Mean { fn mean(&self, other: &Self) -> Self; } - - impl Mean for i32 { fn mean(&self, other: &Self) -> Self { - ((*self as i64 + *other as i64) / 2) as i32 - } } - - impl Add> for PosC { - type Output = Result; - #[throws(CoordinateOverflow)] - fn add(self, rhs: PosC) -> PosC { - PosC::try_from_iter_2( - itertools::zip_eq( - self.0.iter().cloned(), - rhs .0.iter().cloned(), - ).map( - |(a,b)| a.checked_add(b) - ) - )? - } - } - - impl Sub> for PosC { - type Output = Result; - #[throws(CoordinateOverflow)] - fn sub(self, rhs: PosC) -> PosC { - PosC::try_from_iter_2( - itertools::zip_eq( - self.0.iter().cloned(), - rhs .0.iter().cloned(), - ).map(|(a,b)| a.checked_sub(b)) - )? - } - } - - impl> Mul for PosC { - type Output = Result; - #[throws(CoordinateOverflow)] - fn mul(self, rhs: S) -> PosC { - PosC::try_from_iter_2( - self.0.iter().cloned().map( - |a| a.checked_mul(rhs) - ) - )? - } - } - - impl Neg for PosC { - type Output = Result; - #[throws(CoordinateOverflow)] - fn neg(self) -> Self { - PosC::try_from_iter_2( - self.0.iter().cloned().map(|a| a.checked_neg()) - )? - } - } - - impl PosC { - pub fn map U>(self, f: F) -> PosC { - PosC::from_iter( - self.0.iter().cloned().map(f) - ).unwrap() - } - } +// ---------- Implementation - angles ---------- - impl PosC { - pub fn try_map Result> - (self, f: F) -> Result,E> - { - PosC::try_from_iter_2( - self.0.iter().cloned().map(f) - ) - } - } - - impl Mean for PosC where T: Mean + Debug { - fn mean(&self, other: &Self) -> Self where T: Mean { - PosC::try_from_iter_2( - izip!(&self.0, &other.0) - .map(|(a,b)| Ok::<_,Void>(a.mean(b))) - ).unwrap_or_else(|v| match v { }) - } - } - - impl PosC { - pub fn promote(&self) -> PosC { self.map(|v| v as f64) } - } +impl Default for PieceAngle { + fn default() -> Self { PieceAngle::Compass(default()) } +} - #[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)] - pub struct PosCFromIteratorError; - display_as_debug!{PosCFromIteratorError} - - impl PosC { - #[throws(PosCFromIteratorError)] - pub fn from_iter>(i: I) -> Self { PosC( - i - .collect::>() - .into_inner() - .map_err(|_| PosCFromIteratorError)? - )} +impl TryFrom for CompassAngle { + type Error = SpecError; + #[throws(SpecError)] + fn try_from(v: u8) -> Self { + if v < 8 { Self(v) } + else { throw!(SpE::CompassAngleInvalid) } } +} - impl PosC { - /// Panics if the iterator doesn't yield exactly 2 elements - #[throws(E)] - pub fn try_from_iter_2< - E: Debug, - I: Iterator> - >(i: I) -> Self { PosC( - i - .collect::,E>>()? - .into_inner().unwrap() - )} +impl From for u8 { + fn from(a: CompassAngle) -> u8 { + a.0 } } //---------- Implementation ---------- pub mod implementation { - use super::*; + use super::{*, SpE}; use crate::prelude::*; type AS = AccountScope; type TPS = TablePlayerSpec; - impl AreaC { - pub fn contains(&self, p: PosC) -> bool where T: Ord { - (0..2).all(|i| { - p.0[i] >= self.0[0].0[i] && - p.0[i] <= self.0[1].0[i] - }) - } - - pub fn overlaps(&self, other: &AreaC) -> bool where T: Ord { - ! (0..2).any(|i| ( - other.0[1].0[i] < self .0[0].0[i] || - self .0[1].0[i] < other.0[0].0[i] - )) - } - - pub fn empty() -> Self where T: Copy + num_traits::Zero + num_traits::One { - let zero = ::zero(); - let one = ::one(); - AreaC([ - PosC([ one, one ]), - PosC([ zero, zero ]), - ]) - } - } - - impl AreaC where T: Mean + Debug { - pub fn middle(&self) -> PosC { - Mean::mean(&self.0[0], - &self.0[1]) - } - } - - #[test] - fn empty_area() { - let empty = Area::empty(); - for x in -3..3 { for y in -3..3 { - assert!(! empty.contains(PosC([x,y]))); - } } - } - - impl Default for PieceAngle { - fn default() -> Self { PieceAngle::Compass(default()) } - } - - impl TryFrom for CompassAngle { - type Error = SpecError; - #[throws(SpecError)] - fn try_from(v: u8) -> Self { - if v < 8 { Self(v) } - else { throw!(SpE::CompassAngleInvalid) } - } - } - - impl From for u8 { - fn from(a: CompassAngle) -> u8 { - a.0 - } - } - impl Default for piece_specs::PieceLabelPlace { fn default() -> Self { Self::BottomLeft } } @@ -703,9 +535,9 @@ pub mod implementation { lazy_static! { static ref RE: Regex = Regex::new(concat!( r"^(?:", r"[[:alpha:]]{1,50}", - r"|", r"#[[:xdigit:]]{3}{1,2}", - r"|", r"(?:rgba?|hsla?)\([-.%\t 0-9]{1,50}\)", - r")$" + r"|", r"#[[:xdigit:]]{3}{1,2}", + r"|", r"(?:rgba?|hsla?)\([-.%\t 0-9]{1,50}\)", + r")$" )).unwrap(); } let s = &spec.0;