vec3.lua 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. --- A 3 component vector.
  2. -- @module vec3
  3. local sqrt = math.sqrt
  4. local cos = math.cos
  5. local sin = math.sin
  6. local vec3 = {}
  7. local vec3_mt = {}
  8. -- Private constructor.
  9. local function new(x, y, z)
  10. return setmetatable({
  11. x = x or 0,
  12. y = y or 0,
  13. z = z or 0
  14. }, vec3_mt)
  15. end
  16. -- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
  17. local status, ffi
  18. if type(jit) == "table" and jit.status() then
  19. status, ffi = pcall(require, "ffi")
  20. if status then
  21. ffi.cdef "typedef struct { double x, y, z;} cpml_vec3;"
  22. new = ffi.typeof("cpml_vec3")
  23. end
  24. end
  25. --- Constants
  26. -- @table vec3
  27. -- @field unit_x X axis of rotation
  28. -- @field unit_y Y axis of rotation
  29. -- @field unit_z Z axis of rotation
  30. -- @field zero Empty vector
  31. vec3.unit_x = new(1, 0, 0)
  32. vec3.unit_y = new(0, 1, 0)
  33. vec3.unit_z = new(0, 0, 1)
  34. vec3.zero = new(0, 0, 0)
  35. --- The public constructor.
  36. -- @param x Can be of three types: </br>
  37. -- number X component
  38. -- table {x, y, z} or {x=x, y=y, z=z}
  39. -- scalar To fill the vector eg. {x, x, x}
  40. -- @tparam number y Y component
  41. -- @tparam number z Z component
  42. -- @treturn vec3 out
  43. function vec3.new(x, y, z)
  44. -- number, number, number
  45. if x and y and z then
  46. assert(type(x) == "number", "new: Wrong argument type for x (<number> expected)")
  47. assert(type(y) == "number", "new: Wrong argument type for y (<number> expected)")
  48. assert(type(z) == "number", "new: Wrong argument type for z (<number> expected)")
  49. return new(x, y, z)
  50. -- {x, y, z} or {x=x, y=y, z=z}
  51. elseif type(x) == "table" then
  52. local xx, yy, zz = x.x or x[1], x.y or x[2], x.z or x[3]
  53. assert(type(xx) == "number", "new: Wrong argument type for x (<number> expected)")
  54. assert(type(yy) == "number", "new: Wrong argument type for y (<number> expected)")
  55. assert(type(zz) == "number", "new: Wrong argument type for z (<number> expected)")
  56. return new(xx, yy, zz)
  57. -- number
  58. elseif type(x) == "number" then
  59. return new(x, x, x)
  60. else
  61. return new()
  62. end
  63. end
  64. --- Clone a vector.
  65. -- @tparam vec3 a Vector to be cloned
  66. -- @treturn vec3 out
  67. function vec3.clone(a)
  68. return new(a.x, a.y, a.z)
  69. end
  70. --- Add two vectors.
  71. -- @tparam vec3 a Left hand operant
  72. -- @tparam vec3 b Right hand operant
  73. -- @treturn vec3 out
  74. function vec3.add(a, b)
  75. return new(
  76. a.x + b.x,
  77. a.y + b.y,
  78. a.z + b.z
  79. )
  80. end
  81. --- Subtract one vector from another.
  82. -- @tparam vec3 a Left hand operant
  83. -- @tparam vec3 b Right hand operant
  84. -- @treturn vec3 out
  85. function vec3.sub(a, b)
  86. return new(
  87. a.x - b.x,
  88. a.y - b.y,
  89. a.z - b.z
  90. )
  91. end
  92. --- Multiply a vector by another vectorr.
  93. -- @tparam vec3 a Left hand operant
  94. -- @tparam vec3 b Right hand operant
  95. -- @treturn vec3 out
  96. function vec3.mul(a, b)
  97. return new(
  98. a.x * b.x,
  99. a.y * b.y,
  100. a.z * b.z
  101. )
  102. end
  103. --- Divide a vector by a scalar.
  104. -- @tparam vec3 a Left hand operant
  105. -- @tparam vec3 b Right hand operant
  106. -- @treturn vec3 out
  107. function vec3.div(a, b)
  108. return new(
  109. a.x / b.x,
  110. a.y / b.y,
  111. a.z / b.z
  112. )
  113. end
  114. --- Get the normal of a vector.
  115. -- @tparam vec3 a Vector to normalize
  116. -- @treturn vec3 out
  117. function vec3.normalize(a)
  118. if a:is_zero() then
  119. return new()
  120. end
  121. return a:scale(1 / a:len())
  122. end
  123. --- Trim a vector to a given length
  124. -- @tparam vec3 a Vector to be trimmed
  125. -- @tparam number len Length to trim the vector to
  126. -- @treturn vec3 out
  127. function vec3.trim(a, len)
  128. return a:normalize():scale(math.min(a:len(), len))
  129. end
  130. --- Get the cross product of two vectors.
  131. -- @tparam vec3 a Left hand operant
  132. -- @tparam vec3 b Right hand operant
  133. -- @treturn vec3 out
  134. function vec3.cross(a, b)
  135. return new(
  136. a.y * b.z - a.z * b.y,
  137. a.z * b.x - a.x * b.z,
  138. a.x * b.y - a.y * b.x
  139. )
  140. end
  141. --- Get the dot product of two vectors.
  142. -- @tparam vec3 a Left hand operant
  143. -- @tparam vec3 b Right hand operant
  144. -- @treturn number dot
  145. function vec3.dot(a, b)
  146. return a.x * b.x + a.y * b.y + a.z * b.z
  147. end
  148. --- Get the length of a vector.
  149. -- @tparam vec3 a Vector to get the length of
  150. -- @treturn number len
  151. function vec3.len(a)
  152. return sqrt(a.x * a.x + a.y * a.y + a.z * a.z)
  153. end
  154. --- Get the squared length of a vector.
  155. -- @tparam vec3 a Vector to get the squared length of
  156. -- @treturn number len
  157. function vec3.len2(a)
  158. return a.x * a.x + a.y * a.y + a.z * a.z
  159. end
  160. --- Get the distance between two vectors.
  161. -- @tparam vec3 a Left hand operant
  162. -- @tparam vec3 b Right hand operant
  163. -- @treturn number dist
  164. function vec3.dist(a, b)
  165. local dx = a.x - b.x
  166. local dy = a.y - b.y
  167. local dz = a.z - b.z
  168. return sqrt(dx * dx + dy * dy + dz * dz)
  169. end
  170. --- Get the squared distance between two vectors.
  171. -- @tparam vec3 a Left hand operant
  172. -- @tparam vec3 b Right hand operant
  173. -- @treturn number dist
  174. function vec3.dist2(a, b)
  175. local dx = a.x - b.x
  176. local dy = a.y - b.y
  177. local dz = a.z - b.z
  178. return dx * dx + dy * dy + dz * dz
  179. end
  180. --- Scale a vector by a scalar.
  181. -- @tparam vec3 a Left hand operant
  182. -- @tparam number b Right hand operant
  183. -- @treturn vec3 out
  184. function vec3.scale(a, b)
  185. return new(
  186. a.x * b,
  187. a.y * b,
  188. a.z * b
  189. )
  190. end
  191. --- Rotate vector about an axis.
  192. -- @tparam vec3 a Vector to rotate
  193. -- @tparam number phi Angle to rotate vector by (in radians)
  194. -- @tparam vec3 axis Axis to rotate by
  195. -- @treturn vec3 out
  196. function vec3.rotate(a, phi, axis)
  197. if not vec3.is_vec3(axis) then
  198. return a
  199. end
  200. local u = axis:normalize()
  201. local c = cos(phi)
  202. local s = sin(phi)
  203. -- Calculate generalized rotation matrix
  204. local m1 = new((c + u.x * u.x * (1 - c)), (u.x * u.y * (1 - c) - u.z * s), (u.x * u.z * (1 - c) + u.y * s))
  205. local m2 = new((u.y * u.x * (1 - c) + u.z * s), (c + u.y * u.y * (1 - c)), (u.y * u.z * (1 - c) - u.x * s))
  206. local m3 = new((u.z * u.x * (1 - c) - u.y * s), (u.z * u.y * (1 - c) + u.x * s), (c + u.z * u.z * (1 - c)) )
  207. return new(
  208. a:dot(m1),
  209. a:dot(m2),
  210. a:dot(m3)
  211. )
  212. end
  213. --- Get the perpendicular vector of a vector.
  214. -- @tparam vec3 a Vector to get perpendicular axes from
  215. -- @treturn vec3 out
  216. function vec3.perpendicular(a)
  217. return new(-a.y, a.x, 0)
  218. end
  219. --- Lerp between two vectors.
  220. -- @tparam vec3 a Left hand operant
  221. -- @tparam vec3 b Right hand operant
  222. -- @tparam number s Step value
  223. -- @treturn vec3 out
  224. function vec3.lerp(a, b, s)
  225. return a + (b - a) * s
  226. end
  227. --- Unpack a vector into individual components.
  228. -- @tparam vec3 a Vector to unpack
  229. -- @treturn number x
  230. -- @treturn number y
  231. -- @treturn number z
  232. function vec3.unpack(a)
  233. return a.x, a.y, a.z
  234. end
  235. --- Return a boolean showing if a table is or is not a vec3.
  236. -- @tparam vec3 a Vector to be tested
  237. -- @treturn boolean is_vec3
  238. function vec3.is_vec3(a)
  239. if type(a) == "cdata" then
  240. return ffi.istype("cpml_vec3", a)
  241. end
  242. return
  243. type(a) == "table" and
  244. type(a.x) == "number" and
  245. type(a.y) == "number" and
  246. type(a.z) == "number"
  247. end
  248. --- Return a boolean showing if a table is or is not a zero vec3.
  249. -- @tparam vec3 a Vector to be tested
  250. -- @treturn boolean is_zero
  251. function vec3.is_zero(a)
  252. return a.x == 0 and a.y == 0 and a.z == 0
  253. end
  254. --- Return a formatted string.
  255. -- @tparam vec3 a Vector to be turned into a string
  256. -- @treturn string formatted
  257. function vec3.to_string(a)
  258. return string.format("(%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z)
  259. end
  260. vec3_mt.__index = vec3
  261. vec3_mt.__tostring = vec3.to_string
  262. function vec3_mt.__call(_, x, y, z)
  263. return vec3.new(x, y, z)
  264. end
  265. function vec3_mt.__unm(a)
  266. return new(-a.x, -a.y, -a.z)
  267. end
  268. function vec3_mt.__eq(a, b)
  269. if not vec3.is_vec3(a) or not vec3.is_vec3(b) then
  270. return false
  271. end
  272. return a.x == b.x and a.y == b.y and a.z == b.z
  273. end
  274. function vec3_mt.__add(a, b)
  275. assert(vec3.is_vec3(a), "__add: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
  276. assert(vec3.is_vec3(b), "__add: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
  277. return a:add(b)
  278. end
  279. function vec3_mt.__sub(a, b)
  280. assert(vec3.is_vec3(a), "__sub: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
  281. assert(vec3.is_vec3(b), "__sub: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
  282. return a:sub(b)
  283. end
  284. function vec3_mt.__mul(a, b)
  285. assert(vec3.is_vec3(a), "__mul: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
  286. assert(vec3.is_vec3(b) or type(b) == "number", "__mul: Wrong argument type for right hand operant. (<cpml.vec3> or <number> expected)")
  287. if vec3.is_vec3(b) then
  288. return a:mul(b)
  289. end
  290. return a:scale(b)
  291. end
  292. function vec3_mt.__div(a, b)
  293. assert(vec3.is_vec3(a), "__div: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
  294. assert(vec3.is_vec3(b) or type(b) == "number", "__div: Wrong argument type for right hand operant. (<cpml.vec3> or <number> expected)")
  295. if vec3.is_vec3(b) then
  296. return a:div(b)
  297. end
  298. return a:scale(1 / b)
  299. end
  300. if status then
  301. ffi.metatype(new, vec3_mt)
  302. end
  303. return setmetatable({}, vec3_mt)