point_nd/
point.rs

1use core::convert::TryFrom;
2use core::array::TryFromSliceError;
3use core::ops::{Deref, DerefMut};
4
5#[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
6use core::ops::AddAssign;
7
8#[cfg(any(feature = "appliers", feature = "var-dims"))]
9use arrayvec::ArrayVec;
10#[cfg(any(feature = "appliers", feature = "var-dims"))]
11use crate::utils::ARRVEC_CAP;
12#[cfg(any(feature = "appliers", feature = "var-dims"))]
13use crate::utils::arrvec_into_inner;
14
15#[cfg(feature = "appliers")]
16use crate::utils::{ApplyFn, ApplyDimsFn, ApplyValsFn, ApplyPointFn};
17
18
19// Note to Developers:
20// - The docs have been written with the assumption that default features have been enabled
21// - Running doctests without default features will result in test failures
22// - Sorry about the mixed doc comment styles... we are slowly refactoring the /** block comments **/
23//   to /// triple slashes and the ```triple_code_blocks``` to `singles`
24
25
26/**
27The whole _point_ of the crate.
28
29The `PointND` struct is really just a smart pointer to an array of type `[T; N]`
30with convenience methods for accessing, setting and transforming values.
31
32As the struct dereferences to a slice, all methods implemented for slices are available with this.
33
34# Making a Point
35
36There are three `PointND` constructors (in order of usefulness): `from()`, `fill()`
37and `from_slice()`.
38
39The `from_slice()` and `fill()` functions can only be used if creating a point where the
40items implement `Copy`
41
42```
43# use point_nd::PointND;
44// Creating a 2D point from a given array
45let arr = [0, 1];
46let p = PointND::from(arr);
47
48// Creating a 4D point with all values set to 5
49let p: PointND<_, 4> = PointND::fill(5);
50
51// Creating a 3D point from values of a given slice
52let p: PointND<_, 3> = PointND::from_slice(&vec![0, 1, 2]);
53
54// You can even construct a PointND with zero dimensions
55let arr: [i32; 0] = [];
56let p = PointND::from(arr);
57```
58
59The generic arg ```N``` in ```PointND<T, N>``` is a ```usize``` constant generic and for the ```fill()```
60and ```from_slice()``` functions, specifying it is sometimes needed when the compiler cannot infer it itself.
61
62See their documentation for cases when explicit generics are not necessary
63
64Otherwise, if you don't like writing `PointND` twice for type annotation, it is recommended to
65use FQS (_fully qualified syntax_) instead:
66
67```
68# use point_nd::PointND;
69let p1 = PointND::<_, 4>::from_slice(&vec![5,5,5,5]);
70let p2 = PointND::<_, 4>::fill(5);
71
72assert_eq!(p1, p2);
73```
74
75# Getting Values
76
77If the dimensions of the point are within **1..=4**, it is recommended to
78use the convenience getters `x()`, `y()`, `z()` and `w()`
79
80The above all return references to the value, regardless of whether they implement `Copy`
81
82```
83# use point_nd::PointND;
84let p = PointND::from([0,1]);
85
86// As the point has 2 dimensions, we can access
87//  it's values with the x() and y() methods
88let x: &i32 = p.x();
89let y = p.y();
90assert_eq!(*x, 0);
91assert_eq!(*y, 1);
92
93// If the point had 3 dimensions, we could use the above and:
94//  let z = p.z();
95// Or with 4 dimensions, the above and:
96//  let w = p.w();
97```
98
99The above methods are not implemented for `PointND`'s with more than 4 dimensions.
100
101Instead, we must use the native implementations of the contained array. See the [notes][notes-indexing]
102below on how direct indexing can be made easier.
103
104```
105# use point_nd::PointND;
106let p = PointND::from([0,1,2,3,4,5]);
107
108// ERROR: Not implemented for PointND of 6 dimensions
109//  let x = p.x();
110
111let x = p[0];
112let y = p.get(1);
113let z_to_last = &p[2..];
114
115assert_eq!(x, 0);
116assert_eq!(*y.unwrap(), 1);
117assert_eq!(z_to_last, &[2, 3, 4, 5]);
118```
119
120To get **all** the values contained by a point, use the `into_arr()` method
121
122```
123# use point_nd::PointND;
124let p = PointND::from([-10, -2, 0, 2, 10]);
125assert_eq!(p.into_arr(), [-10, -2, 0, 2, 10])
126```
127
128# Querying Size
129
130The number of dimensions can be retrieved using the `dims()` method (short for _dimensions_).
131
132```
133# use point_nd::PointND;
134let p = PointND::from([0,1]);
135assert_eq!(p.dims(), 2);
136
137// Alternatively, as PointND implements Deref, we can use len().
138// It's not as descriptive however...
139assert_eq!(p.len(), 2);
140```
141
142# Transforming Values
143
144If the dimensions of the point are within **1..=4**, it is recommended to use the convenience
145`set` and `shift` methods.
146
147```
148# use point_nd::PointND;
149let mut p = PointND::from([0, 1]);
150
151// As the point has 2 dimensions, we can set
152//  it's values with the set_x() and set_y() methods
153// There are set_z() and set_w() methods available for
154//  points with 3 and 4 dimensions respectively
155p.set_x(-10);
156p.set_y(-20);
157assert_eq!(*p.x(), -10);
158assert_eq!(*p.y(), -20);
159
160// We can AddAssign the values of a point with the
161//  shift methods
162// Like set, there are methods available for points
163//  of 3 and 4 dimensions
164p.shift_x(5);
165p.shift_y(25);
166assert_eq!(*p.x(), -5);
167assert_eq!(*p.y(), 5);
168```
169
170The above methods are not implemented for `PointND`'s with more than 4 dimensions.
171
172Instead, we must use the native implementations of the contained array. See the [notes][notes-indexing]
173below on how direct indexing can be made easier.
174
175```
176# use point_nd::PointND;
177let mut p = PointND::from([0, 1, 2, 3, 4, 5]);
178
179// Sets x via indexing
180p[0] = -100;
181assert_eq!(p[0], -100);
182```
183
184### Appliers
185
186The ```apply()```, ```apply_vals()```, ```apply_dims()``` and ```apply_point()``` methods all
187consume self and return a new point after calling a function or closure on all contained values
188
189Multiple applier methods can be chained together to make complex transformations to a `PointND`
190
191This is probably best explained with an example:
192
193```
194# use point_nd::PointND;
195// A trivial transformation more easily done other ways
196//  ...but it gets the point across
197let p = PointND
198    ::from([0,1,2])            // Creates a new PointND
199    .apply(|item| item + 2)    // Adds 2 to each item
200    .apply(|item| item * 3);   // Multiplies each item by 3
201assert_eq!(p.into_arr(), [6, 9, 12]);
202```
203
204Each applier has it's own subtle differences, it is recommended to read the documentation for
205each of them
206
207# Iterating
208
209Iterating over a `PointND` is as easy as:
210
211```
212# use point_nd::PointND;
213let mut p = PointND::from([0,1]);
214
215for _ in p.iter()      { /* Do stuff     */ }
216for _ in p.iter_mut()  { /* Change stuff */ }
217for _ in p.into_iter() { /* Move stuff (unless items implement Copy) */ }
218```
219
220It must be noted that if the items implement `Copy`, using `into_iter()` will not actually
221move the point out of scope.
222
223If this behaviour is necessary, use the `into_arr()` method to consume the point and move the
224contained array into the loop
225
226```
227# use point_nd::PointND;
228# let mut p = PointND::from([0,1]);
229for _ in p.into_arr().into_iter() { /* Move stuff */ }
230
231// ERROR: Can't access moved value
232// assert_eq!(p.dims(), 2);
233```
234
235# Things (not strictly necessary) to Note
236
237### Convenience Methods
238
239As stated earlier, certain methods for accessing and setting the values contained by a `PointND`
240are only implemented for points within **1..=4** dimensions.
241
242This was done to mirror the behaviour of arrays closely as possible, where out of bounds indexing
243errors are caught at compile time.
244
245### Direct Indexing
246
247Indexing values can become cumbersome if using `usize` values. As of `v0.5.0`, `point-nd`'s indexing
248macros have been moved to the [`axmac`][axmac] crate which provides macros to index the first
2494 dimensions of a point by simply specifying _x_, _y_, _z_ or _w_.
250
251The `axmac` crate is **highly recommended** when working with points above 4 dimensions
252
253### Math Operations
254
255Unlike structures in other crates, `PointND`'s (as of `v0.5.0`) do not implement mutating
256and consuming math operations like `Neg`, `Add`, `SubAssign`, _etc_.
257
258It was decided that these functionalities and others could provided by independent crates via
259functions which could be imported and passed to the `apply` methods.
260
261`Eq` and `PartialEq` are implemented though.
262
263### Dimensional Capacity
264
265This crate relies heavily on the [`arrayvec`][arrayvec] crate when applying
266transformations to points. Due to the fact that `arrayvec::ArrayVec`'s lengths are capped at
267`u32::MAX`, any `apply`, `extend` and `retain` methods will panic if used on `PointND`'s with
268dimensions exceeding that limit.
269
270This shouldn't be a problem in most use cases (who needs a `u32::MAX + 1` dimensional point
271anyway?), but it is probably worth mentioning.
272
273 [axmac]: https://crates.io/crates/axmac
274 [arrayvec]: https://crates.io/crates/arrayvec
275
276 [notes]: https://docs.rs/point-nd/0.5.0/point_nd/struct.PointND.html#things-not-strictly-necessary-to-note
277 [notes-indexing]: https://docs.rs/point-nd/0.5.0/point_nd/struct.PointND.html#direct-indexing
278 */
279#[derive(Clone, Debug, Eq, PartialEq)]
280pub struct PointND<T, const N: usize>([T; N]);
281
282// From and Fill
283impl<T, const N: usize> PointND<T, N>
284    where T: Copy {
285
286    /**
287     Returns a new `PointND` with values from the specified slice
288
289     If the compiler is not able to infer the dimensions (a.k.a - length)
290     of the point, it needs to be explicitly specified
291
292     ```
293     # use point_nd::PointND;
294     // Explicitly specifying dimensions
295     let p = PointND::<_, 3>::from_slice(&vec![0,1,2]);
296
297     // The generics don't always have to be specified though, for example
298     let p1 = PointND::from([0,1]);       // Compiler knows this has 2 dimensions
299     let p2 = PointND::from_slice(&vec![2,3]);
300
301     // Later, p2 is applied to p1. The compiler is able to infer its dimensions
302     let p3 = p1.apply_point(p2, |a, b| a + b);
303     ```
304
305     If the length of the slice being passed is uncertain, it is recommended to use the `try_from()`
306     method for more graceful error handling.
307
308     # Panics
309
310     - If the slice passed cannot be converted into an array
311
312    ```should_panic
313    # use point_nd::PointND;
314    let arr = [0,1,2];
315    // ERROR: Cannot convert slice of [i32; 3] to [i32; 100]
316    let p = PointND::<_, 100>::from_slice(&arr[..]);
317    ```
318     */
319    pub fn from_slice(slice: &[T]) -> Self {
320        let arr: [T; N] = slice.try_into().unwrap();
321        PointND::from(arr)
322    }
323
324    ///
325    /// Returns a new `PointND` with all values set as specified
326    ///
327    /// If the compiler is not able to infer the dimensions (a.k.a - length)
328    /// of the point, it needs to be explicitly specified
329    ///
330    /// See the ```from_slice()``` function for cases when generics don't need to be explicitly specified
331    ///
332    /// ```
333    /// # use point_nd::PointND;
334    /// // A 10 dimensional point with all values set to 2
335    /// let p = PointND::<_, 10>::fill(2);
336    ///
337    /// assert_eq!(p.dims(), 10);
338    /// assert_eq!(p.into_arr(), [2; 10]);
339    /// ```
340    ///
341    pub fn fill(value: T) -> Self {
342        PointND::from([value; N])
343    }
344
345}
346
347impl<T, const N: usize> PointND<T, N> {
348
349    ///
350    /// Returns the number of dimensions of the point (a 2D point will return 2, a 3D point 3, _etc_)
351    ///
352    /// Equivalent to calling ```len()```
353    ///
354    pub fn dims(&self) -> usize {
355        self.0.len()
356    }
357
358    /// Consumes `self`, returning the contained array
359    pub fn into_arr(self) -> [T; N] {
360        self.0
361    }
362
363
364    ///
365    /// Panics with customised error message if specified `cap` is greater than the max `ArrayVec` capacity (`u32::MAX`)
366    ///
367    #[cfg(any(feature = "appliers", feature = "var-dims"))]
368    fn _check_arrvec_cap(&self, cap: usize, method_name: &str) {
369        if cap > ARRVEC_CAP {
370            panic!("Attempted to call {}() on PointND with more than u32::MAX dimensions",  method_name);
371        }
372    }
373
374
375    ///
376    /// Consumes `self` and calls the `modifier` on each item contained
377    /// by `self` to create a new `PointND` of the same length.
378    ///
379    /// ```
380    /// # use point_nd::PointND;
381    /// let p = PointND
382    ///     ::from([0,1,2])             // Creates a new PointND
383    ///     .apply(|item| item + 2)     // Adds 2 to each item
384    ///     .apply(|item| item * 3);    // Multiplies each item by 3
385    /// assert_eq!(p.into_arr(), [6, 9, 12]);
386    /// ```
387    ///
388    /// The return type of the `modifier` does not necessarily have to be
389    /// the same as the type of the items passed to it. This means that ```apply```
390    /// can create a new point with items of a different type, but the same length.
391    ///
392    /// ```
393    /// # use point_nd::PointND;
394    /// let p = PointND
395    ///     ::from([0,1,2])                // Creates a new PointND
396    ///     .apply(|item| item as f32);    // Converts items to float
397    /// assert_eq!(p.into_arr(), [0.0, 1.0, 2.0]);
398    /// ```
399    ///
400    /// # Enabled by features:
401    ///
402    /// - `default`
403    ///
404    /// - `appliers`
405    ///
406    /// # Panics
407    ///
408    /// - If the dimensions of `self` are greater than `u32::MAX`.
409    ///
410    #[cfg(feature = "appliers")]
411    pub fn apply<U>(self, modifier: ApplyFn<T, U>) -> PointND<U, N> {
412        self._check_arrvec_cap(N, "apply");
413
414        let mut arr_v = ArrayVec::<U, N>::new();
415        let mut this = ArrayVec::from(self.into_arr());
416
417        for _ in 0..N {
418            let item = this.pop_at(0).unwrap();
419            arr_v.push(modifier(item));
420        }
421
422        PointND::from(
423            arrvec_into_inner(arr_v, "apply")
424        )
425    }
426
427    ///
428    /// Consumes `self` and calls the `modifier` on the items at the
429    /// specified `dims` to create a new `PointND` of the same length.
430    ///
431    /// Any items at dimensions not specified will be passed to the new point without change
432    ///
433    /// ```
434    /// # use point_nd::PointND;
435    /// let p = PointND
436    ///     ::from([0,1,2,3,4])                       // Creates a PointND
437    ///     .apply_dims(&[1,3], |item| item * 2)      // Multiplies items 1 and 3 by 2
438    ///     .apply_dims(&[0,2], |item| item + 10);    // Adds 10 to items 0 and 2
439    /// assert_eq!(p.into_arr(), [10, 2, 12, 6, 4]);
440    /// ```
441    ///
442    /// Unlike some other apply methods, this ```apply_dims``` cannot return
443    /// a `PointND` with items of a different type from the original.
444    ///
445    /// # Enabled by features:
446    ///
447    /// - `default`
448    ///
449    /// - `appliers`
450    ///
451    /// # Panics
452    ///
453    /// - If the dimensions of `self` are greater than `u32::MAX`.
454    ///
455    #[cfg(feature = "appliers")]
456    pub fn apply_dims(self, dims: &[usize], modifier: ApplyDimsFn<T>) -> Self {
457        self._check_arrvec_cap(N, "apply_dims");
458
459        let mut arr_v = ArrayVec::<T, N>::new();
460        let mut this = ArrayVec::from(self.into_arr());
461
462        for i in 0..N {
463            let item = this.pop_at(0).unwrap();
464            if dims.contains(&i) {
465                arr_v.push(modifier(item));
466            } else {
467                arr_v.push(item);
468            }
469        }
470
471        PointND::from(
472            arrvec_into_inner(arr_v, "apply_dims")
473        )
474    }
475
476    /**
477     Consumes `self` and calls the `modifier` on each item contained by
478     `self` and ```values``` to create a new `PointND` of the same length.
479
480     As this method may modify every value in the original point,
481     the ```values``` array must be the same length as the point.
482
483     When creating a modifier function to be used by this method, keep
484     in mind that the items in `self` are passed to it through the
485     **first arg**, and the items in ```values``` through the **second**.
486
487     ```
488     # use point_nd::PointND;
489     let p = PointND
490         ::from([0,1,2])                      // Creates a new PointND
491         .apply_vals([1,3,5], |a, b| a + b)   // Adds items in point to items in array
492         .apply_vals([2,4,6], |a, b| a * b);  // Multiplies items in point to items in array
493     assert_eq!(p.into_arr(), [2, 16, 42]);
494     ```
495
496     Neither the return type of the `modifier` nor the type of the items contained
497     by the ```values``` array necessarily have to be the same as the item type of the
498     original point. This means that ```apply_vals``` can create a new point with items
499     of a different type, but the same length.
500
501     ```
502     # use point_nd::PointND;
503     enum Op {
504        Add,
505        Sub,
506     }
507
508    // Adds or subtracts 10 from 'a' depending on the
509    //  operation specified in 'b', then converts to float
510    let add_or_sub = |a, b| {
511        match b {
512            Op::Add => (a + 10) as f32,
513            Op::Sub => (a - 10) as f32
514        }
515    };
516
517     let p = PointND
518         ::from([0,1,2])
519         .apply_vals(
520             [Op::Add, Op::Sub, Op::Add],
521             add_or_sub
522         );
523     assert_eq!(p.into_arr(), [10.0, -9.0, 12.0]);
524     ```
525
526     # Enabled by features:
527
528     - `default`
529
530     - `appliers`
531
532     # Panics
533
534     - If the dimensions of `self` or ```values``` are greater than `u32::MAX`.
535     */
536    #[cfg(feature = "appliers")]
537    pub fn apply_vals<U, V>(
538        self,
539        values: [V; N],
540        modifier: ApplyValsFn<T, U, V>
541    ) -> PointND<U, N> {
542        self._check_arrvec_cap(N, "apply_vals");
543
544        let mut arr_v = ArrayVec::<U, N>::new();
545        let mut vals = ArrayVec::from(values);
546        let mut this = ArrayVec::from(self.into_arr());
547
548        for _ in 0..N {
549            let a = this.pop_at(0).unwrap();
550            let b = vals.pop_at(0).unwrap();
551            arr_v.push(modifier(a, b));
552        }
553
554        PointND::from(
555            // Had to put two method names here as this function is called from apply_point()
556            arrvec_into_inner(arr_v, "apply_vals() or apply_point")
557        )
558    }
559
560    ///
561    /// Consumes `self` and calls the `modifier` on each item contained by
562    /// `self` and another `PointND` to create a new point of the same length.
563    ///
564    /// When creating a modifier function to be used by this method, keep
565    /// in mind that the items in `self` are passed to it through the
566    /// **first arg**, and the items in `other` through the **second**.
567    ///
568    /// ```
569    /// # use point_nd::PointND;
570    /// let p1 = PointND::from([0,9,3,1]);
571    /// let p2 = PointND::fill(10);
572    /// let p3 = PointND
573    ///     ::from([1,2,3,4])                // Creates a new PointND
574    ///     .apply_point(p1, |a, b| a - b)   // Subtracts items in p3 with those in p1
575    ///     .apply_point(p2, |a, b| a * b);  // Multiplies items in p3 with those in p2
576    /// assert_eq!(p3.into_arr(), [10, -70, 0, 30]);
577    /// ```
578    ///
579    /// Neither the return type of the `modifier` nor the type of the items
580    /// contained by the `other` point necessarily have to be  the same as
581    /// the type of the items in the original point. This means that ```apply_point```
582    /// can create a new point with items of a different type, but the same length.
583    ///
584    /// # Enabled by features:
585    ///
586    /// - `default`
587    ///
588    /// - `appliers`
589    ///
590    /// # Panics
591    ///
592    /// - If the dimensions of `self` or `other` are greater than `u32::MAX`.
593    ///
594    #[cfg(feature = "appliers")]
595    pub fn apply_point<U, V>(
596        self,
597        other: PointND<V, N>,
598        modifier: ApplyPointFn<T, U, V>
599    ) -> PointND<U, N> {
600        self._check_arrvec_cap(N, "apply_point");
601
602        self.apply_vals(other.into_arr(), modifier)
603    }
604
605    
606    ///
607    /// Consumes `self` and returns a new `PointND` with items from `values` appended to
608    /// items from the original.
609    /// 
610    /// ```
611    /// # use point_nd::PointND;
612    /// let p = PointND
613    ///     ::from([0,1])
614    ///     .extend([2,3]);
615    ///  assert_eq!(p.into_arr(), [0,1,2,3]);
616    /// ```
617    ///
618    /// # **Warning!**
619    ///
620    /// Although we believe it has been tested against the most common use cases, no guarantees are
621    /// made as to the stability of this method.
622    ///
623    /// # Enabled by features:
624    ///
625    /// - `var-dims`
626    ///
627    /// # Panics
628    ///
629    /// - If the combined length of `self` and `values` are greater than `u32::MAX`.
630    ///
631    /// ```should_panic
632    /// # use point_nd::PointND;
633    /// const N: usize = u32::MAX as usize;
634    /// const L: usize = 1;
635    /// const M: usize = N + L;
636    ///
637    /// let p: PointND<_, M> = PointND
638    ///     ::from([0; N])
639    ///     .extend([1; L]);
640    /// ```
641    ///
642    #[cfg(feature = "var-dims")]
643    pub fn extend<const L: usize, const M: usize>(self, values: [T; L]) -> PointND<T, M> {
644        self._check_arrvec_cap(N, "extend");
645        if N + L > ARRVEC_CAP {
646            panic!("Attempted to extend() a PointND to more than u32::MAX dimensions");
647        }
648
649        let mut arr_v = ArrayVec::<T, M>::new();
650        let mut this = ArrayVec::from(self.into_arr());
651        let mut vals = ArrayVec::from(values);
652
653        for _ in 0..N { arr_v.push(this.pop_at(0).unwrap()); }
654        for _ in 0..L { arr_v.push(vals.pop_at(0).unwrap());  }
655
656        PointND::from(
657            arrvec_into_inner(arr_v, "extend")
658        )
659    }
660
661    ///
662    /// Consumes `self` and returns a new `PointND` which retains only the first `dims` items of the
663    /// original.
664    ///
665    /// This method always removes the rearmost items first.
666    ///
667    /// ```
668    /// # use point_nd::PointND;
669    /// let p = PointND
670    ///     ::from([0,1,2,3])
671    ///     .retain(2);
672    /// assert_eq!(p.dims(), 2);
673    /// assert_eq!(p.into_arr(), [0,1]);
674    /// ```
675    ///
676    /// # **Warning!**
677    ///
678    /// Although we believe it has been tested against the most common use cases, no guarantees are
679    /// made as to the stability of this method.
680    ///
681    /// # Enabled by features:
682    ///
683    /// - `var-dims`
684    ///
685    /// # Panics
686    ///
687    /// - If `dims` is greater than the original dimensions of the point (_a.k.a_ - you cannot
688    ///   shorten the dimensions of a point to more than it had originally).
689    ///
690    /// ```should_panic
691    /// # use point_nd::PointND;
692    /// let p = PointND
693    ///     ::from([0,1,2])
694    ///     .retain(1_000_000);
695    /// # // Just to silence the type error
696    /// # let _p2 = PointND::from([0,1,2]).apply_point(p, |a, b| a + b);
697    /// ```
698    ///
699    /// - If the dimensions of `self` are greater than `u32::MAX`.
700    ///
701    #[cfg(feature = "var-dims")]
702    pub fn retain<const M: usize>(self, dims: usize) -> PointND<T, M> {
703        self._check_arrvec_cap(N, "retain");
704        // This check allows us to safely unwrap the values in self
705        if dims > N || M > N {
706            panic!("Attempted to contract PointND to more dimensions than it had originally. Try \
707                    passing a usize value that is less than the dimensions of the original point");
708        }
709
710        let mut arr_v = ArrayVec::<T, M>::new();
711        let mut this = ArrayVec::from(self.into_arr());
712
713        for _ in 0..dims {
714            let item = this.pop_at(0).unwrap();
715            arr_v.push(item);
716        }
717
718        PointND::from(
719            arrvec_into_inner(arr_v, "retain")
720        )
721    }
722
723}
724
725
726// Deref
727impl<T, const N: usize> Deref for PointND<T, N> {
728
729    type Target = [T; N];
730    fn deref(&self) -> &Self::Target {
731        &self.0
732    }
733
734}
735
736impl<T, const N: usize> DerefMut for PointND<T, N> {
737
738    fn deref_mut(&mut self) -> &mut Self::Target {
739        &mut self.0
740    }
741
742}
743
744
745// Convenience Getters and Setters
746///
747/// Methods for safely getting and setting the value contained by a 1D `PointND`
748///
749/// # Enabled by features:
750///
751/// - `default`
752///
753/// - `conv_methods`
754///
755/// - `x`
756///
757#[cfg(feature = "x")]
758impl<T> PointND<T, 1> {
759
760    pub fn x(&self) -> &T { &self[0] }
761
762    pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
763
764}
765///
766/// Methods for safely getting and setting the values contained by a 2D `PointND`
767///
768/// # Enabled by features:
769///
770/// - `default`
771///
772/// - `conv_methods`
773///
774/// - `y`
775///
776#[cfg(feature = "y")]
777impl<T> PointND<T, 2> {
778
779    pub fn x(&self) -> &T { &self[0] }
780    pub fn y(&self) -> &T { &self[1] }
781
782    pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
783    pub fn set_y(&mut self, new_value: T) { self[1] = new_value; }
784
785}
786///
787/// Methods for safely getting and setting the values contained by a 3D `PointND`
788///
789/// # Enabled by features:
790///
791/// - `default`
792///
793/// - `conv_methods`
794///
795/// - `z`
796///
797#[cfg(feature = "z")]
798impl<T> PointND<T, 3>  {
799
800    pub fn x(&self) -> &T { &self[0] }
801    pub fn y(&self) -> &T { &self[1] }
802    pub fn z(&self) -> &T { &self[2] }
803
804    pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
805    pub fn set_y(&mut self, new_value: T) { self[1] = new_value; }
806    pub fn set_z(&mut self, new_value: T) { self[2] = new_value; }
807
808}
809///
810/// Methods for safely getting and setting the values contained by a 4D `PointND`
811///
812/// # Enabled by features:
813///
814/// - `default`
815///
816/// - `conv_methods`
817///
818/// - `w`
819///
820#[cfg(feature = "w")]
821impl<T> PointND<T, 4>  {
822
823    pub fn x(&self) -> &T { &self[0] }
824    pub fn y(&self) -> &T { &self[1] }
825    pub fn z(&self) -> &T { &self[2] }
826    pub fn w(&self) -> &T { &self[3] }
827
828    pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
829    pub fn set_y(&mut self, new_value: T) { self[1] = new_value; }
830    pub fn set_z(&mut self, new_value: T) { self[2] = new_value; }
831    pub fn set_w(&mut self, new_value: T) { self[3] = new_value; }
832
833}
834
835// Convenience Shifters
836///
837/// Method for safely transforming the value contained by a 1D `PointND`
838///
839/// # Enabled by features:
840///
841/// - `default`
842///
843/// - `conv_methods`
844///
845/// - `x`
846/// 
847#[cfg(feature = "x")]
848impl<T> PointND<T, 1>
849    where T: AddAssign {
850
851    pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
852
853}
854///
855/// Methods for safely transforming the values contained by a 2D `PointND`
856///
857/// # Enabled by features:
858///
859/// - `default`
860///
861/// - `conv_methods`
862///
863/// - `y`
864///
865#[cfg(feature = "y")]
866impl<T> PointND<T, 2>
867    where T: AddAssign {
868
869    pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
870    pub fn shift_y(&mut self, delta: T) { self[1] += delta; }
871
872}
873///
874/// Methods for safely transforming the values contained by a 3D `PointND`
875///
876/// # Enabled by features:
877///
878/// - `default`
879///
880/// - `conv_methods`
881///
882/// - `z`
883///
884#[cfg(feature = "z")]
885impl<T> PointND<T, 3>
886    where T: AddAssign {
887
888    pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
889    pub fn shift_y(&mut self, delta: T) { self[1] += delta; }
890    pub fn shift_z(&mut self, delta: T) { self[2] += delta; }
891
892}
893///
894/// Methods for safely transforming the values contained by a 4D `PointND`
895///
896/// # Enabled by features:
897///
898/// - `default`
899///
900/// - `conv_methods`
901///
902/// - `w`
903///
904#[cfg(feature = "w")]
905impl<T> PointND<T, 4>
906    where T: AddAssign {
907
908    pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
909    pub fn shift_y(&mut self, delta: T) { self[1] += delta; }
910    pub fn shift_z(&mut self, delta: T) { self[2] += delta; }
911    pub fn shift_w(&mut self, delta: T) { self[3] += delta; }
912
913}
914
915
916impl<T, const N: usize> From<[T; N]> for PointND<T, N> {
917
918    fn from(array: [T; N]) -> Self {
919        PointND(array)
920    }
921
922}
923
924impl<T, const N: usize> From<PointND<T, N>> for [T; N] {
925
926    fn from(point: PointND<T, N>) -> Self {
927        point.into_arr()
928    }
929
930}
931
932impl<T, const N: usize> TryFrom<&[T]> for PointND<T, N>
933    where T: Copy {
934
935    type Error = TryFromSliceError;
936    fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
937
938        let res: Result<[T; N], _> = slice.try_into();
939        match res {
940            Ok(arr) => Ok( PointND(arr) ),
941            Err(err) => Err( err )
942        }
943    }
944
945}
946
947
948#[cfg(test)]
949mod tests {
950    use super::*;
951
952    #[cfg(test)]
953    mod iterating {
954        use super::*;
955
956        #[test]
957        fn can_iter() {
958
959            let arr = [0, 1, 2, 3];
960
961            let p = PointND::<u8, 4>::from_slice(&arr);
962            for (i, item) in p.iter().enumerate() {
963                assert_eq!(arr[i], *item);
964            }
965
966            let mut p = PointND::<u8, 4>::from_slice(&arr);
967            for item in p.iter_mut() {
968                *item = 10;
969            }
970
971            for i in p.into_iter() {
972                assert_eq!(i, 10u8);
973            }
974
975        }
976
977    }
978
979    #[cfg(test)]
980    mod constructors {
981        use super::*;
982
983        // The from() constructor is under tests::from_and_into
984
985        #[test]
986        fn from_slice_works() {
987            let arr = [0.0, 0.1, 0.2];
988            let p = PointND::<f64, 3>::from_slice(&arr);
989            for i in 0..p.dims() {
990                assert_eq!(arr[i], p[i]);
991            }
992        }
993
994        #[test]
995        fn fill_works() {
996            let fill_val = 21u8;
997            let p = PointND::<u8, 5>::fill(fill_val);
998            for i in p.into_iter() {
999                assert_eq!(i, fill_val);
1000            }
1001        }
1002
1003    }
1004
1005    #[cfg(test)]
1006    mod indexing {
1007        use super::*;
1008
1009        #[test]
1010        fn can_get_slice_by_range_index() {
1011            let p = PointND::from([0,1,2,3,4]);
1012            let slice = &p[0..3];
1013            assert_eq!(slice, [0,1,2]);
1014        }
1015
1016        #[test]
1017        #[should_panic]
1018        fn cannot_get_out_of_bounds_index() {
1019            let p = PointND::from([0,1,2]);
1020            let _x = p[p.dims() + 1];
1021        }
1022
1023        #[test]
1024        fn can_set_value_by_index() {
1025
1026            let mut p = PointND::from([0,1,2]);
1027
1028            let new_val = 9999;
1029            p[1] = new_val;
1030
1031            assert_eq!(p.into_arr(), [0, new_val, 2]);
1032        }
1033
1034    }
1035
1036    #[cfg(test)]
1037    #[cfg(feature = "appliers")]
1038    mod appliers {
1039        use super::*;
1040
1041        #[test]
1042        fn can_apply() {
1043
1044            let arr = [0,1,2,3];
1045
1046            let p = PointND::<u8, 4>
1047                ::from(arr)
1048                .apply(|a| a * 2);
1049
1050            assert_eq!(p.into_arr(), [0, 2, 4, 6]);
1051        }
1052
1053        #[test]
1054        fn can_apply_dims() {
1055
1056            let p = PointND::from([-2,-1,0,1,2])
1057                .apply_dims(&[0, 3], |item| item - 10);
1058            assert_eq!(p.into_arr(), [-12,-1, 0, -9, 2]);
1059        }
1060
1061        #[test]
1062        fn can_apply_vals() {
1063
1064            let p = PointND::from([0,1,2])
1065                .apply_vals([Some(10), None, Some(20)],
1066                            |a, b| {
1067                        if let Some(i) = b {
1068                            a + i
1069                        } else {
1070                            a
1071                        }
1072                    });
1073            assert_eq!(p.into_arr(), [10, 1, 22]);
1074        }
1075
1076        #[test]
1077        fn can_apply_point() {
1078
1079            let p1 = PointND::from([0, 1, 2, 3]);
1080            let p2 = PointND::from([0, -1, -2, -3]);
1081            let p3 = p1.apply_point(p2, |a, b| a - b );
1082            assert_eq!(p3.into_arr(), [0, 2, 4, 6]);
1083        }
1084
1085        #[test]
1086        fn can_apply_noclone_items() {
1087
1088            #[derive(Debug, Eq, PartialEq)]
1089            enum X { A, B, C }
1090
1091            let p = PointND
1092                ::from([X::A, X::B, X::C])
1093                .apply(|x| {
1094                    match x {
1095                        X::A => X::B,
1096                        X::B => X::C,
1097                        X::C => X::A,
1098                    }
1099                });
1100
1101            assert_eq!(p.into_arr(), [X::B, X::C, X::A]);
1102        }
1103
1104    }
1105
1106    #[cfg(test)]
1107    #[cfg(feature = "var-dims")]
1108    mod extenders {
1109        use super::*;
1110
1111        #[test]
1112        fn can_extend() {
1113
1114            let zero = PointND::<i32, 0>::from([]);
1115            assert_eq!(zero.dims(), 0);
1116
1117            let two = zero.clone().extend([0,1]);
1118            assert_eq!(two.dims(), 2);
1119            assert_eq!(two.into_arr(), [0, 1]);
1120
1121            let five = PointND
1122                ::from([0,1,2])
1123                .extend([3,4]);
1124            assert_eq!(five.dims(), 5);
1125            assert_eq!(five.clone().into_arr(), [0,1,2,3,4]);
1126
1127            let sum = five.apply_point(PointND::from([0,1,2,3,4]), |a, b| a + b);
1128            assert_eq!(sum.into_arr(), [0,2,4,6,8]);
1129
1130            let huge = PointND
1131                ::from([0; 100])
1132                .extend([1; 1_000]) as PointND<i32, 1_100>;
1133            assert_eq!(huge.dims(), 1_100);
1134        }
1135
1136        #[test]
1137        fn can_extend_nothing() {
1138            let arr: [i32; 0] = [];
1139            let zero = PointND
1140                ::from(arr)
1141                .extend::<0, 0>(arr);
1142            assert_eq!(zero.dims(), 0);
1143        }
1144
1145    }
1146
1147    #[cfg(test)]
1148    #[cfg(feature = "var-dims")]
1149    mod retain {
1150        use super::*;
1151
1152        #[test]
1153        fn can_retain_n() {
1154            let p = PointND
1155                ::from([0,1,2,3])
1156                .retain(3);
1157
1158            assert_eq!(p.dims(), 3);
1159            assert_eq!(p.into_arr(), [0,1,2]);
1160        }
1161
1162        #[test]
1163        fn can_retain_zero() {
1164            let p = PointND
1165                ::from([0,1,2,3])
1166                .retain(0);
1167
1168            assert_eq!(p.dims(), 0);
1169            assert_eq!(p.into_arr(), []);
1170        }
1171
1172        #[test]
1173        fn can_retain_same() {
1174            let p = PointND
1175                ::from([0,1,2,3])
1176                .retain(4);
1177
1178            assert_eq!(p.dims(), 4);
1179            assert_eq!(p.into_arr(), [0,1,2,3]);
1180        }
1181
1182        #[test]
1183        #[should_panic]
1184        #[allow(unused_variables)]
1185        fn cannot_retain_more_dimensions() {
1186            let p = PointND
1187                ::from([0,1,2,3])
1188                .retain::<1000>(1000);
1189        }
1190
1191    }
1192
1193    #[cfg(test)]
1194    #[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
1195    mod conv_methods {
1196        use super::*;
1197
1198        #[cfg(test)]
1199        #[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
1200        mod get {
1201            use super::*;
1202
1203            #[test]
1204            #[cfg(feature = "x")]
1205            fn getter_for_1d_points_work() {
1206                let arr = [0];
1207                let p = PointND::from(arr);
1208                assert_eq!(*p.x(), arr[0]);
1209            }
1210
1211            #[test]
1212            #[cfg(feature = "y")]
1213            fn getters_for_2d_points_work() {
1214                let arr = [0,1];
1215                let p = PointND::from(arr);
1216
1217                assert_eq!(*p.x(), arr[0]);
1218                assert_eq!(*p.y(), arr[1]);
1219            }
1220
1221            #[test]
1222            #[cfg(feature = "z")]
1223            fn getters_for_3d_points_work() {
1224                let arr = [0,1,2];
1225                let p = PointND::from(arr);
1226
1227                assert_eq!(*p.x(), arr[0]);
1228                assert_eq!(*p.y(), arr[1]);
1229                assert_eq!(*p.z(), arr[2]);
1230            }
1231
1232            #[test]
1233            #[cfg(feature = "w")]
1234            fn getters_for_4d_points_work() {
1235                let arr = [0,1,2,3];
1236                let p = PointND::from(arr);
1237
1238                assert_eq!(*p.x(), arr[0]);
1239                assert_eq!(*p.y(), arr[1]);
1240                assert_eq!(*p.z(), arr[2]);
1241                assert_eq!(*p.w(), arr[3]);
1242            }
1243
1244        }
1245
1246        #[cfg(test)]
1247        #[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
1248        mod set {
1249            use super::*;
1250
1251            #[test]
1252            #[cfg(feature = "x")]
1253            fn setter_for_1d_points_work() {
1254
1255                let old_vals = [0];
1256                let new_val = 4;
1257                let mut p = PointND::from(old_vals);
1258
1259                p.set_x(new_val);
1260                assert_eq!(*p.x(), new_val);
1261            }
1262
1263            #[test]
1264            #[cfg(feature = "y")]
1265            fn setters_for_2d_points_work() {
1266
1267                let old_vals = [0,1];
1268                let new_vals = [4,5];
1269                let mut p = PointND::from(old_vals);
1270
1271                p.set_x(new_vals[0]);
1272                p.set_y(new_vals[1]);
1273
1274                assert_eq!(*p.x(), new_vals[0]);
1275                assert_eq!(*p.y(), new_vals[1]);
1276            }
1277
1278            #[test]
1279            #[cfg(feature = "z")]
1280            fn setters_for_3d_points_work() {
1281
1282                let old_vals = [0,1,2];
1283                let new_vals = [4,5,6];
1284                let mut p = PointND::from(old_vals);
1285
1286                p.set_x(new_vals[0]);
1287                p.set_y(new_vals[1]);
1288                p.set_z(new_vals[2]);
1289
1290                assert_eq!(*p.x(), new_vals[0]);
1291                assert_eq!(*p.y(), new_vals[1]);
1292                assert_eq!(*p.z(), new_vals[2]);
1293            }
1294
1295            #[test]
1296            #[cfg(feature = "w")]
1297            fn setters_for_4d_points_work() {
1298
1299                let old_vals = [0,1,2,3];
1300                let new_vals = [4,5,6,7];
1301                let mut p = PointND::from(old_vals);
1302
1303                p.set_x(new_vals[0]);
1304                p.set_y(new_vals[1]);
1305                p.set_z(new_vals[2]);
1306                p.set_w(new_vals[3]);
1307
1308                assert_eq!(*p.x(), new_vals[0]);
1309                assert_eq!(*p.y(), new_vals[1]);
1310                assert_eq!(*p.z(), new_vals[2]);
1311                assert_eq!(*p.w(), new_vals[3]);
1312            }
1313
1314        }
1315
1316        #[cfg(test)]
1317        #[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
1318        mod shift {
1319            use super::*;
1320
1321            #[test]
1322            #[cfg(feature = "x")]
1323            fn can_shift_1d_points() {
1324                let mut p = PointND::from([0.1]);
1325                p.shift_x(1.23);
1326
1327                assert_eq!(p.into_arr(), [1.33]);
1328            }
1329
1330            #[test]
1331            #[cfg(feature = "y")]
1332            fn can_shift_2d_points() {
1333                let mut p = PointND::from([12, 345]);
1334                p.shift_x(-22);
1335                p.shift_y(-345);
1336
1337                assert_eq!(p.into_arr(), [-10, 0]);
1338            }
1339
1340            #[test]
1341            #[cfg(feature = "z")]
1342            fn can_shift_3d_points() {
1343                let mut p = PointND::from([42.4, 2.85, 75.01]);
1344                p.shift_x(40.6);
1345                p.shift_y(-2.85);
1346                p.shift_z(24.99);
1347
1348                assert_eq!(p.into_arr(), [83.0, 0.0, 100.0]);
1349            }
1350
1351            #[test]
1352            #[cfg(feature = "w")]
1353            fn can_shift_4d_points() {
1354                let mut p = PointND::from([0,1,2,3]);
1355                p.shift_x(10);
1356                p.shift_y(-2);
1357                p.shift_z(5);
1358                p.shift_w(0);
1359
1360                assert_eq!(p.into_arr(), [10, -1, 7, 3]);
1361            }
1362
1363        }
1364
1365    }
1366
1367    #[cfg(test)]
1368    mod from_and_into {
1369        use super::*;
1370
1371        #[test]
1372        fn from_array_works() {
1373            let p = PointND::from([0,1,2]);
1374            assert_eq!(p.dims(), 3);
1375
1376            let p: PointND<i32, 4> = [22; 4].into();
1377            assert_eq!(p.into_arr(), [22; 4]);
1378        }
1379
1380        #[test]
1381        fn into_array_works() {
1382            let arr: [i32; 3] = PointND::fill(10).into();
1383            assert_eq!(arr, [10, 10, 10]);
1384        }
1385
1386    }
1387
1388    #[cfg(test)]
1389    mod try_from_and_try_into {
1390        use super::*;
1391
1392        #[test]
1393        fn can_try_from_array() {
1394            let arr = [0,1,2,3,4,5];
1395            let p: Result<PointND<_, 6>, _> = arr.try_into();
1396            assert!(p.is_ok());
1397        }
1398
1399        #[test]
1400        fn can_try_from_slice_of_same_len() {
1401            let slice = &[0,1,2,3,4][..];
1402            let p: Result<PointND<_, 5>, _> = slice.try_into();
1403            assert!(p.is_ok());
1404        }
1405
1406        #[test]
1407        fn cannot_try_from_slice_of_different_length() {
1408            let slice = &[0,1,2,3,4][..];
1409            let p: Result<PointND<_, 10921>, _> = slice.try_into();
1410            assert!(p.is_err());
1411        }
1412
1413    }
1414
1415}