1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#[cfg(test)]
mod tests;

use std::fmt::{Display, Formatter};
use std::ops::{Add, Sub};
#[derive(Debug, PartialEq, Copy, Clone)]
#[must_use]
/// A grid-based two-dimensional representation of a mathematical vector.
pub struct Vector2D {
    /// Defines the origin point of the vector on a grid centered around (0, 0).
    origin: (f64, f64),
    /// The end of the vector in relation to its origin.
    target: (f64, f64)
}

impl Vector2D {
    /// A zero [`Vector2D`] with both `origin` and `target` at (0, 0).
    pub fn zero() -> Self {
        Vector2D {
            origin: (0.0, 0.0),
            target: (0.0, 0.0),
        }
    }

    /// A [`Vector2D`] with a `target` of (0, 0).
    ///
    /// Also known as a zero-length vector
    pub fn null(origin: (f64, f64)) -> Self {
        Vector2D {
            origin,
            target: (0.0, 0.0),
        }
    }

    /// Constructs a [`Vector2D`] from the provided `origin` and `target`.
    pub fn new(origin: (f64, f64), target: (f64, f64)) -> Self {
        Vector2D {
            origin,
            target,
        }
    }

    /// Constructs a [`Vector2D`] from the provided `origin` and `target`.
    ///
    /// The difference between this and [`Vector2D::new()`] is that this
    /// treats `target` as an absolute value,
    /// meaning that `Vector2D::with_absolute_target((1.0,0.0),(2.0,1.0))`
    /// constructs a vector with an `origin` of (1.0, 0.0) but a `target`
    /// of (1.0, 1.0) as opposed to a `target` of (2.0, 1.0).
    pub fn with_absolute_target(origin: (f64, f64), target: (f64, f64)) -> Self {
        Vector2D {
            origin,
            target: (target.0 - origin.0, target.1 - origin.1),
        }
    }

    /// Sets the `origin` of [`self`]
    pub fn set_origin(mut self, origin: (f64, f64)) -> Self {
        self.origin = origin;
        self
    }

    /// Sets the `target` of [`self`]
    pub fn set_target(mut self, target: (f64, f64)) -> Self {
        self.target = target;
        self
    }

    /// Sets the `target` of [`self`].
    ///
    /// Uses an absolute `target` instead of a relative one.
    ///
    /// Due to issues with imprecise float arithmetic operations,
    /// only the first five digits of the input `target` are kept,
    /// and anything past that is rounded.
    /// 
    /// So far, I have only encountered this issue with this function,
    /// but if you encounter any others during use, please report them in the issue tracker.
    pub fn set_target_absolute(mut self, target: (f64, f64)) -> Self {
        self.target = (
            ((target.0 - self.origin.0) * 10000.0).round() / 10000.0, // Rounding as a workaround for errors in insignificant bits
            ((target.1 - self.origin.1) * 10000.0).round() / 10000.0, // Rounding as a workaround for errors in insignificant bits
        );
        self
    }

    /// Shifts the left-hand-side [`Vector2D`] by the right-hand-side tuple of types
    /// which implement [`Into<f32>`].
    ///
    /// The resulting [`Vector2D`] has the same `target` value, as it's relative to its `origin`,
    /// but a shifted `origin`, which results in an overall shift of the [`Vector2D`].
    pub fn shift(mut self, shift: (impl Into<f64>, impl Into<f64>)) -> Self {
        self.origin = (self.origin.0 + shift.0.into(), self.origin.1 + shift.1.into());
        self
    }

    /// Returns the magnitude of the given [`Vector2D`].
    pub fn get_magnitude(&self) -> f64 {
        f64::sqrt((self.origin.0 - self.target.0).powf(2.0) + (self.origin.1 - self.target.1).powf(2.0))
    }
    
    /// Returns the dot product of the given [`Vector2D`]s.
    pub fn dot_product(&self, vector: Vector2D) -> f64 {
        self.target.0 * vector.target.0 + self.target.1 * vector.target.1
    }

    pub fn get_angle_between(&self, vector: Vector2D) -> f64 {
        f64::acos(self.dot_product(vector) / (self.get_magnitude() * vector.get_magnitude()))
    }
}

impl Display for Vector2D {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "({},{})[{},{}]",
            self.origin.0,
            self.origin.1,
            self.target.0,
            self.target.1,
        )
    }
}

impl Add for Vector2D {
    type Output = Self;

    /// Performs a mathematical addition of two [`Vector2D`]s.
    fn add(self, rhs: Self) -> Self::Output {
        Vector2D {
            origin: self.origin,
            target: (rhs.target.0 + self.target.0, rhs.target.1 + self.target.1),
        }
    }
}


impl Sub for Vector2D {
    type Output = Self;

    /// Performs a mathematical subtraction of two [`Vector2D`]s.
    fn sub(self, rhs: Self) -> Self::Output {
        Vector2D {
            origin: self.origin,
            target: (self.target.0 - rhs.target.0, self.target.1 - rhs.target.1),
        }
    }
}