447 lines
12 KiB
Rust
447 lines
12 KiB
Rust
use std::cmp::Ordering;
|
|
use std::collections::HashSet;
|
|
use std::fmt;
|
|
use std::fmt::Display;
|
|
use std::fmt::Formatter;
|
|
use std::iter::FromIterator;
|
|
use std::ops::Add;
|
|
use std::ops::AddAssign;
|
|
use std::ops::Index;
|
|
use std::ops::IndexMut;
|
|
use std::ops::Mul;
|
|
use std::ops::MulAssign;
|
|
use std::ops::Sub;
|
|
use std::ops::SubAssign;
|
|
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
use super::coordinate::Coordinate;
|
|
|
|
/// Store a position as efficiently as possible in terms of space.
|
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
|
pub enum Position {
|
|
/// 1 dimension positions.
|
|
Position1(Coordinate),
|
|
/// 2 dimensions positions.
|
|
Position2([Coordinate; 2]),
|
|
/// 3 dimensions positions.
|
|
Position3([Coordinate; 3]),
|
|
/// 4 dimensions positions.
|
|
Position4([Coordinate; 4]),
|
|
/// 5 dimensions positions.
|
|
Position5([Coordinate; 5]),
|
|
/// 6 dimensions positions.
|
|
Position6([Coordinate; 6]),
|
|
/// 7 dimensions positions.
|
|
Position7([Coordinate; 7]),
|
|
/// 8 dimensions positions.
|
|
Position8([Coordinate; 8]),
|
|
/// N dimensions positions.
|
|
PositionN(Vec<Coordinate>),
|
|
}
|
|
|
|
impl Position {
|
|
/// Returns the number of dimensions or size of the vector.
|
|
pub fn dimensions(&self) -> usize {
|
|
match self {
|
|
Position::Position1(_) => 1,
|
|
Position::Position2(_) => 2,
|
|
Position::Position3(_) => 3,
|
|
Position::Position4(_) => 4,
|
|
Position::Position5(_) => 5,
|
|
Position::Position6(_) => 6,
|
|
Position::Position7(_) => 7,
|
|
Position::Position8(_) => 8,
|
|
Position::PositionN(coordinates) => coordinates.len(),
|
|
}
|
|
}
|
|
|
|
/// Compute `||self||`.
|
|
pub fn norm(&self) -> f64 {
|
|
if let Position::Position1(coordinates) = self {
|
|
// the square root of a single number to the square is its positive value, so ensure it is.
|
|
coordinates.f64().abs()
|
|
} else {
|
|
let point: Vec<&Coordinate> = self.into();
|
|
let mut squared = 0f64;
|
|
|
|
for c in point {
|
|
let t: f64 = c.into();
|
|
squared += t * t;
|
|
}
|
|
|
|
squared.sqrt()
|
|
}
|
|
}
|
|
|
|
/// Compute the unit vector pointing in the same direction as `self`.
|
|
pub fn unit(&self) -> Self {
|
|
self * (1f64 / self.norm())
|
|
}
|
|
|
|
/// Multiplies `self` with `rhs`, producing a scalar value.
|
|
///
|
|
/// `self • rhs = product`
|
|
///
|
|
/// **Note:** The two vector sizes must be equal, a.k.a the two
|
|
/// vectors must have the same number of dimensions.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// `rhs`:
|
|
/// The right-hand side vector.
|
|
pub fn dot_product(&self, rhs: &Self) -> f64 {
|
|
assert_eq!(self.dimensions(), rhs.dimensions());
|
|
|
|
let mut product = 0f64;
|
|
|
|
for k in 0..self.dimensions() {
|
|
product += (self[k] * rhs[k]).f64();
|
|
}
|
|
|
|
product
|
|
}
|
|
|
|
/// Remove bits of precision.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `scale`:
|
|
/// Number of bits of precision to remove from each coordinates.
|
|
pub fn reduce_precision(&self, scale: u32) -> Self {
|
|
let mut position = Vec::with_capacity(self.dimensions());
|
|
|
|
for i in 0..self.dimensions() {
|
|
position.push(self[i].u64() >> scale)
|
|
}
|
|
|
|
position.into()
|
|
}
|
|
}
|
|
|
|
impl Display for Position {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
let v: Vec<&Coordinate> = self.into();
|
|
write!(f, "{:?}", v)
|
|
}
|
|
}
|
|
|
|
impl Ord for Position {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
self.partial_cmp(other).unwrap()
|
|
}
|
|
}
|
|
impl PartialOrd for Position {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
// Let's restrict for now to same-length vectors.
|
|
if self.dimensions() != other.dimensions() {
|
|
return None;
|
|
}
|
|
|
|
let mut ordering = HashSet::with_capacity(self.dimensions());
|
|
for k in 0..self.dimensions() {
|
|
ordering.insert(self[k].partial_cmp(&other[k]));
|
|
}
|
|
|
|
if ordering.contains(&None) {
|
|
return None;
|
|
}
|
|
|
|
let ordering = ordering.drain().flatten().collect::<Vec<_>>();
|
|
match ordering.len() {
|
|
3 => None,
|
|
2 => {
|
|
// The two values are, by construction different, which means we
|
|
// have the following possibilities as there are only GREATER,
|
|
// EQUAL and LESS in the enum:
|
|
// - LESS, GREATER
|
|
// - LESS, EQUAL
|
|
// - GREATER, EQUAL
|
|
// If one of the values is EQUAL, then the ordering will be the
|
|
// other value.
|
|
if ordering[0] == Ordering::Equal {
|
|
Some(ordering[1])
|
|
} else if ordering[1] == Ordering::Equal {
|
|
Some(ordering[0])
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
1 => Some(ordering[0]),
|
|
// We can not have more than 3 elements, and if we have 0, it means
|
|
// we had only None in the list
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Index<usize> for Position {
|
|
type Output = Coordinate;
|
|
|
|
fn index(&self, k: usize) -> &Self::Output {
|
|
match self {
|
|
Position::Position1(coordinate) => {
|
|
assert_eq!(k, 0);
|
|
coordinate
|
|
}
|
|
Position::Position2(coordinates) => &coordinates[k],
|
|
Position::Position3(coordinates) => &coordinates[k],
|
|
Position::Position4(coordinates) => &coordinates[k],
|
|
Position::Position5(coordinates) => &coordinates[k],
|
|
Position::Position6(coordinates) => &coordinates[k],
|
|
Position::Position7(coordinates) => &coordinates[k],
|
|
Position::Position8(coordinates) => &coordinates[k],
|
|
Position::PositionN(coordinates) => &coordinates[k],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IndexMut<usize> for Position {
|
|
fn index_mut(&mut self, k: usize) -> &mut Self::Output {
|
|
match self {
|
|
Position::Position1(coordinate) => {
|
|
assert_eq!(k, 0);
|
|
coordinate
|
|
}
|
|
Position::Position2(coordinates) => &mut coordinates[k],
|
|
Position::Position3(coordinates) => &mut coordinates[k],
|
|
Position::Position4(coordinates) => &mut coordinates[k],
|
|
Position::Position5(coordinates) => &mut coordinates[k],
|
|
Position::Position6(coordinates) => &mut coordinates[k],
|
|
Position::Position7(coordinates) => &mut coordinates[k],
|
|
Position::Position8(coordinates) => &mut coordinates[k],
|
|
Position::PositionN(coordinates) => &mut coordinates[k],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Add for Position {
|
|
type Output = Position;
|
|
|
|
fn add(mut self, rhs: Self) -> Self::Output {
|
|
self += rhs;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Add for &Position {
|
|
type Output = Position;
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
let dimensions = self.dimensions();
|
|
assert_eq!(dimensions, rhs.dimensions());
|
|
let mut v = Vec::with_capacity(dimensions);
|
|
|
|
for k in 0..dimensions {
|
|
v.push(self[k] + rhs[k]);
|
|
}
|
|
|
|
v.into()
|
|
}
|
|
}
|
|
|
|
impl AddAssign for Position {
|
|
fn add_assign(&mut self, rhs: Self) {
|
|
let dimensions = self.dimensions();
|
|
assert_eq!(dimensions, rhs.dimensions());
|
|
|
|
for k in 0..dimensions {
|
|
self[k] = self[k] + rhs[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Sub for Position {
|
|
type Output = Position;
|
|
|
|
fn sub(mut self, rhs: Self) -> Self::Output {
|
|
self -= rhs;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Sub for &Position {
|
|
type Output = Position;
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
let dimensions = self.dimensions();
|
|
assert_eq!(dimensions, rhs.dimensions());
|
|
let mut v = Vec::with_capacity(dimensions);
|
|
|
|
for k in 0..dimensions {
|
|
v.push(self[k] - rhs[k]);
|
|
}
|
|
|
|
v.into()
|
|
}
|
|
}
|
|
|
|
impl SubAssign for Position {
|
|
fn sub_assign(&mut self, rhs: Self) {
|
|
let dimensions = self.dimensions();
|
|
assert_eq!(dimensions, rhs.dimensions());
|
|
|
|
for k in 0..dimensions {
|
|
self[k] = self[k] - rhs[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Scalar product
|
|
impl Mul<f64> for Position {
|
|
type Output = Position;
|
|
|
|
fn mul(mut self, rhs: f64) -> Self::Output {
|
|
self *= rhs;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Mul<f64> for &Position {
|
|
type Output = Position;
|
|
|
|
fn mul(self, rhs: f64) -> Self::Output {
|
|
let dimensions = self.dimensions();
|
|
let mut v = Vec::with_capacity(dimensions);
|
|
|
|
for k in 0..dimensions {
|
|
v.push(self[k] * rhs);
|
|
}
|
|
|
|
v.into()
|
|
}
|
|
}
|
|
|
|
// Scalar product
|
|
impl MulAssign<f64> for Position {
|
|
fn mul_assign(&mut self, rhs: f64) {
|
|
for k in 0..self.dimensions() {
|
|
self[k] = self[k] * rhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Outer product
|
|
impl Mul for Position {
|
|
type Output = Vec<Position>;
|
|
|
|
fn mul(self, rhs: Self) -> Self::Output {
|
|
let mut m = Vec::with_capacity(rhs.dimensions());
|
|
|
|
for i in 0..rhs.dimensions() {
|
|
let mut u = Vec::with_capacity(self.dimensions());
|
|
|
|
for k in 0..self.dimensions() {
|
|
u[k] = self[k] * rhs[i];
|
|
}
|
|
m[i] = u.into();
|
|
}
|
|
|
|
m
|
|
}
|
|
}
|
|
|
|
// FIXME: Which is faster, the code below or the automatically generated
|
|
// implementation?
|
|
/*
|
|
impl PartialEq for Position {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
for i in 0..self.dimensions() {
|
|
if self[i] != other[i] {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
*/
|
|
|
|
impl<'s> From<&'s Position> for Vec<&'s Coordinate> {
|
|
fn from(position: &'s Position) -> Self {
|
|
match position {
|
|
Position::Position1(coordinate) => vec![coordinate],
|
|
Position::Position2(coordinates) => coordinates.iter().collect(),
|
|
Position::Position3(coordinates) => coordinates.iter().collect(),
|
|
Position::Position4(coordinates) => coordinates.iter().collect(),
|
|
Position::Position5(coordinates) => coordinates.iter().collect(),
|
|
Position::Position6(coordinates) => coordinates.iter().collect(),
|
|
Position::Position7(coordinates) => coordinates.iter().collect(),
|
|
Position::Position8(coordinates) => coordinates.iter().collect(),
|
|
Position::PositionN(coordinates) => coordinates.iter().collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Vec<Coordinate>> for Position {
|
|
fn from(coordinates: Vec<Coordinate>) -> Self {
|
|
match coordinates.len() {
|
|
1 => Position::Position1(coordinates[0]),
|
|
2 => Position::Position2(*array_ref!(coordinates, 0, 2)),
|
|
3 => Position::Position3(*array_ref!(coordinates, 0, 3)),
|
|
4 => Position::Position4(*array_ref!(coordinates, 0, 4)),
|
|
5 => Position::Position5(*array_ref!(coordinates, 0, 5)),
|
|
6 => Position::Position6(*array_ref!(coordinates, 0, 6)),
|
|
7 => Position::Position7(*array_ref!(coordinates, 0, 7)),
|
|
8 => Position::Position8(*array_ref!(coordinates, 0, 8)),
|
|
_ => Position::PositionN(coordinates),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Vec<f64>> for Position {
|
|
fn from(coordinates: Vec<f64>) -> Self {
|
|
coordinates
|
|
.into_iter()
|
|
.map(|c| c.into())
|
|
.collect::<Vec<Coordinate>>()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl From<&Vec<f64>> for Position {
|
|
fn from(coordinates: &Vec<f64>) -> Self {
|
|
coordinates
|
|
.iter()
|
|
.map(|c| (*c).into())
|
|
.collect::<Vec<Coordinate>>()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl From<Vec<u64>> for Position {
|
|
fn from(coordinates: Vec<u64>) -> Self {
|
|
coordinates
|
|
.into_iter()
|
|
.map(|c| c.into())
|
|
.collect::<Vec<Coordinate>>()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl From<Position> for Vec<f64> {
|
|
fn from(position: Position) -> Self {
|
|
(&position).into()
|
|
}
|
|
}
|
|
|
|
impl From<&Position> for Vec<f64> {
|
|
fn from(position: &Position) -> Self {
|
|
let point: Vec<&Coordinate> = position.into();
|
|
|
|
point.into_iter().map(|c| c.into()).collect()
|
|
}
|
|
}
|
|
|
|
impl FromIterator<f64> for Position {
|
|
fn from_iter<I: IntoIterator<Item = f64>>(iter: I) -> Self {
|
|
iter.into_iter().collect::<Vec<_>>().into()
|
|
}
|
|
}
|
|
|
|
impl FromIterator<Coordinate> for Position {
|
|
fn from_iter<I: IntoIterator<Item = Coordinate>>(iter: I) -> Self {
|
|
iter.into_iter().collect::<Vec<_>>().into()
|
|
}
|
|
}
|