diff --git a/README.md b/README.md index df184b7e2..cf330e0f1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ ## フォルダ構成 - アプリケーション: - - [`nusamai`](./nusamai/) — アプリケーションバックエンドおよびコマンドライン版の実装 - 基盤・ユーティリティ: - [`nusamai-geometry`](./nusamai-geometry/) — ジオメトリ型 - [`nusamai-projection`](./nusamai-projection/) — 投影法変換 diff --git a/nusamai-geometry/src/compact/linestring.rs b/nusamai-geometry/src/compact/linestring.rs index e08cd6691..90b92218b 100644 --- a/nusamai-geometry/src/compact/linestring.rs +++ b/nusamai-geometry/src/compact/linestring.rs @@ -1,9 +1,9 @@ use crate::{Coord, Coord2d}; -use std::borrow::Cow; +use std::{borrow::Cow, hash::Hash}; /// Computer-friendly LineString #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, Default, PartialEq)] +#[derive(Debug, Clone, Default)] pub struct LineString<'a, T: Coord> { /// Coordinates of all points /// @@ -14,6 +14,34 @@ pub struct LineString<'a, T: Coord> { pub type LineString2<'a, C = f64> = LineString<'a, [C; 2]>; pub type LineString3<'a, C = f64> = LineString<'a, [C; 3]>; +impl PartialEq for LineString2<'_, f64> { + fn eq(&self, other: &Self) -> bool { + self.coords == other.coords + } +} + +impl PartialEq for LineString3<'_, f64> { + fn eq(&self, other: &Self) -> bool { + self.coords == other.coords + } +} + +impl Hash for LineString2<'_, f64> { + fn hash(&self, state: &mut H) { + self.coords + .iter() + .for_each(|c| c.iter().for_each(|a| a.to_bits().hash(state))); + } +} + +impl Hash for LineString3<'_, f64> { + fn hash(&self, state: &mut H) { + self.coords + .iter() + .for_each(|c| c.iter().for_each(|a| a.to_bits().hash(state))); + } +} + impl<'a, T: Coord> LineString<'a, T> { /// Creates an empty LineString. pub fn new() -> Self { diff --git a/nusamai-geometry/src/compact/polygon.rs b/nusamai-geometry/src/compact/polygon.rs index 71facf9ca..fbaeb5a1a 100644 --- a/nusamai-geometry/src/compact/polygon.rs +++ b/nusamai-geometry/src/compact/polygon.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, hash::Hash}; use crate::Coord2d; @@ -6,7 +6,7 @@ use super::{linestring::LineString, Coord}; /// Computer-friendly Polygon #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, Default, PartialEq)] +#[derive(Debug, Clone, Default)] pub struct Polygon<'a, T: Coord> { /// Coordinates coords: Cow<'a, [T]>, @@ -18,6 +18,41 @@ pub struct Polygon<'a, T: Coord> { pub type Polygon3<'a, C = f64> = Polygon<'a, [C; 3]>; pub type Polygon2<'a, C = f64> = Polygon<'a, [C; 2]>; +impl PartialEq for Polygon3<'_, f64> { + fn eq(&self, other: &Self) -> bool { + self.exterior() == other.exterior() + && self.interiors().zip(other.interiors()).all(|(a, b)| a == b) + } +} + +impl PartialEq for Polygon2<'_, f64> { + fn eq(&self, other: &Self) -> bool { + self.exterior() == other.exterior() + && self.interiors().zip(other.interiors()).all(|(a, b)| a == b) + } +} + +impl Hash for Polygon2<'_, f64> { + fn hash(&self, state: &mut H) { + self.exterior().hash(state); + for interior in self.interiors() { + interior.hash(state); + } + } +} + +impl Hash for Polygon3<'_, f64> { + fn hash(&self, state: &mut H) { + self.exterior().hash(state); + for interior in self.interiors() { + interior.hash(state); + } + } +} + +impl Eq for Polygon3<'_, f64> {} +impl Eq for Polygon2<'_, f64> {} + impl<'a, T: Coord> Polygon<'a, T> { /// Creates an empty Polygon. pub fn new() -> Self { @@ -98,7 +133,7 @@ impl<'a, T: Coord> Polygon<'a, T> { } /// Create a new Polygon by applying the given transformation to all coordinates. - pub fn transform(&self, f: impl Fn(&T) -> T2) -> Polygon { + pub fn transform(self, f: impl Fn(&T) -> T2) -> Polygon<'a, T2> { Polygon { coords: self.coords.iter().map(f).collect(), hole_indices: self.hole_indices.clone(),