Carp/core/Vector.carp
2021-07-05 14:48:35 +02:00

289 lines
7.4 KiB
Plaintext

(deftype (Vector2 f) [x f, y f])
(derive Vector2 zero)
(derive Vector2 =)
(doc Vector2 "is a two-dimensional vector data type.")
(defmodule Vector2
(defn map [f v]
(init (f @(x v))
(f @(y v))))
(defn zip [f a b]
(init (f @(x a) @(x b))
(f @(y a) @(y b))))
(defn vreduce [f i v]
(f (f i @(x v)) @(y v)))
(defn random []
(init (random-0-1) (random-0-1)))
(implements random Vector2.random)
(defn add [a b]
(zip + a b))
(defn sub [a b]
(zip - a b))
(defn mul [a n]
(init (* @(x a) n)
(* @(y a) n)))
(defn div [a n]
(init (/ @(x a) n)
(/ @(y a) n)))
(doc vapprox "Check whether the vectors a and b are approximately equal.")
(defn vapprox [a b]
(vreduce (fn [i v] (and i v)) true &(zip Generics.approx a b)))
(defn sum [o]
(vreduce + (zero) o))
(doc dot "Get the dot product of the two vectors x and y.")
(defn dot [a b]
(sum &(zip * a b)))
(doc mag-sq "Get the squared magnitude of a vector.")
(defn mag-sq [o]
(dot o o))
(doc mag "Get the magnitude of a vector.")
(defn mag [o]
(sqrt (mag-sq o)))
(doc normalize "Normalize a vector.")
(defn normalize [o]
(let [m (mag o)]
(if (zero? m)
@o
(div o m))))
(doc dist "Get the distance between the vectors a and b.")
(defn dist [a b]
(let [s (sub b a)]
(mag &s)))
(doc heading "Get the heading of the vector a.")
(defn heading [a]
(atan2 @(y a) @(x a)))
(doc rotate "Rotate the vector a by the radians n.")
(defn rotate [a n]
(let [h (+ (heading a) n)
m (mag a)]
(init (* (cos h) m) (* (sin h) m))))
(doc angle-between "Get the angle between two vectors a and b.")
(defn angle-between [a b]
(let [dmm (/ (dot a b) (* (mag a) (mag b)))]
(acos (clamp--1-1 dmm))))
(doc anti-parallel? "Check whether the two vectors a and b are anti-parallel.")
(defn anti-parallel? [a b]
(= (angle-between a b) pi))
(doc parallel? "Check whether the two vectors a and b are parallel.")
(defn parallel? [a b]
(zero? (angle-between a b)))
(doc perpendicular? "Check whether the two vectors a and b are perpendicular.")
(defn perpendicular? [a b]
(= (angle-between a b) (Generics.half-pi)))
(doc vlerp "Linearly interpolate between the two vectors a and b by amnt (between 0 and 1).")
(defn vlerp [a b amnt]
(zip (fn [a b] (lerp a b amnt)) a b))
)
(deftype (Vector3 f) [x f, y f, z f])
(derive Vector3 zero)
(derive Vector3 =)
(doc Vector3 "is a three-dimensional vector data type.")
(defmodule Vector3
(defn map [f v]
(init (f @(x v))
(f @(y v))
(f @(z v))))
(defn zip [f a b]
(init (f @(x a) @(x b))
(f @(y a) @(y b))
(f @(z a) @(z b))))
(defn vreduce [f i v]
(f (f (f i @(x v)) @(y v)) @(z v)))
(defn random []
(init (random-0-1) (random-0-1) (random-0-1)))
(implements random Vector3.random)
(doc vapprox "Check whether the vectors a and b are approximately equal.")
(defn vapprox [a b]
(vreduce (fn [i v] (and i v)) true &(zip Generics.approx a b)))
(defn add [a b]
(zip + a b))
(defn sub [a b]
(zip - a b))
(defn cmul [a b]
(zip * a b))
(defn neg [a]
(map neg a))
(defn mul [v n]
(map (fn [c] (* n c)) v))
(defn div [v n]
(map (fn [c] (/ c n)) v))
(defn sum [o]
(vreduce + (zero) o))
(doc dot "Get the dot product of the two vectors x and y.")
(defn dot [a b]
(sum &(zip * a b)))
(doc mag-sq "Get the squared magnitude of a vector.")
(defn mag-sq [o]
(dot o o))
(doc mag "Get the magnitude of a vector.")
(defn mag [o]
(sqrt (mag-sq o)))
(doc normalize "Normalize a vector.")
(defn normalize [o]
(let [m (mag o)]
(if (= m (zero))
@o
(div o m))))
(doc dist "Get the distance between the vectors a and b.")
(defn dist [a b]
(let [s (sub b a)]
(mag &s)))
(doc cross "Compute the cross product of the two vectors x and y.")
(defn cross [a b]
(init
(- (* @(y a) @(z b))
(* @(z a) @(y b)))
(- (* @(z a) @(x b))
(* @(x a) @(z b)))
(- (* @(x a) @(y b))
(* @(y a) @(x b)))))
(doc angle-between "Get the angle between two vectors a and b.")
(defn angle-between [a b]
(let [dmm (/ (dot a b) (* (mag a) (mag b)))]
(acos (clamp--1-1 dmm))))
(doc anti-parallel? "Check whether the two vectors a and b are anti-parallel.")
(defn anti-parallel? [a b]
(= (angle-between a b) pi))
(doc parallel? "Check whether the two vectors a and b are parallel.")
(defn parallel? [a b]
(zero? (angle-between a b)))
(doc perpendicular? "Check whether the two vectors a and b are perpendicular.")
(defn perpendicular? [a b]
(= (angle-between a b) (Generics.half-pi)))
(doc vlerp "Linearly interpolate between the two vectors a and b by amnt (between 0 and 1).")
(defn vlerp [a b amnt]
(zip (fn [a b] (lerp a b amnt)) a b))
)
(deftype (VectorN f) [n Int, v (Array f)])
(derive VectorN =)
(doc VectorN "is an n-dimensional vector data type. Its implementation is
array-backed.")
(defmodule VectorN
(defn zero-sized [n]
(let [z (zero)]
(init n (Array.replicate n &z))))
(defn random-sized [n]
(init n (Array.repeat n &random-0-1)))
(defn zip- [f a b]
(let [total (Array.allocate (Array.length a))]
(do
(for [i 0 (Array.length a)]
(Array.aset-uninitialized! &total i (f @(Array.unsafe-nth a i)
@(Array.unsafe-nth b i))))
(init (Array.length a) total))))
(defn zip [f a b]
(if (= @(n a) @(n b))
(Maybe.Just (zip- f (v a) (v b)))
(Maybe.Nothing)))
(defn add [a b]
(zip + a b))
(defn sub [a b]
(zip - a b))
(defn mul [a n]
(zip- * (v a) &(Array.replicate @(VectorN.n a) &n)))
(defn div [a n]
(zip- / (v a) &(Array.replicate @(VectorN.n a) &n)))
(doc dot "Get the dot product of the two vectors x and y.")
(defn dot [x y]
(Maybe.apply (zip * x y)
&(fn [x] (Array.reduce &(fn [x y] (+ x @y)) (zero) (v &x)))))
(doc mag-sq "Get the squared magnitude of a vector.")
(defn mag-sq [o]
(Maybe.unsafe-from (dot o o)))
(doc mag "Get the magnitude of a vector.")
(defn mag [o]
(sqrt (mag-sq o)))
(doc dist "Get the distance between the vectors a and b.")
(defn dist [a b]
(Maybe.apply (sub b a) &(fn [s] (mag &s))))
(doc normalize "Normalize a vector.")
(defn normalize [o]
(let [m (mag o)]
(if (zero? m)
@o
(div o m))))
(doc angle-between "Get the angle between two vectors a and b.")
(defn angle-between [a b]
(Maybe.apply (VectorN.dot a b)
&(fn [x]
(let [dmm (/ x (* (VectorN.mag a) (VectorN.mag b)))]
(acos (clamp--1-1 dmm))))))
(doc anti-parallel? "Check whether the two vectors a and b are anti-parallel.")
(defn anti-parallel? [a b]
(Maybe.apply (angle-between a b) &(fn [x] (= x pi))))
(doc parallel? "Check whether the two vectors a and b are parallel.")
(defn parallel? [a b]
(Maybe.apply (angle-between a b) &(fn [x] (zero? x))))
(doc perpendicular? "Check whether the two vectors a and b are perpendicular.")
(defn perpendicular? [a b]
(Maybe.apply (angle-between a b) &(fn [x] (= x (Generics.half-pi)))))
(doc vlerp "Linearly interpolate between the two vectors a and b by amnt (between 0 and 1).")
(defn vlerp [a b amnt]
(zip (fn [a b] (lerp a b amnt)) a b))
)