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}