v2 / vlib / math / vec / vec2.v
467 lines · 400 sloc · 12.25 KB · 7690cbfbaf547f6875dbc20550409b943c5a776a
Raw
1// Copyright(C) 2020-2024 Lars Pontoppidan. All rights reserved.
2// Use of this source code is governed by an MIT license file distributed with this software package
3module vec
4
5import math
6
7pub const vec_epsilon = 10e-7
8
9// Vec2[T] is a generic struct representing a vector in 2D space.
10pub struct Vec2[T] {
11pub mut:
12 x T
13 y T
14}
15
16// vec2[T] returns a `Vec2` of type `T`, with `x` and `y` fields set.
17pub fn vec2[T](x T, y T) Vec2[T] {
18 return Vec2[T]{
19 x: x
20 y: y
21 }
22}
23
24// zero sets the `x` and `y` fields to 0.
25pub fn (mut v Vec2[T]) zero() {
26 v.x = 0
27 v.y = 0
28}
29
30// one sets the `x` and `y` fields to 1.
31pub fn (mut v Vec2[T]) one() {
32 v.x = 1
33 v.y = 1
34}
35
36// copy returns a copy of this vector.
37pub fn (v Vec2[T]) copy() Vec2[T] {
38 return Vec2[T]{v.x, v.y}
39}
40
41// from sets the `x` and `y` fields from `u`.
42pub fn (mut v Vec2[T]) from(u Vec2[T]) {
43 v.x = u.x
44 v.y = u.y
45}
46
47// from_vec3 sets the `x` and `y` fields from `u`.
48pub fn (mut v Vec2[T]) from_vec3[U](u Vec3[U]) {
49 v.x = T(u.x)
50 v.y = T(u.y)
51}
52
53// as_vec3 returns a Vec3 with `x` and `y` fields set from `v`, `z` is set to 0.
54pub fn (v Vec2[T]) as_vec3[T]() Vec3[T] {
55 return Vec3[T]{v.x, v.y, 0}
56}
57
58// from_vec4 sets the `x` and `y` fields from `u`.
59pub fn (mut v Vec2[T]) from_vec4[U](u Vec4[U]) {
60 v.x = T(u.x)
61 v.y = T(u.y)
62}
63
64// as_vec4 returns a Vec4 with `x` and `y` fields set from `v`, `z` and `w` is set to 0.
65pub fn (v Vec2[T]) as_vec4[T]() Vec4[T] {
66 return Vec4[T]{v.x, v.y, 0, 0}
67}
68
69//
70// Addition
71//
72
73// + returns the resulting vector of the addition of `v` and `u`.
74@[inline]
75pub fn (v Vec2[T]) + (u Vec2[T]) Vec2[T] {
76 return Vec2[T]{v.x + u.x, v.y + u.y}
77}
78
79// add returns the resulting vector of the addition of `v` + `u`.
80pub fn (v Vec2[T]) add(u Vec2[T]) Vec2[T] {
81 return v + u
82}
83
84// add_scalar returns the resulting vector of the addition of the `scalar` value.
85pub fn (v Vec2[T]) add_scalar[U](scalar U) Vec2[T] {
86 return Vec2[T]{v.x + T(scalar), v.y + T(scalar)}
87}
88
89// plus adds vector `u` to the vector.
90pub fn (mut v Vec2[T]) plus(u Vec2[T]) {
91 v.x += u.x
92 v.y += u.y
93}
94
95// plus_scalar adds the scalar `scalar` to the vector.
96pub fn (mut v Vec2[T]) plus_scalar[U](scalar U) {
97 v.x += T(scalar)
98 v.y += T(scalar)
99}
100
101//
102// Subtraction
103//
104
105// - returns the resulting vector of the subtraction of `v` and `u`.
106@[inline]
107pub fn (v Vec2[T]) - (u Vec2[T]) Vec2[T] {
108 return Vec2[T]{v.x - u.x, v.y - u.y}
109}
110
111// sub returns the resulting vector of the subtraction of `v` - `u`.
112pub fn (v Vec2[T]) sub(u Vec2[T]) Vec2[T] {
113 return v - u
114}
115
116// sub_scalar returns the resulting vector of the subtraction of the `scalar` value.
117pub fn (v Vec2[T]) sub_scalar[U](scalar U) Vec2[T] {
118 return Vec2[T]{v.x - T(scalar), v.y - T(scalar)}
119}
120
121// subtract subtracts vector `u` from the vector.
122pub fn (mut v Vec2[T]) subtract(u Vec2[T]) {
123 v.x -= u.x
124 v.y -= u.y
125}
126
127// subtract_scalar subtracts the scalar `scalar` from the vector.
128pub fn (mut v Vec2[T]) subtract_scalar[U](scalar U) {
129 v.x -= T(scalar)
130 v.y -= T(scalar)
131}
132
133//
134// Multiplication
135//
136
137// * returns the resulting vector of the multiplication of `v` and `u`.
138@[inline]
139pub fn (v Vec2[T]) * (u Vec2[T]) Vec2[T] {
140 return Vec2[T]{v.x * u.x, v.y * u.y}
141}
142
143// mul returns the resulting vector of the multiplication of `v` * `u`.
144pub fn (v Vec2[T]) mul(u Vec2[T]) Vec2[T] {
145 return v * u
146}
147
148// mul_scalar returns the resulting vector of the multiplication of the `scalar` value.
149pub fn (v Vec2[T]) mul_scalar[U](scalar U) Vec2[T] {
150 return Vec2[T]{v.x * T(scalar), v.y * T(scalar)}
151}
152
153// multiply multiplies the vector with `u`.
154pub fn (mut v Vec2[T]) multiply(u Vec2[T]) {
155 v.x *= u.x
156 v.y *= u.y
157}
158
159// multiply_scalar multiplies the vector with `scalar`.
160pub fn (mut v Vec2[T]) multiply_scalar[U](scalar U) {
161 v.x *= T(scalar)
162 v.y *= T(scalar)
163}
164
165//
166// Division
167//
168
169// / returns the resulting vector of the division of `v` and `u`.
170@[inline]
171pub fn (v Vec2[T]) / (u Vec2[T]) Vec2[T] {
172 return Vec2[T]{v.x / u.x, v.y / u.y}
173}
174
175// div returns the resulting vector of the division of `v` / `u`.
176pub fn (v Vec2[T]) div(u Vec2[T]) Vec2[T] {
177 return v / u
178}
179
180// div_scalar returns the resulting vector of the division by the `scalar` value.
181pub fn (v Vec2[T]) div_scalar[U](scalar U) Vec2[T] {
182 return Vec2[T]{v.x / T(scalar), v.y / T(scalar)}
183}
184
185// divide divides the vector by `u`.
186pub fn (mut v Vec2[T]) divide(u Vec2[T]) {
187 v.x /= u.x
188 v.y /= u.y
189}
190
191// divide_scalar divides the vector by `scalar`.
192pub fn (mut v Vec2[T]) divide_scalar[U](scalar U) {
193 v.x /= T(scalar)
194 v.y /= T(scalar)
195}
196
197//
198// Utility
199//
200
201// magnitude returns the magnitude, also known as the length, of the vector.
202pub fn (v Vec2[T]) magnitude() T {
203 if v.x == 0 && v.y == 0 {
204 return T(0)
205 }
206 $if T is f64 {
207 return math.sqrt((v.x * v.x) + (v.y * v.y))
208 } $else {
209 return T(math.sqrt(f64(v.x * v.x) + f64(v.y * v.y)))
210 }
211}
212
213// magnitude_x returns the magnitude, also known as the length, of the 1D vector field x, y is ignored.
214pub fn (v Vec2[T]) magnitude_x() T {
215 return T(math.sqrt(v.x * v.x))
216}
217
218// magnitude_x returns the magnitude, also known as the length, of the 1D vector field y, x is ignored.
219pub fn (v Vec2[T]) magnitude_y() T {
220 return T(math.sqrt(v.y * v.y))
221}
222
223// dot returns the dot product of `v` and `u`.
224// The dot product is a scalar value that represents the magnitude of one vector
225// projected onto another vector.
226// It is calculated by multiplying the corresponding components of the vectors
227// and summing the results.
228// example:
229// ```v
230// v := vec2[f32](3, 4) //magnitude = 5
231// u := vec2[f32](5, 6) //magnitude = 7.81
232// dot := v.dot(u) // 3*5 + 4*6 = 15 + 24 = 39
233// (dot) // Output: 39
234// ```
235pub fn (v Vec2[T]) dot(u Vec2[T]) T {
236 return (v.x * u.x) + (v.y * u.y)
237}
238
239// cross returns the cross product of `v` and `u`.
240pub fn (v Vec2[T]) cross(u Vec2[T]) T {
241 return (v.x * u.y) - (v.y * u.x)
242}
243
244// unit returns the unit vector.
245// unit vectors always have a magnitude, or length, of exactly 1.
246pub fn (v Vec2[T]) unit() Vec2[T] {
247 m := v.magnitude()
248 return Vec2[T]{v.x / m, v.y / m}
249}
250
251// perp_cw returns the clockwise, or "left-hand", perpendicular vector of this vector.
252pub fn (v Vec2[T]) perp_cw() Vec2[T] {
253 return Vec2[T]{v.y, -v.x}
254}
255
256// perp_ccw returns the counter-clockwise, or "right-hand", perpendicular vector of this vector.
257pub fn (v Vec2[T]) perp_ccw() Vec2[T] {
258 return Vec2[T]{-v.y, v.x}
259}
260
261// perpendicular returns the `v` projected perpendicular vector to the 'u' vector.
262pub fn (v Vec2[T]) perpendicular(u Vec2[T]) Vec2[T] {
263 return v - v.project(u)
264}
265
266// project returns the projected vector.
267// The projection of vector `v` onto vector `u` is the orthogonal projection
268// of `v` onto a straight line parallel to `u` that passes through the origin.
269// This is equivalent to the vector projection of `v` onto the unit vector in the direction of `u`.
270// and is given by the formula: proj_v(u) = (v · u / |u|^2) * u
271// where "·" denotes the dot product and |u| is the magnitude of vector `u`.
272// If `v` is a zero vector, the result will also be a zero vector.
273// example:
274// ```v
275// v := vec2[f32](3, 4)
276// u := vec2[f32](5, 6)
277// proj := v.project(u)
278// println(proj) // Output: vec2[f32](3.1967213, 3.8360658)
279// ```
280pub fn (v Vec2[T]) project(u Vec2[T]) Vec2[T] {
281 scale := T(v.dot(u) / u.dot(u))
282 return u.mul_scalar(scale)
283}
284
285// rotate_around_cw returns the vector `v` rotated *clockwise* `radians` around an origin vector `o` in Cartesian space.
286pub fn (v Vec2[T]) rotate_around_cw(o Vec2[T], radians f64) Vec2[T] {
287 return v.rotate_around_ccw(o, -radians)
288}
289
290// rotate_around_ccw returns the vector `v` rotated *counter-clockwise* `radians` around an origin vector `o` in Cartesian space.
291pub fn (v Vec2[T]) rotate_around_ccw(o Vec2[T], radians f64) Vec2[T] {
292 s := math.sin(radians)
293 c := math.cos(radians)
294 dx := v.x - o.x
295 dy := v.y - o.y
296 return Vec2[T]{
297 x: T(c * dx - s * dy + o.x)
298 y: T(s * dx + c * dy + o.y)
299 }
300}
301
302// eq returns a bool indicating if the two vectors are equal.
303@[inline]
304pub fn (v Vec2[T]) eq(u Vec2[T]) bool {
305 return v.x == u.x && v.y == u.y
306}
307
308// eq_epsilon returns a bool indicating if the two vectors are equal within the module `vec_epsilon` const.
309pub fn (v Vec2[T]) eq_epsilon(u Vec2[T]) bool {
310 return v.eq_approx[T, T](u, T(vec_epsilon))
311}
312
313// eq_approx returns whether these vectors are approximately equal within `tolerance`.
314pub fn (v Vec2[T]) eq_approx[T, U](u Vec2[T], tolerance U) bool {
315 diff_x := math.abs(v.x - u.x)
316 diff_y := math.abs(v.y - u.y)
317 if diff_x <= tolerance && diff_y <= tolerance {
318 return true
319 }
320
321 max_x := math.max(math.abs(v.x), math.abs(u.x))
322 max_y := math.max(math.abs(v.y), math.abs(u.y))
323 if diff_x < max_x * tolerance && diff_y < max_y * tolerance {
324 return true
325 }
326
327 return false
328}
329
330// is_approx_zero returns whether this vector is equal to zero within `tolerance`.
331pub fn (v Vec2[T]) is_approx_zero(tolerance T) bool {
332 if math.abs(v.x) <= tolerance && math.abs(v.y) <= tolerance {
333 return true
334 }
335 return false
336}
337
338// eq_scalar returns a bool indicating if the `x` and `y` fields both equals `scalar`.
339pub fn (v Vec2[T]) eq_scalar[U](scalar U) bool {
340 return v.x == T(scalar) && v.y == scalar
341}
342
343// distance returns the distance to the vector `u`.
344pub fn (v Vec2[T]) distance(u Vec2[T]) T {
345 $if T is f64 {
346 return math.sqrt((v.x - u.x) * (v.x - u.x) + (v.y - u.y) * (v.y - u.y))
347 } $else {
348 return T(math.sqrt(f64(v.x - u.x) * f64(v.x - u.x) + f64(v.y - u.y) * f64(v.y - u.y)))
349 }
350}
351
352// manhattan_distance returns the Manhattan Distance to the vector `u`.
353pub fn (v Vec2[T]) manhattan_distance(u Vec2[T]) T {
354 return math.abs(v.x - u.x) + math.abs(v.y - u.y)
355}
356
357// angle_between returns the angle in radians to the vector `u`.
358pub fn (v Vec2[T]) angle_between(u Vec2[T]) T {
359 $if T is f64 {
360 return math.atan2(v.cross(u), v.dot(u))
361 } $else {
362 return T(math.atan2(f64(v.cross(u)), f64(v.dot(u))))
363 }
364}
365
366// angle_towards returns the angle in radians between the horizontal axis,
367// and a line passing through the first and second point, as if the first point
368// was at the center of the coordinate system.
369pub fn (p1 Vec2[T]) angle_towards(p2 Vec2[T]) T {
370 $if T is f64 {
371 return math.atan2(p2.y - p1.y, p2.x - p1.x)
372 } $else {
373 return T(math.atan2(f64(p2.y) - f64(p1.y), f64(p2.x) - f64(p1.x)))
374 }
375}
376
377// angle returns the angle in radians of the vector.
378// example:
379// ```v
380// v := vec2[f32](3.0, 4.0)
381// a := v.angle()
382// assert a == 0.64 (approximate value in radians)
383// w := vec2[f32](0.0, 1.0)
384// b := w.angle()
385// assert b == 1.57 (approximate value in radians)
386// ```
387pub fn (v Vec2[T]) angle() T {
388 $if T is f64 {
389 return math.atan2(v.y, v.x)
390 } $else {
391 return T(math.atan2(f64(v.y), f64(v.x)))
392 }
393}
394
395// abs sets `x` and `y` field values to their absolute values.
396pub fn (mut v Vec2[T]) abs() {
397 v.x = math.abs(v.x)
398 v.y = math.abs(v.y)
399}
400
401// clean returns a vector with all fields of this vector set to zero (0) if they fall within `tolerance`.
402pub fn (v Vec2[T]) clean[U](tolerance U) Vec2[T] {
403 mut r := v.copy()
404 if math.abs(v.x) < tolerance {
405 r.x = 0
406 }
407 if math.abs(v.y) < tolerance {
408 r.y = 0
409 }
410 return r
411}
412
413// clean_tolerance sets all fields to zero (0) if they fall within `tolerance`.
414pub fn (mut v Vec2[T]) clean_tolerance[U](tolerance U) {
415 if math.abs(v.x) < tolerance {
416 v.x = 0
417 }
418 if math.abs(v.y) < tolerance {
419 v.y = 0
420 }
421}
422
423// inv returns the inverse, or reciprocal, of the vector.
424// If a field is zero, its inverse is also set to zero to avoid division by zero.
425// the direction the vector points is generally not preserved, but
426// the magnitude of each field is inverted.
427// example:
428// ```v
429// v := vec2[f32](2.0, 4.0)
430// inv_v := v.inv() // inv_v == vec2[f32](0.5, 0.25)
431// ```
432pub fn (v Vec2[T]) inv() Vec2[T] {
433 return Vec2[T]{
434 x: if v.x != 0 { T(1) / v.x } else { 0 }
435 y: if v.y != 0 { T(1) / v.y } else { 0 }
436 }
437}
438
439// normalize normalizes the vector.
440// A normalized vector has the same direction as the original vector but a magnitude of 1.
441// If the vector has a magnitude of 0, a zero vector is returned since we cannot find the direction of a zero-length vector.
442// example:
443// ```v
444// v := vec2[f32](3.0, 4.0)//magnitude = 5.0
445// n := v.normalize() // n == vec2[f32](0.6, 0.8) // magnitude = 1.0
446// ```
447pub fn (v Vec2[T]) normalize() Vec2[T] {
448 m := v.magnitude()
449 if m == 0 {
450 return vec2[T](0, 0)
451 }
452 return Vec2[T]{
453 x: v.x * (1 / m)
454 y: v.y * (1 / m)
455 }
456}
457
458// sum returns a sum of all the fields.
459// example:
460// ```v
461// v := vec2[f32](3.0, 4.0)
462// s := v.sum()
463// assert s == 7.0
464// ```
465pub fn (v Vec2[T]) sum() T {
466 return v.x + v.y
467}
468