Skip to content

Commit

Permalink
Refactor traits into an independent crate.
Browse files Browse the repository at this point in the history
This change refactors adjunct and geometric traits into the Eudoxus
crate. Eudoxus provides essential traits and implementations for
primitives and other foreign types. Theon now depends on Eudoxus and
re-exports these traits while providing additional types and APIs.

Theon remains the primary crate and API. Because rich sets of traits are
infectious, Eudoxus is meant to change very infrequently while Theon can
change without invalidating any foreign trait implementations. Theon can
also provide APIs with richer type constraints including traits from
various crates in the ecosystem.
  • Loading branch information
olson-sean-k committed Jul 12, 2021
1 parent 74ffd82 commit 8ad482c
Show file tree
Hide file tree
Showing 19 changed files with 1,332 additions and 791 deletions.
62 changes: 4 additions & 58 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,59 +1,5 @@
[package]
name = "theon"
description = "Abstraction of Euclidean spaces."
keywords = ["geometry", "graphics", "math"]
version = "0.0.1"
authors = ["Sean Olson <[email protected]>"]
edition = "2018"
license = "MIT"
readme = "README.md"
repository = "https://github.com/olson-sean-k/theon"

[package.metadata.docs.rs]
default-target = "x86_64-unknown-linux-gnu"
# Enable all features so that trait implementations for types from commonly used
# crates are shown.
all-features = true
# Enable KaTex support.
rustdoc-args = [
"--html-in-header",
"doc/katex-header.html",
[workspace]
members = [
"eudoxus",
"theon"
]

[features]
default = ["geometry-nalgebra"]
geometry-cgmath = ["cgmath"]
geometry-glam = ["glam"]
geometry-mint = ["mint"]
geometry-nalgebra = ["nalgebra"]
geometry-ultraviolet = ["ultraviolet"]
lapack = ["ndarray", "ndarray-linalg"]

[dependencies]
approx = "^0.3.0"
arrayvec = "^0.5.1"
decorum = "^0.3.0"
itertools = "^0.9.0"
num = "^0.3.0"
typenum = "^1.10.0"

cgmath = { version = "^0.17.0", optional = true }
glam = { version = "^0.9.0", optional = true }
mint = { version = "^0.5.0", optional = true }
nalgebra = { version = "^0.22.0", optional = true }
ultraviolet = { version = "^0.6.0", optional = true }

[target.'cfg(target_os = "linux")'.dependencies.ndarray]
version = "^0.13.0"
optional = true

# LAPACK packages are difficult to distribute. MKL appears to build reliably on
# Linux, but does not support Windows and yields strange results on MacOS. The
# other supported packages require a more complex build environment.
[target.'cfg(target_os = "linux")'.dependencies.ndarray-linalg]
version = "^0.12.0"
features = ["intel-mkl"]
optional = true

[dev-dependencies]
nalgebra = "^0.22.0"
23 changes: 9 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ Theon provides optional implementations for commonly used crates in the Rust
ecosystem, including [`glam`] and [`ultraviolet`]. These implementations can be
enabled using Cargo features.

| Feature | Default | Crate | Support |
|------------------------|---------|-----------------|-----------|
| `geometry-cgmath` | No | [`cgmath`] | Complete¹ |
| `geometry-glam` | No | [`glam`] | Complete¹ |
| `geometry-mint` | No | [`mint`] | Partial |
| `geometry-nalgebra` | Yes | [`nalgebra`] | Complete¹ |
| `geometry-ultraviolet` | No | [`ultraviolet`] | Partial² |
| Feature | Crate | Version | Support |
|------------------------|-----------------|----------|----------|
| `geometry-cgmath` | [`cgmath`] | `0.18.0` | Complete |
| `geometry-glam` | [`glam`] | `0.17.1` | Complete |
| `geometry-mint` | [`mint`] | `0.5.6` | Partial |
| `geometry-nalgebra` | [`nalgebra`] | `0.28.0` | Complete |
| `geometry-ultraviolet` | [`ultraviolet`] | `0.8.1` | Partial¹ |

Integrated crates are re-exported in the `integration` module. Because a given
version of Theon implements traits for specific versions of integrated crates,
Expand All @@ -41,12 +41,7 @@ re-exported crates provided by Theon with no direct dependency or ensure that
the version of a supported crate resolves to the same version for which Theon
implements its traits.

\[1\]: Because Theon is still in its initial development phase, _complete_ does
not necessarily mean that all traits and features are implemented, but instead
that all traits and features can be feasibly supported and are implemented for
common use cases.

\[2\]: Importantly, traits and features are not yet implemented for SIMD types
\[1\]: Importantly, traits and features are not yet implemented for SIMD types
like `Wec3`.

## Spatial Queries
Expand Down Expand Up @@ -96,4 +91,4 @@ The `lapack` feature only supports Linux at this time.
[`mint`]: https://crates.io/crates/mint
[`nalgebra`]: https://crates.io/crates/nalgebra
[`ndarray`]: https://crates.io/crates/ndarray
[`ultraviolet`]: https://crates.io/crates/ultraviolet
[`ultraviolet`]: https://crates.io/crates/ultraviolet
43 changes: 43 additions & 0 deletions eudoxus/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[package]
name = "eudoxus"
description = "Abstraction of Euclidean spaces."
keywords = ["geometry", "graphics", "math"]
version = "0.0.1"
authors = ["Sean Olson <[email protected]>"]
edition = "2018"
license = "MIT"
readme = "../README.md"
repository = "https://github.com/olson-sean-k/theon"

[package.metadata.docs.rs]
default-target = "x86_64-unknown-linux-gnu"
# Enable all features so that trait implementations for types from commonly used
# crates are shown.
all-features = true
# Enable KaTex support.
rustdoc-args = [
"--html-in-header",
"doc/katex-header.html",
]

[features]
default = []
geometry-cgmath = ["cgmath"]
geometry-glam = ["glam"]
geometry-mint = ["mint"]
geometry-nalgebra = ["nalgebra"]
geometry-ultraviolet = ["ultraviolet"]

[dependencies]
num-traits = "^0.2.0"
typenum = "^1.13.0"

# Integrations.
cgmath = { version = "=0.18.0", optional = true }
glam = { version = "=0.17.1", optional = true }
mint = { version = "=0.5.6", optional = true }
nalgebra = { version = "=0.28.0", optional = true }
ultraviolet = { version = "=0.8.1", optional = true }

[dev-dependencies]
nalgebra = "^0.22.0"
183 changes: 183 additions & 0 deletions eudoxus/src/adjunct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//! Homogeneous structured data.
//!
//! This module provides traits that abstract over structured homogeneous data.
//! In particular, these traits are implemented by array-like types with some
//! number of structured homogeneous elements. Here, _structure_ refers to
//! layout and ordering. A type that implements these traits is known as an
//! _adjunct_ and must implement the most basic `Adjunct` trait.
//!
//! Adjunct traits mirror iterator operations but act on both bounded and
//! unbounded data. Eudoxus uses these traits to manipulate data structures that
//! describe geometric constructs, typically as a matrix of scalar values.
//! Adjuncts can be both simple `struct`s with well-defined fields and more
//! dynamic types like `Vec`s.
//!
//! Implementations for adjunct traits are provided for integrated foreign types
//! when enabling geometry features. For example, implementations of `Adjunct`
//! and other traits are provided for `nalgebra` types when the
//! `geometry-nalgebra` feature is enabled. See the `integration` module.
use num_traits::{One, Zero};
use std::ops::{Add, Mul};

use crate::space::{FiniteDimensional, ProjectiveDimensions};
use crate::Increment;

pub trait Adjunct: Sized {
type Item;
}

pub trait Linear: Adjunct {
fn get(&self, index: usize) -> Option<&Self::Item>;
}

pub trait Converged: Adjunct {
fn converged(value: Self::Item) -> Self;
}

pub trait Map<T = <Self as Adjunct>::Item>: Adjunct {
type Output: Adjunct<Item = T>;

fn map<F>(self, f: F) -> Self::Output
where
F: FnMut(Self::Item) -> T;
}

pub trait ZipMap<T = <Self as Adjunct>::Item>: Adjunct {
type Output: Adjunct<Item = T>;

fn zip_map<F>(self, other: Self, f: F) -> Self::Output
where
F: FnMut(Self::Item, Self::Item) -> T;

fn per_item_sum(self, other: Self) -> Self::Output
where
Self: Adjunct<Item = T>,
T: Add<Output = T>,
{
self.zip_map(other, |a, b| a + b)
}

fn per_item_product(self, other: Self) -> Self::Output
where
Self: Adjunct<Item = T>,
T: Mul<Output = T>,
{
self.zip_map(other, |a, b| a * b)
}
}

pub trait Fold: Adjunct {
fn fold<T, F>(self, seed: T, f: F) -> T
where
F: FnMut(T, Self::Item) -> T;

fn sum(self) -> Self::Item
where
Self::Item: Add<Output = Self::Item> + Zero,
{
self.fold(Zero::zero(), |sum, n| sum + n)
}

fn product(self) -> Self::Item
where
Self::Item: Mul<Output = Self::Item> + One,
{
self.fold(One::one(), |product, n| product * n)
}

fn any<F>(self, mut f: F) -> bool
where
F: FnMut(Self::Item) -> bool,
{
self.fold(false, |sum, item| {
if sum {
sum
}
else {
f(item)
}
})
}

fn all<F>(self, mut f: F) -> bool
where
F: FnMut(Self::Item) -> bool,
{
self.fold(true, |sum, item| {
if sum {
f(item)
}
else {
sum
}
})
}
}

// NOTE: This trait uses an input parameter `S` instead of an associated type
// `Output`, because some types can be reasonably truncated into various
// types. For example, the `glam` crate provides several vector
// representations, and its `Vec3` and `Vec3A` types both extend into and
// truncate from the `Vec4` type.
pub trait TruncateInto<S>: FiniteDimensional<N = ProjectiveDimensions<S>>
where
S: Adjunct<Item = Self::Item> + FiniteDimensional,
S::N: Increment,
{
fn truncate(self) -> (S, Self::Item);
}

// NOTE: This trait uses an input parameter `S` instead of an associated type
// `Output`, because some types can be reasonably extended into various
// types. For example, the `glam` crate provides several vector
// representations, and its `Vec3` and `Vec3A` types both extend into and
// truncate from the `Vec4` type.
pub trait ExtendInto<S>: FiniteDimensional
where
S: Adjunct<Item = Self::Item> + FiniteDimensional<N = ProjectiveDimensions<Self>>,
Self::N: Increment,
{
fn extend(self, item: Self::Item) -> S;
}

pub trait TryFromIterator<I>: Linear
where
I: Iterator<Item = Self::Item>,
{
type Error;
type Remainder: Iterator<Item = I::Item>;

fn try_from_iter(items: I) -> Result<(Self, Option<Self::Remainder>), Self::Error>;
}

pub trait IntoIterator: Linear {
type Output: Iterator<Item = Self::Item>;

fn into_iter(self) -> Self::Output;
}

pub trait IteratorExt: Iterator + Sized {
fn try_collect<T>(self) -> Result<(T, Option<T::Remainder>), T::Error>
where
T: TryFromIterator<Self, Item = Self::Item>,
{
T::try_from_iter(self)
}

// TODO: Move this into Theon.
fn try_collect_all<T>(self) -> Result<T, ()>
where
T: TryFromIterator<Self, Item = Self::Item>,
{
let (collection, remainder) = self.try_collect::<T>().map_err(|_| ())?;
if remainder.is_some() {
Err(())
}
else {
Ok(collection)
}
}
}

impl<I> IteratorExt for I where I: Iterator + Sized {}
Loading

0 comments on commit 8ad482c

Please sign in to comment.