From bccb81f5a67023d5ebc4bfd07c7ca4945432d6c8 Mon Sep 17 00:00:00 2001 From: Sean Olson Date: Tue, 17 Sep 2019 11:56:50 -0700 Subject: [PATCH] Simplify geometric traits to reduce the complexity of type bounds. --- examples/subdivide.rs | 4 +- plexus/src/graph/geometry.rs | 170 +++++++++++++++----------------- plexus/src/graph/mod.rs | 2 +- plexus/src/graph/view/edge.rs | 17 ++-- plexus/src/graph/view/face.rs | 19 ++-- plexus/src/graph/view/vertex.rs | 7 +- 6 files changed, 107 insertions(+), 112 deletions(-) diff --git a/examples/subdivide.rs b/examples/subdivide.rs index 59bd0292..1f2d69eb 100644 --- a/examples/subdivide.rs +++ b/examples/subdivide.rs @@ -1,6 +1,6 @@ use decorum::N64; use nalgebra::Point2; -use plexus::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph, VertexPosition}; +use plexus::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph}; use plexus::prelude::*; use plexus::primitive::NGon; use plexus::AsPosition; @@ -8,7 +8,7 @@ use smallvec::SmallVec; pub fn circumscribe(face: FaceView<&mut MeshGraph, G>) -> FaceView<&mut MeshGraph, G> where - G: EdgeMidpoint> + GraphGeometry, + G: EdgeMidpoint + GraphGeometry, G::Vertex: AsPosition, { // Split each edge, stashing the vertex key and moving to the next arc. diff --git a/plexus/src/graph/geometry.rs b/plexus/src/graph/geometry.rs index 84b6dd11..56ecbb4d 100644 --- a/plexus/src/graph/geometry.rs +++ b/plexus/src/graph/geometry.rs @@ -2,21 +2,21 @@ //! //! This module provides traits that specify the geometry of `MeshGraph`s and //! express the spatial operations supported by that geometry. The most basic -//! and only required trait is `Geometry`, which uses associated types to +//! and only required trait is `GraphGeometry`, which uses associated types to //! specify the type of the `geometry` field in the payloads for vertices, //! arcs, edges, and faces in a graph. //! -//! To support useful spatial operations, types that implement `Geometry` may -//! also implement `AsPosition`. The `AsPosition` trait exposes positional data -//! in vertices. If that positional data also implements spatial traits from -//! the [theon](https://crates.io/crates/theon) crate, then spatial operations -//! will be enabled, such as `smooth`, `split_at_midpoint`, and +//! To support useful spatial operations, types that implement `GraphGeometry` +//! may also implement `AsPosition`. The `AsPosition` trait exposes positional +//! data in vertices. If that positional data also implements spatial traits +//! from the [`theon`](https://crates.io/crates/theon) crate, then spatial +//! operations will be enabled, such as `smooth`, `split_at_midpoint`, and //! `poke_with_offset`. //! //! This module also defines traits that define the capabilities of geometry in //! a `MeshGraph`. These traits enable generic code without the need to express //! complicated relationships between types representing a Euclidean space. For -//! example, the `FaceCentroid` trait is defined for `Geometry` types that +//! example, the `FaceCentroid` trait is defined for `GraphGeometry` types that //! expose positional data that implements the necessary traits to compute the //! centroid of a face. //! @@ -29,7 +29,7 @@ //! # extern crate plexus; //! # extern crate smallvec; //! # -//! use plexus::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph, VertexPosition}; +//! use plexus::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph}; //! use plexus::prelude::*; //! use plexus::AsPosition; //! use smallvec::SmallVec; @@ -38,7 +38,7 @@ //! // Requires `EdgeMidpoint` for `split_at_midpoint`. //! pub fn circumscribe(face: FaceView<&mut MeshGraph, G>) -> FaceView<&mut MeshGraph, G> //! where -//! G: EdgeMidpoint> + GraphGeometry, +//! G: EdgeMidpoint + GraphGeometry, //! G::Vertex: AsPosition, //! { //! let arity = face.arity(); @@ -60,9 +60,18 @@ // TODO: Integrate this module documentation into the `graph` module. +// Geometric traits like `FaceNormal` and `EdgeMidpoint` are defined in such a +// way to reduce the contraints necessary for writing generic user code. With +// few exceptions, these traits only depend on `AsPosition` being implemented +// by the `Vertex` type in their definition. If a more complex implementation +// is necessary, constraints are specified there so that they do not pollute +// user code. + use theon::ops::{Cross, Interpolate, Project}; -use theon::space::{EuclideanSpace, InnerSpace, Vector, VectorSpace}; +use theon::query::Plane; +use theon::space::{EuclideanSpace, FiniteDimensional, InnerSpace, Vector, VectorSpace}; use theon::{AsPosition, FromItems, Position}; +use typenum::U3; use crate::graph::borrow::Reborrow; use crate::graph::mutation::Consistent; @@ -164,29 +173,16 @@ where type Face = (); } -pub trait VertexCentroid: GraphGeometry { - type Centroid; - - fn centroid(vertex: VertexView) -> Result - where - M: Reborrow, - M::Target: AsStorage> + AsStorage> + Consistent; -} - -impl VertexCentroid for G +pub trait VertexCentroid: GraphGeometry where - G: GraphGeometry, - G::Vertex: AsPosition, - VertexPosition: EuclideanSpace, + Self::Vertex: AsPosition, { - type Centroid = VertexPosition; - - fn centroid(vertex: VertexView) -> Result + fn centroid(vertex: VertexView) -> Result, GraphError> where M: Reborrow, M::Target: AsStorage> + AsStorage> + Consistent, { - VertexPosition::::centroid( + VertexPosition::::centroid( vertex .neighboring_vertices() .map(|vertex| *vertex.geometry.as_position()), @@ -195,23 +191,18 @@ where } } -pub trait VertexNormal: FaceNormal + GraphGeometry { - fn normal(vertex: VertexView) -> Result - where - M: Reborrow, - M::Target: AsStorage> - + AsStorage> - + AsStorage> - + Consistent; +impl VertexCentroid for G +where + G: GraphGeometry, + G::Vertex: AsPosition, +{ } -impl VertexNormal for G +pub trait VertexNormal: FaceNormal where - G: FaceNormal>>, - G::Vertex: AsPosition, - VertexPosition: EuclideanSpace, + Self::Vertex: AsPosition, { - fn normal(vertex: VertexView) -> Result + fn normal(vertex: VertexView) -> Result>, GraphError> where M: Reborrow, M::Target: AsStorage> @@ -219,10 +210,10 @@ where + AsStorage> + Consistent, { - Vector::>::mean( + Vector::>::mean( vertex .neighboring_faces() - .map(G::normal) + .map(::normal) .collect::, _>>()?, ) .ok_or_else(|| GraphError::TopologyNotFound)? @@ -231,10 +222,18 @@ where } } -pub trait ArcNormal: GraphGeometry { - type Normal; +impl VertexNormal for G +where + G: FaceNormal, + G::Vertex: AsPosition, +{ +} - fn normal(arc: ArcView) -> Result +pub trait ArcNormal: GraphGeometry +where + Self::Vertex: AsPosition, +{ + fn normal(arc: ArcView) -> Result>, GraphError> where M: Reborrow, M::Target: AsStorage> + AsStorage> + Consistent; @@ -244,12 +243,10 @@ impl ArcNormal for G where G: GraphGeometry, G::Vertex: AsPosition, - Vector>: Project>>, VertexPosition: EuclideanSpace, + Vector>: Project>>, { - type Normal = Vector>; - - fn normal(arc: ArcView) -> Result + fn normal(arc: ArcView) -> Result>, GraphError> where M: Reborrow, M::Target: AsStorage> + AsStorage> + Consistent, @@ -264,10 +261,11 @@ where } } -pub trait EdgeMidpoint: GraphGeometry { - type Midpoint; - - fn midpoint(edge: E) -> Result +pub trait EdgeMidpoint: GraphGeometry +where + Self::Vertex: AsPosition, +{ + fn midpoint(edge: E) -> Result, GraphError> where E: CompositeEdge, M: Reborrow, @@ -281,11 +279,9 @@ impl EdgeMidpoint for G where G: GraphGeometry, G::Vertex: AsPosition, - VertexPosition: EuclideanSpace + Interpolate>, + VertexPosition: Interpolate>, { - type Midpoint = VertexPosition; - - fn midpoint(edge: E) -> Result + fn midpoint(edge: E) -> Result, GraphError> where E: CompositeEdge, M: Reborrow, @@ -301,41 +297,35 @@ where } } -pub trait FaceCentroid: GraphGeometry { - type Centroid; - - fn centroid(ring: R) -> Result - where - R: Ring, - M: Reborrow, - M::Target: AsStorage> + AsStorage> + Consistent; -} - -impl FaceCentroid for G +pub trait FaceCentroid: GraphGeometry where - G: GraphGeometry, - G::Vertex: AsPosition, - VertexPosition: EuclideanSpace, + Self::Vertex: AsPosition, { - type Centroid = VertexPosition; - - fn centroid(ring: R) -> Result + fn centroid(ring: R) -> Result, GraphError> where R: Ring, M: Reborrow, M::Target: AsStorage> + AsStorage> + Consistent, { Ok( - VertexPosition::::centroid(ring.vertices().map(|vertex| *vertex.position())) + VertexPosition::::centroid(ring.vertices().map(|vertex| *vertex.position())) .expect_consistent(), ) } } -pub trait FaceNormal: GraphGeometry { - type Normal; +impl FaceCentroid for G +where + G: GraphGeometry, + G::Vertex: AsPosition, +{ +} - fn normal(ring: R) -> Result +pub trait FaceNormal: GraphGeometry +where + Self::Vertex: AsPosition, +{ + fn normal(ring: R) -> Result>, GraphError> where R: Ring, M: Reborrow, @@ -344,14 +334,12 @@ pub trait FaceNormal: GraphGeometry { impl FaceNormal for G where - G: FaceCentroid> + GraphGeometry, + G: FaceCentroid + GraphGeometry, G::Vertex: AsPosition, Vector>: Cross>>, VertexPosition: EuclideanSpace, { - type Normal = Vector>; - - fn normal(ring: R) -> Result + fn normal(ring: R) -> Result>, GraphError> where R: Ring, M: Reborrow, @@ -367,10 +355,12 @@ where } } -pub trait FacePlane: GraphGeometry { - type Plane; - - fn plane(ring: R) -> Result +pub trait FacePlane: GraphGeometry +where + Self::Vertex: AsPosition, + VertexPosition: FiniteDimensional, +{ + fn plane(ring: R) -> Result>, GraphError> where R: Ring, M: Reborrow, @@ -385,10 +375,8 @@ mod array { use smallvec::SmallVec; use theon::array::ArrayScalar; - use theon::query::Plane; - use theon::space::{FiniteDimensional, Scalar}; + use theon::space::Scalar; use theon::{FromItems, IntoItems}; - use typenum::U3; impl FacePlane for G where @@ -398,9 +386,7 @@ mod array { Scalar>: ArrayScalar, Vector>: FromItems + IntoItems, { - type Plane = Plane>; - - fn plane(ring: R) -> Result + fn plane(ring: R) -> Result>, GraphError> where R: Ring, M: Reborrow, diff --git a/plexus/src/graph/mod.rs b/plexus/src/graph/mod.rs index d3d9e986..f4eb6c90 100644 --- a/plexus/src/graph/mod.rs +++ b/plexus/src/graph/mod.rs @@ -571,7 +571,7 @@ where pub fn smooth(&mut self, factor: T) where T: Into>>, - G: VertexCentroid>, + G: VertexCentroid, G::Vertex: AsPosition, VertexPosition: EuclideanSpace, { diff --git a/plexus/src/graph/view/edge.rs b/plexus/src/graph/view/edge.rs index d35e970a..c1ed526d 100644 --- a/plexus/src/graph/view/edge.rs +++ b/plexus/src/graph/view/edge.rs @@ -511,11 +511,12 @@ impl ArcView where M: Reborrow, M::Target: AsStorage> + AsStorage> + Consistent, - G: ArcNormal, + G: GraphGeometry, { - pub fn normal(&self) -> G::Normal + pub fn normal(&self) -> Vector> where - G: GraphGeometry, + G: ArcNormal, + G::Vertex: AsPosition, { G::normal(self.interior_reborrow()).expect_consistent() } @@ -530,9 +531,10 @@ where + Consistent, G: GraphGeometry, { - pub fn midpoint(&self) -> G::Midpoint + pub fn midpoint(&self) -> VertexPosition where G: EdgeMidpoint, + G::Vertex: AsPosition, { G::midpoint(self.interior_reborrow()).expect_consistent() } @@ -637,7 +639,7 @@ where /// ``` pub fn split_at_midpoint(self) -> VertexView<&'a mut M, G> where - G: EdgeMidpoint>, + G: EdgeMidpoint, G::Vertex: AsPosition, { let mut geometry = self.source_vertex().geometry; @@ -797,7 +799,7 @@ where pub fn extrude(self, offset: T) -> Result, GraphError> where T: Into>>, - G: ArcNormal>>, + G: ArcNormal, G::Vertex: AsPosition, VertexPosition: EuclideanSpace, { @@ -1139,9 +1141,10 @@ where + Consistent, G: GraphGeometry, { - pub fn midpoint(&self) -> G::Midpoint + pub fn midpoint(&self) -> VertexPosition where G: EdgeMidpoint, + G::Vertex: AsPosition, { G::midpoint(self.interior_reborrow()).expect_consistent() } diff --git a/plexus/src/graph/view/face.rs b/plexus/src/graph/view/face.rs index f33039c5..1ee7a0b9 100644 --- a/plexus/src/graph/view/face.rs +++ b/plexus/src/graph/view/face.rs @@ -323,23 +323,27 @@ where >::vertices(self) } - pub fn centroid(&self) -> G::Centroid + pub fn centroid(&self) -> VertexPosition where G: FaceCentroid, + G::Vertex: AsPosition, { G::centroid(self.interior_reborrow()).expect_consistent() } - pub fn normal(&self) -> G::Normal + pub fn normal(&self) -> Vector> where G: FaceNormal, + G::Vertex: AsPosition, { G::normal(self.interior_reborrow()).expect_consistent() } - pub fn plane(&self) -> Result + pub fn plane(&self) -> Result>, GraphError> where G: FacePlane, + G::Vertex: AsPosition, + VertexPosition: FiniteDimensional, { G::plane(self.interior_reborrow()) } @@ -397,7 +401,7 @@ where /// could not be translated into the plane. pub fn flatten(&mut self) -> Result<(), GraphError> where - G: FacePlane>>, + G: FacePlane, G::Vertex: AsPosition, VertexPosition: EuclideanSpace + FiniteDimensional, { @@ -683,7 +687,7 @@ where /// Returns the inserted vertex. pub fn poke_at_centroid(self) -> VertexView<&'a mut M, G> where - G: FaceCentroid>, + G: FaceCentroid, G::Vertex: AsPosition, { let mut geometry = self.arc().source_vertex().geometry; @@ -730,8 +734,7 @@ where pub fn poke_with_offset(self, offset: T) -> VertexView<&'a mut M, G> where T: Into>>, - G: FaceCentroid> - + FaceNormal>>, + G: FaceCentroid + FaceNormal, G::Vertex: AsPosition, VertexPosition: EuclideanSpace, { @@ -746,7 +749,7 @@ where pub fn extrude(self, offset: T) -> FaceView<&'a mut M, G> where T: Into>>, - G: FaceNormal>>, + G: FaceNormal, G::Vertex: AsPosition, VertexPosition: EuclideanSpace, { diff --git a/plexus/src/graph/view/vertex.rs b/plexus/src/graph/view/vertex.rs index 407af161..3fdc1972 100644 --- a/plexus/src/graph/view/vertex.rs +++ b/plexus/src/graph/view/vertex.rs @@ -3,6 +3,7 @@ use smallvec::SmallVec; use std::marker::PhantomData; use std::mem; use std::ops::{Deref, DerefMut}; +use theon::space::Vector; use theon::AsPosition; use crate::graph::borrow::{Reborrow, ReborrowMut}; @@ -246,9 +247,10 @@ where .map(|arc| arc.into_opposite_arc()) } - pub fn centroid(&self) -> G::Centroid + pub fn centroid(&self) -> VertexPosition where G: VertexCentroid, + G::Vertex: AsPosition, { G::centroid(self.interior_reborrow()).expect_consistent() } @@ -307,9 +309,10 @@ where )) } - pub fn normal(&self) -> Result + pub fn normal(&self) -> Result>, GraphError> where G: VertexNormal, + G::Vertex: AsPosition, { ::normal(self.interior_reborrow()) }