pub struct PointND<T, const N: usize>(/* private fields */);Expand description
The whole point of the crate.
The PointND struct is really just a smart pointer to an array of type [T; N]
with convenience methods for accessing, setting and transforming values.
As the struct dereferences to a slice, all methods implemented for slices are available with this.
§Making a Point
There are three PointND constructors (in order of usefulness): from(), fill()
and from_slice().
The from_slice() and fill() functions can only be used if creating a point where the
items implement Copy
// Creating a 2D point from a given array
let arr = [0, 1];
let p = PointND::from(arr);
// Creating a 4D point with all values set to 5
let p: PointND<_, 4> = PointND::fill(5);
// Creating a 3D point from values of a given slice
let p: PointND<_, 3> = PointND::from_slice(&vec![0, 1, 2]);
// You can even construct a PointND with zero dimensions
let arr: [i32; 0] = [];
let p = PointND::from(arr);The generic arg N in PointND<T, N> is a usize constant generic and for the fill()
and from_slice() functions, specifying it is sometimes needed when the compiler cannot infer it itself.
See their documentation for cases when explicit generics are not necessary
Otherwise, if you don’t like writing PointND twice for type annotation, it is recommended to
use FQS (fully qualified syntax) instead:
let p1 = PointND::<_, 4>::from_slice(&vec![5,5,5,5]);
let p2 = PointND::<_, 4>::fill(5);
assert_eq!(p1, p2);§Getting Values
If the dimensions of the point are within 1..=4, it is recommended to
use the convenience getters x(), y(), z() and w()
The above all return references to the value, regardless of whether they implement Copy
let p = PointND::from([0,1]);
// As the point has 2 dimensions, we can access
// it's values with the x() and y() methods
let x: &i32 = p.x();
let y = p.y();
assert_eq!(*x, 0);
assert_eq!(*y, 1);
// If the point had 3 dimensions, we could use the above and:
// let z = p.z();
// Or with 4 dimensions, the above and:
// let w = p.w();The above methods are not implemented for PointND’s with more than 4 dimensions.
Instead, we must use the native implementations of the contained array. See the notes below on how direct indexing can be made easier.
let p = PointND::from([0,1,2,3,4,5]);
// ERROR: Not implemented for PointND of 6 dimensions
// let x = p.x();
let x = p[0];
let y = p.get(1);
let z_to_last = &p[2..];
assert_eq!(x, 0);
assert_eq!(*y.unwrap(), 1);
assert_eq!(z_to_last, &[2, 3, 4, 5]);To get all the values contained by a point, use the into_arr() method
let p = PointND::from([-10, -2, 0, 2, 10]);
assert_eq!(p.into_arr(), [-10, -2, 0, 2, 10])§Querying Size
The number of dimensions can be retrieved using the dims() method (short for dimensions).
let p = PointND::from([0,1]);
assert_eq!(p.dims(), 2);
// Alternatively, as PointND implements Deref, we can use len().
// It's not as descriptive however...
assert_eq!(p.len(), 2);§Transforming Values
If the dimensions of the point are within 1..=4, it is recommended to use the convenience
set and shift methods.
let mut p = PointND::from([0, 1]);
// As the point has 2 dimensions, we can set
// it's values with the set_x() and set_y() methods
// There are set_z() and set_w() methods available for
// points with 3 and 4 dimensions respectively
p.set_x(-10);
p.set_y(-20);
assert_eq!(*p.x(), -10);
assert_eq!(*p.y(), -20);
// We can AddAssign the values of a point with the
// shift methods
// Like set, there are methods available for points
// of 3 and 4 dimensions
p.shift_x(5);
p.shift_y(25);
assert_eq!(*p.x(), -5);
assert_eq!(*p.y(), 5);The above methods are not implemented for PointND’s with more than 4 dimensions.
Instead, we must use the native implementations of the contained array. See the notes below on how direct indexing can be made easier.
let mut p = PointND::from([0, 1, 2, 3, 4, 5]);
// Sets x via indexing
p[0] = -100;
assert_eq!(p[0], -100);§Appliers
The apply(), apply_vals(), apply_dims() and apply_point() methods all
consume self and return a new point after calling a function or closure on all contained values
Multiple applier methods can be chained together to make complex transformations to a PointND
This is probably best explained with an example:
// A trivial transformation more easily done other ways
// ...but it gets the point across
let p = PointND
::from([0,1,2]) // Creates a new PointND
.apply(|item| item + 2) // Adds 2 to each item
.apply(|item| item * 3); // Multiplies each item by 3
assert_eq!(p.into_arr(), [6, 9, 12]);Each applier has it’s own subtle differences, it is recommended to read the documentation for each of them
§Iterating
Iterating over a PointND is as easy as:
let mut p = PointND::from([0,1]);
for _ in p.iter() { /* Do stuff */ }
for _ in p.iter_mut() { /* Change stuff */ }
for _ in p.into_iter() { /* Move stuff (unless items implement Copy) */ }It must be noted that if the items implement Copy, using into_iter() will not actually
move the point out of scope.
If this behaviour is necessary, use the into_arr() method to consume the point and move the
contained array into the loop
for _ in p.into_arr().into_iter() { /* Move stuff */ }
// ERROR: Can't access moved value
// assert_eq!(p.dims(), 2);§Things (not strictly necessary) to Note
§Convenience Methods
As stated earlier, certain methods for accessing and setting the values contained by a PointND
are only implemented for points within 1..=4 dimensions.
This was done to mirror the behaviour of arrays closely as possible, where out of bounds indexing errors are caught at compile time.
§Direct Indexing
Indexing values can become cumbersome if using usize values. As of v0.5.0, point-nd’s indexing
macros have been moved to the axmac crate which provides macros to index the first
4 dimensions of a point by simply specifying x, y, z or w.
The axmac crate is highly recommended when working with points above 4 dimensions
§Math Operations
Unlike structures in other crates, PointND’s (as of v0.5.0) do not implement mutating
and consuming math operations like Neg, Add, SubAssign, etc.
It was decided that these functionalities and others could provided by independent crates via
functions which could be imported and passed to the apply methods.
Eq and PartialEq are implemented though.
§Dimensional Capacity
This crate relies heavily on the arrayvec crate when applying
transformations to points. Due to the fact that arrayvec::ArrayVec’s lengths are capped at
u32::MAX, any apply, extend and retain methods will panic if used on PointND’s with
dimensions exceeding that limit.
This shouldn’t be a problem in most use cases (who needs a u32::MAX + 1 dimensional point
anyway?), but it is probably worth mentioning.
Implementations§
Source§impl<T, const N: usize> PointND<T, N>where
T: Copy,
impl<T, const N: usize> PointND<T, N>where
T: Copy,
Sourcepub fn from_slice(slice: &[T]) -> Self
pub fn from_slice(slice: &[T]) -> Self
Returns a new PointND with values from the specified slice
If the compiler is not able to infer the dimensions (a.k.a - length) of the point, it needs to be explicitly specified
// Explicitly specifying dimensions
let p = PointND::<_, 3>::from_slice(&vec![0,1,2]);
// The generics don't always have to be specified though, for example
let p1 = PointND::from([0,1]); // Compiler knows this has 2 dimensions
let p2 = PointND::from_slice(&vec![2,3]);
// Later, p2 is applied to p1. The compiler is able to infer its dimensions
let p3 = p1.apply_point(p2, |a, b| a + b);If the length of the slice being passed is uncertain, it is recommended to use the try_from()
method for more graceful error handling.
§Panics
- If the slice passed cannot be converted into an array
let arr = [0,1,2];
// ERROR: Cannot convert slice of [i32; 3] to [i32; 100]
let p = PointND::<_, 100>::from_slice(&arr[..]);Sourcepub fn fill(value: T) -> Self
pub fn fill(value: T) -> Self
Returns a new PointND with all values set as specified
If the compiler is not able to infer the dimensions (a.k.a - length) of the point, it needs to be explicitly specified
See the from_slice() function for cases when generics don’t need to be explicitly specified
// A 10 dimensional point with all values set to 2
let p = PointND::<_, 10>::fill(2);
assert_eq!(p.dims(), 10);
assert_eq!(p.into_arr(), [2; 10]);Source§impl<T, const N: usize> PointND<T, N>
impl<T, const N: usize> PointND<T, N>
Sourcepub fn dims(&self) -> usize
pub fn dims(&self) -> usize
Returns the number of dimensions of the point (a 2D point will return 2, a 3D point 3, etc)
Equivalent to calling len()
Sourcepub fn apply<U>(self, modifier: ApplyFn<T, U>) -> PointND<U, N>
pub fn apply<U>(self, modifier: ApplyFn<T, U>) -> PointND<U, N>
Consumes self and calls the modifier on each item contained
by self to create a new PointND of the same length.
let p = PointND
::from([0,1,2]) // Creates a new PointND
.apply(|item| item + 2) // Adds 2 to each item
.apply(|item| item * 3); // Multiplies each item by 3
assert_eq!(p.into_arr(), [6, 9, 12]);The return type of the modifier does not necessarily have to be
the same as the type of the items passed to it. This means that apply
can create a new point with items of a different type, but the same length.
let p = PointND
::from([0,1,2]) // Creates a new PointND
.apply(|item| item as f32); // Converts items to float
assert_eq!(p.into_arr(), [0.0, 1.0, 2.0]);§Enabled by features:
-
default -
appliers
§Panics
- If the dimensions of
selfare greater thanu32::MAX.
Sourcepub fn apply_dims(self, dims: &[usize], modifier: ApplyDimsFn<T>) -> Self
pub fn apply_dims(self, dims: &[usize], modifier: ApplyDimsFn<T>) -> Self
Consumes self and calls the modifier on the items at the
specified dims to create a new PointND of the same length.
Any items at dimensions not specified will be passed to the new point without change
let p = PointND
::from([0,1,2,3,4]) // Creates a PointND
.apply_dims(&[1,3], |item| item * 2) // Multiplies items 1 and 3 by 2
.apply_dims(&[0,2], |item| item + 10); // Adds 10 to items 0 and 2
assert_eq!(p.into_arr(), [10, 2, 12, 6, 4]);Unlike some other apply methods, this apply_dims cannot return
a PointND with items of a different type from the original.
§Enabled by features:
-
default -
appliers
§Panics
- If the dimensions of
selfare greater thanu32::MAX.
Sourcepub fn apply_vals<U, V>(
self,
values: [V; N],
modifier: ApplyValsFn<T, U, V>,
) -> PointND<U, N>
pub fn apply_vals<U, V>( self, values: [V; N], modifier: ApplyValsFn<T, U, V>, ) -> PointND<U, N>
Consumes self and calls the modifier on each item contained by
self and values to create a new PointND of the same length.
As this method may modify every value in the original point,
the values array must be the same length as the point.
When creating a modifier function to be used by this method, keep
in mind that the items in self are passed to it through the
first arg, and the items in values through the second.
let p = PointND
::from([0,1,2]) // Creates a new PointND
.apply_vals([1,3,5], |a, b| a + b) // Adds items in point to items in array
.apply_vals([2,4,6], |a, b| a * b); // Multiplies items in point to items in array
assert_eq!(p.into_arr(), [2, 16, 42]);Neither the return type of the modifier nor the type of the items contained
by the values array necessarily have to be the same as the item type of the
original point. This means that apply_vals can create a new point with items
of a different type, but the same length.
enum Op {
Add,
Sub,
}
// Adds or subtracts 10 from 'a' depending on the
// operation specified in 'b', then converts to float
let add_or_sub = |a, b| {
match b {
Op::Add => (a + 10) as f32,
Op::Sub => (a - 10) as f32
}
};
let p = PointND
::from([0,1,2])
.apply_vals(
[Op::Add, Op::Sub, Op::Add],
add_or_sub
);
assert_eq!(p.into_arr(), [10.0, -9.0, 12.0]);§Enabled by features:
-
default -
appliers
§Panics
- If the dimensions of
selforvaluesare greater thanu32::MAX.
Sourcepub fn apply_point<U, V>(
self,
other: PointND<V, N>,
modifier: ApplyPointFn<T, U, V>,
) -> PointND<U, N>
pub fn apply_point<U, V>( self, other: PointND<V, N>, modifier: ApplyPointFn<T, U, V>, ) -> PointND<U, N>
Consumes self and calls the modifier on each item contained by
self and another PointND to create a new point of the same length.
When creating a modifier function to be used by this method, keep
in mind that the items in self are passed to it through the
first arg, and the items in other through the second.
let p1 = PointND::from([0,9,3,1]);
let p2 = PointND::fill(10);
let p3 = PointND
::from([1,2,3,4]) // Creates a new PointND
.apply_point(p1, |a, b| a - b) // Subtracts items in p3 with those in p1
.apply_point(p2, |a, b| a * b); // Multiplies items in p3 with those in p2
assert_eq!(p3.into_arr(), [10, -70, 0, 30]);Neither the return type of the modifier nor the type of the items
contained by the other point necessarily have to be the same as
the type of the items in the original point. This means that apply_point
can create a new point with items of a different type, but the same length.
§Enabled by features:
-
default -
appliers
§Panics
- If the dimensions of
selforotherare greater thanu32::MAX.
Source§impl<T> PointND<T, 1>
Methods for safely getting and setting the value contained by a 1D PointND
impl<T> PointND<T, 1>
Methods for safely getting and setting the value contained by a 1D PointND
§Enabled by features:
-
default -
conv_methods -
x
Source§impl<T> PointND<T, 2>
Methods for safely getting and setting the values contained by a 2D PointND
impl<T> PointND<T, 2>
Methods for safely getting and setting the values contained by a 2D PointND
§Enabled by features:
-
default -
conv_methods -
y
Source§impl<T> PointND<T, 3>
Methods for safely getting and setting the values contained by a 3D PointND
impl<T> PointND<T, 3>
Methods for safely getting and setting the values contained by a 3D PointND
§Enabled by features:
-
default -
conv_methods -
z
Source§impl<T> PointND<T, 4>
Methods for safely getting and setting the values contained by a 4D PointND
impl<T> PointND<T, 4>
Methods for safely getting and setting the values contained by a 4D PointND
§Enabled by features:
-
default -
conv_methods -
w
Source§impl<T> PointND<T, 1>where
T: AddAssign,
Method for safely transforming the value contained by a 1D PointND
impl<T> PointND<T, 1>where
T: AddAssign,
Method for safely transforming the value contained by a 1D PointND
§Enabled by features:
-
default -
conv_methods -
x
Source§impl<T> PointND<T, 2>where
T: AddAssign,
Methods for safely transforming the values contained by a 2D PointND
impl<T> PointND<T, 2>where
T: AddAssign,
Methods for safely transforming the values contained by a 2D PointND
§Enabled by features:
-
default -
conv_methods -
y
Source§impl<T> PointND<T, 3>where
T: AddAssign,
Methods for safely transforming the values contained by a 3D PointND
impl<T> PointND<T, 3>where
T: AddAssign,
Methods for safely transforming the values contained by a 3D PointND
§Enabled by features:
-
default -
conv_methods -
z
Methods from Deref<Target = [T; N]>§
1.57.0 · Sourcepub fn as_slice(&self) -> &[T]
pub fn as_slice(&self) -> &[T]
Returns a slice containing the entire array. Equivalent to &s[..].
1.57.0 · Sourcepub fn as_mut_slice(&mut self) -> &mut [T]
pub fn as_mut_slice(&mut self) -> &mut [T]
Returns a mutable slice containing the entire array. Equivalent to
&mut s[..].
1.77.0 · Sourcepub fn each_ref(&self) -> [&T; N]
pub fn each_ref(&self) -> [&T; N]
Borrows each element and returns an array of references with the same
size as self.
§Example
let floats = [3.1, 2.7, -1.0];
let float_refs: [&f64; 3] = floats.each_ref();
assert_eq!(float_refs, [&3.1, &2.7, &-1.0]);This method is particularly useful if combined with other methods, like
map. This way, you can avoid moving the original
array if its elements are not Copy.
let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()];
let is_ascii = strings.each_ref().map(|s| s.is_ascii());
assert_eq!(is_ascii, [true, false, true]);
// We can still access the original array: it has not been moved.
assert_eq!(strings.len(), 3);1.77.0 · Sourcepub fn each_mut(&mut self) -> [&mut T; N]
pub fn each_mut(&mut self) -> [&mut T; N]
Borrows each element mutably and returns an array of mutable references
with the same size as self.
§Example
let mut floats = [3.1, 2.7, -1.0];
let float_refs: [&mut f64; 3] = floats.each_mut();
*float_refs[0] = 0.0;
assert_eq!(float_refs, [&mut 0.0, &mut 2.7, &mut -1.0]);
assert_eq!(floats, [0.0, 2.7, -1.0]);Sourcepub fn split_array_ref<const M: usize>(&self) -> (&[T; M], &[T])
🔬This is a nightly-only experimental API. (split_array)
pub fn split_array_ref<const M: usize>(&self) -> (&[T; M], &[T])
split_array)Divides one array reference into two at an index.
The first will contain all indices from [0, M) (excluding
the index M itself) and the second will contain all
indices from [M, N) (excluding the index N itself).
§Panics
Panics if M > N.
§Examples
#![feature(split_array)]
let v = [1, 2, 3, 4, 5, 6];
{
let (left, right) = v.split_array_ref::<0>();
assert_eq!(left, &[]);
assert_eq!(right, &[1, 2, 3, 4, 5, 6]);
}
{
let (left, right) = v.split_array_ref::<2>();
assert_eq!(left, &[1, 2]);
assert_eq!(right, &[3, 4, 5, 6]);
}
{
let (left, right) = v.split_array_ref::<6>();
assert_eq!(left, &[1, 2, 3, 4, 5, 6]);
assert_eq!(right, &[]);
}Sourcepub fn split_array_mut<const M: usize>(&mut self) -> (&mut [T; M], &mut [T])
🔬This is a nightly-only experimental API. (split_array)
pub fn split_array_mut<const M: usize>(&mut self) -> (&mut [T; M], &mut [T])
split_array)Divides one mutable array reference into two at an index.
The first will contain all indices from [0, M) (excluding
the index M itself) and the second will contain all
indices from [M, N) (excluding the index N itself).
§Panics
Panics if M > N.
§Examples
#![feature(split_array)]
let mut v = [1, 0, 3, 0, 5, 6];
let (left, right) = v.split_array_mut::<2>();
assert_eq!(left, &mut [1, 0][..]);
assert_eq!(right, &mut [3, 0, 5, 6]);
left[1] = 2;
right[1] = 4;
assert_eq!(v, [1, 2, 3, 4, 5, 6]);Sourcepub fn rsplit_array_ref<const M: usize>(&self) -> (&[T], &[T; M])
🔬This is a nightly-only experimental API. (split_array)
pub fn rsplit_array_ref<const M: usize>(&self) -> (&[T], &[T; M])
split_array)Divides one array reference into two at an index from the end.
The first will contain all indices from [0, N - M) (excluding
the index N - M itself) and the second will contain all
indices from [N - M, N) (excluding the index N itself).
§Panics
Panics if M > N.
§Examples
#![feature(split_array)]
let v = [1, 2, 3, 4, 5, 6];
{
let (left, right) = v.rsplit_array_ref::<0>();
assert_eq!(left, &[1, 2, 3, 4, 5, 6]);
assert_eq!(right, &[]);
}
{
let (left, right) = v.rsplit_array_ref::<2>();
assert_eq!(left, &[1, 2, 3, 4]);
assert_eq!(right, &[5, 6]);
}
{
let (left, right) = v.rsplit_array_ref::<6>();
assert_eq!(left, &[]);
assert_eq!(right, &[1, 2, 3, 4, 5, 6]);
}Sourcepub fn rsplit_array_mut<const M: usize>(&mut self) -> (&mut [T], &mut [T; M])
🔬This is a nightly-only experimental API. (split_array)
pub fn rsplit_array_mut<const M: usize>(&mut self) -> (&mut [T], &mut [T; M])
split_array)Divides one mutable array reference into two at an index from the end.
The first will contain all indices from [0, N - M) (excluding
the index N - M itself) and the second will contain all
indices from [N - M, N) (excluding the index N itself).
§Panics
Panics if M > N.
§Examples
#![feature(split_array)]
let mut v = [1, 0, 3, 0, 5, 6];
let (left, right) = v.rsplit_array_mut::<4>();
assert_eq!(left, &mut [1, 0]);
assert_eq!(right, &mut [3, 0, 5, 6][..]);
left[1] = 2;
right[1] = 4;
assert_eq!(v, [1, 2, 3, 4, 5, 6]);