chiark / gitweb /
geometry: Move to new module
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 24 Mar 2021 23:57:08 +0000 (23:57 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 24 Mar 2021 23:57:21 +0000 (23:57 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/geometry.rs [new file with mode: 0644]
src/lib.rs
src/prelude.rs
src/spec.rs

diff --git a/src/geometry.rs b/src/geometry.rs
new file mode 100644 (file)
index 0000000..4b58c7c
--- /dev/null
@@ -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<T>(pub [T; 2]);
+pub type Pos = PosC<Coord>;
+
+#[derive(Clone,Copy,Debug,Serialize,Deserialize,Hash)]
+#[derive(Eq,PartialEq)]
+#[serde(transparent)]
+pub struct AreaC<T>(pub [PosC<T>; 2]);
+pub type Area = AreaC<Coord>;
+
+//---------- 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<T:CheckedArith> Add<PosC<T>> for PosC<T> {
+  type Output = Result<Self, CoordinateOverflow>;
+  #[throws(CoordinateOverflow)]
+  fn add(self, rhs: PosC<T>) -> PosC<T> {
+    PosC::try_from_iter_2(
+      itertools::zip_eq(
+        self.0.iter().cloned(),
+        rhs .0.iter().cloned(),
+      ).map(
+        |(a,b)| a.checked_add(b)
+      )
+    )?
+  }
+}
+
+impl<T:CheckedArith> Sub<PosC<T>> for PosC<T> {
+  type Output = Result<Self, CoordinateOverflow>;
+  #[throws(CoordinateOverflow)]
+  fn sub(self, rhs: PosC<T>) -> PosC<T> {
+    PosC::try_from_iter_2(
+      itertools::zip_eq(
+        self.0.iter().cloned(),
+        rhs .0.iter().cloned(),
+      ).map(|(a,b)| a.checked_sub(b))
+    )?
+  }
+}
+
+impl<S:Copy+Debug+Clone+'static,T:CheckedArithMul<S>> Mul<S> for PosC<T> {
+  type Output = Result<Self, CoordinateOverflow>;
+  #[throws(CoordinateOverflow)]
+  fn mul(self, rhs: S) -> PosC<T> {
+    PosC::try_from_iter_2(
+      self.0.iter().cloned().map(
+        |a| a.checked_mul(rhs)
+      )
+    )?
+  }
+}
+
+impl<T:CheckedArith> Neg for PosC<T> {
+  type Output = Result<Self, CoordinateOverflow>;
+  #[throws(CoordinateOverflow)]
+  fn neg(self) -> Self {
+    PosC::try_from_iter_2(
+      self.0.iter().cloned().map(|a| a.checked_neg())
+    )?
+  }
+}
+
+impl<T:Copy+Clone+Debug> PosC<T> {
+  pub fn map<U:Copy+Clone+Debug, F: FnMut(T) -> U>(self, f: F) -> PosC<U> {
+    PosC::from_iter(
+      self.0.iter().cloned().map(f)
+    ).unwrap()
+  }
+}
+
+impl<T:Copy+Clone+Debug> PosC<T> {
+  pub fn try_map<E:Debug, U:Copy+Clone+Debug, F: FnMut(T) -> Result<U,E>>
+    (self, f: F) -> Result<PosC<U>,E>
+  {
+    PosC::try_from_iter_2(
+      self.0.iter().cloned().map(f)
+    )
+  }
+}
+
+impl<T> Mean for PosC<T> 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<Coord> {
+  pub fn promote(&self) -> PosC<f64> { self.map(|v| v as f64) }
+}
+
+#[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)]
+pub struct PosCFromIteratorError;
+display_as_debug!{PosCFromIteratorError}
+
+impl<T> PosC<T> {
+  #[throws(PosCFromIteratorError)]
+  pub fn from_iter<I: Iterator<Item=T>>(i: I) -> Self { PosC(
+    i
+      .collect::<ArrayVec<_>>()
+      .into_inner()
+      .map_err(|_| PosCFromIteratorError)?
+  )}
+}
+
+impl<T:Debug> PosC<T> {
+  /// Panics if the iterator doesn't yield exactly 2 elements
+  #[throws(E)]
+  pub fn try_from_iter_2<
+    E: Debug,
+    I: Iterator<Item=Result<T,E>>
+  >(i: I) -> Self { PosC(
+    i
+      .collect::<Result<ArrayVec<_>,E>>()?
+      .into_inner().unwrap()
+  )}
+}
+
+// ---------- Area ----------
+
+impl<T> AreaC<T> {
+  pub fn contains(&self, p: PosC<T>) -> 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<T>) -> 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 = <T as num_traits::Zero>::zero();
+    let one = <T as num_traits::One>::one();
+    AreaC([
+      PosC([ one,  one  ]),
+      PosC([ zero, zero ]),
+    ])
+  }
+}
+
+impl<T> AreaC<T> where T: Mean + Debug {
+  pub fn middle(&self) -> PosC<T> {
+    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])));
+  } }
+}
index 5ddbdc100633de61338e6f65ee8cb4a88803cc99..66ad53da8134738dc5ca0a2eb2a068d803393378 100644 (file)
@@ -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;
index 975f7019245fa5ef557ca2b61bb8df87d9b1efd8..657ad455b4110f158b2f8c42bc91d633f2f34392 100644 (file)
@@ -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;
index d573580448ba8de3f642ffa52c01f2ed7e026eb1..10c819d0a82ad13e79fd011e0f9053b32dfa10b6 100644 (file)
@@ -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<T>(pub [T; 2]);
-pub type Pos = PosC<Coord>;
+type SpE = SpecError;
 
-#[derive(Clone,Copy,Debug,Serialize,Deserialize,Hash)]
-#[derive(Eq,PartialEq)]
-#[serde(transparent)]
-pub struct AreaC<T>(pub [PosC<T>; 2]);
-pub type Area = AreaC<Coord>;
+//---------- 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<T:CheckedArith> Add<PosC<T>> for PosC<T> {
-    type Output = Result<Self, CoordinateOverflow>;
-    #[throws(CoordinateOverflow)]
-    fn add(self, rhs: PosC<T>) -> PosC<T> {
-      PosC::try_from_iter_2(
-        itertools::zip_eq(
-          self.0.iter().cloned(),
-          rhs .0.iter().cloned(),
-        ).map(
-          |(a,b)| a.checked_add(b)
-        )
-      )?
-    }
-  }
-
-  impl<T:CheckedArith> Sub<PosC<T>> for PosC<T> {
-    type Output = Result<Self, CoordinateOverflow>;
-    #[throws(CoordinateOverflow)]
-    fn sub(self, rhs: PosC<T>) -> PosC<T> {
-      PosC::try_from_iter_2(
-        itertools::zip_eq(
-          self.0.iter().cloned(),
-          rhs .0.iter().cloned(),
-        ).map(|(a,b)| a.checked_sub(b))
-      )?
-    }
-  }
-
-  impl<S:Copy+Debug+Clone+'static,T:CheckedArithMul<S>> Mul<S> for PosC<T> {
-    type Output = Result<Self, CoordinateOverflow>;
-    #[throws(CoordinateOverflow)]
-    fn mul(self, rhs: S) -> PosC<T> {
-      PosC::try_from_iter_2(
-        self.0.iter().cloned().map(
-          |a| a.checked_mul(rhs)
-        )
-      )?
-    }
-  }
-
-  impl<T:CheckedArith> Neg for PosC<T> {
-    type Output = Result<Self, CoordinateOverflow>;
-    #[throws(CoordinateOverflow)]
-    fn neg(self) -> Self {
-      PosC::try_from_iter_2(
-        self.0.iter().cloned().map(|a| a.checked_neg())
-      )?
-    }
-  }
-
-  impl<T:Copy+Clone+Debug> PosC<T> {
-    pub fn map<U:Copy+Clone+Debug, F: FnMut(T) -> U>(self, f: F) -> PosC<U> {
-      PosC::from_iter(
-        self.0.iter().cloned().map(f)
-      ).unwrap()
-    }
-  }
+// ---------- Implementation - angles ----------
 
-  impl<T:Copy+Clone+Debug> PosC<T> {
-    pub fn try_map<E:Debug, U:Copy+Clone+Debug, F: FnMut(T) -> Result<U,E>>
-      (self, f: F) -> Result<PosC<U>,E>
-    {
-      PosC::try_from_iter_2(
-        self.0.iter().cloned().map(f)
-      )
-    }
-  }
-
-  impl<T> Mean for PosC<T> 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<Coord> {
-    pub fn promote(&self) -> PosC<f64> { 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<T> PosC<T> {
-    #[throws(PosCFromIteratorError)]
-    pub fn from_iter<I: Iterator<Item=T>>(i: I) -> Self { PosC(
-      i
-        .collect::<ArrayVec<_>>()
-        .into_inner()
-        .map_err(|_| PosCFromIteratorError)?
-    )}
+impl TryFrom<u8> for CompassAngle {
+  type Error = SpecError;
+  #[throws(SpecError)]
+  fn try_from(v: u8) -> Self {
+    if v < 8 { Self(v) }
+    else { throw!(SpE::CompassAngleInvalid) }
   }
+}
 
-  impl<T:Debug> PosC<T> {
-    /// Panics if the iterator doesn't yield exactly 2 elements
-    #[throws(E)]
-    pub fn try_from_iter_2<
-      E: Debug,
-      I: Iterator<Item=Result<T,E>>
-    >(i: I) -> Self { PosC(
-      i
-        .collect::<Result<ArrayVec<_>,E>>()?
-        .into_inner().unwrap()
-    )}
+impl From<CompassAngle> 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<T> AreaC<T> {
-    pub fn contains(&self, p: PosC<T>) -> 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<T>) -> 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 = <T as num_traits::Zero>::zero();
-      let one = <T as num_traits::One>::one();
-      AreaC([
-        PosC([ one,  one  ]),
-        PosC([ zero, zero ]),
-      ])
-    }
-  }
-
-  impl<T> AreaC<T> where T: Mean + Debug {
-    pub fn middle(&self) -> PosC<T> {
-      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<u8> for CompassAngle {
-    type Error = SpecError;
-    #[throws(SpecError)]
-    fn try_from(v: u8) -> Self {
-      if v < 8 { Self(v) }
-      else { throw!(SpE::CompassAngleInvalid) }
-    }
-  }
-
-  impl From<CompassAngle> 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;