• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excessive / cpml / 6132588926

09 Sep 2023 06:38PM UTC coverage: 14.013% (-44.7%) from 58.701%
6132588926

push

github

FatalError42O
fixed Busted support (hopefully)

975 of 6958 relevant lines covered (14.01%)

10.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.56
/modules/vec3.lua
1
--- A 3 component vector.
2
-- @module vec3
3

4
local precond = require(modules .. "_private_precond")
6✔
5
local private = require(modules .. "_private_utils")
×
6
local sqrt    = math.sqrt
×
7
local cos     = math.cos
×
8
local sin     = math.sin
×
9
local vec3    = {}
×
10
local vec3_mt = {}
×
11

12
-- Private constructor.
13
local function new(x, y, z)
14
        return setmetatable({
×
15
                x = x or 0,
16
                y = y or 0,
17
                z = z or 0
×
18
        }, vec3_mt)
×
19
end
20

21
-- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
22
local status, ffi
23
if type(jit) == "table" and jit.status() then
×
24
        status, ffi = pcall(require, "ffi")
×
25
        if status then
×
26
                ffi.cdef "typedef struct { double x, y, z;} cpml_vec3;"
×
27
                new = ffi.typeof("cpml_vec3")
×
28
        end
29
end
30

31
--- Constants
32
-- @table vec3
33
-- @field unit_x X axis of rotation
34
-- @field unit_y Y axis of rotation
35
-- @field unit_z Z axis of rotation
36
-- @field zero Empty vector
37
vec3.unit_x = new(1, 0, 0)
×
38
vec3.unit_y = new(0, 1, 0)
×
39
vec3.unit_z = new(0, 0, 1)
×
40
vec3.zero   = new(0, 0, 0)
×
41

42
--- The public constructor.
43
-- @param x Can be of three types: </br>
44
-- number X component
45
-- table {x, y, z} or {x=x, y=y, z=z}
46
-- scalar To fill the vector eg. {x, x, x}
47
-- @tparam number y Y component
48
-- @tparam number z Z component
49
-- @treturn vec3 out
50
function vec3.new(x, y, z)
×
51
        -- number, number, number
52
        if x and y and z then
×
53
                precond.typeof(x, "number", "new: Wrong argument type for x")
×
54
                precond.typeof(y, "number", "new: Wrong argument type for y")
×
55
                precond.typeof(z, "number", "new: Wrong argument type for z")
×
56

57
                return new(x, y, z)
×
58

59
        -- {x, y, z} or {x=x, y=y, z=z}
60
        elseif type(x) == "table" or type(x) == "cdata" then -- table in vanilla lua, cdata in luajit
×
61
                local xx, yy, zz = x.x or x[1], x.y or x[2], x.z or x[3]
×
62
                precond.typeof(xx, "number", "new: Wrong argument type for x")
×
63
                precond.typeof(yy, "number", "new: Wrong argument type for y")
×
64
                precond.typeof(zz, "number", "new: Wrong argument type for z")
×
65

66
                return new(xx, yy, zz)
×
67

68
        -- number
69
        elseif type(x) == "number" then
×
70
                return new(x, x, x)
×
71
        else
72
                return new()
×
73
        end
74
end
75

76
--- Clone a vector.
77
-- @tparam vec3 a Vector to be cloned
78
-- @treturn vec3 out
79
function vec3.clone(a)
×
80
        return new(a.x, a.y, a.z)
×
81
end
82

83
--- Add two vectors.
84
-- @tparam vec3 a Left hand operand
85
-- @tparam vec3 b Right hand operand
86
-- @treturn vec3 out
87
function vec3.add(a, b)
×
88
        return new(
×
89
                a.x + b.x,
×
90
                a.y + b.y,
×
91
                a.z + b.z
×
92
        )
93
end
94

95
--- Subtract one vector from another.
96
-- Order: If a and b are positions, computes the direction and distance from b
97
-- to a.
98
-- @tparam vec3 a Left hand operand
99
-- @tparam vec3 b Right hand operand
100
-- @treturn vec3 out
101
function vec3.sub(a, b)
×
102
        return new(
×
103
                a.x - b.x,
×
104
                a.y - b.y,
×
105
                a.z - b.z
×
106
        )
107
end
108

109
--- Multiply a vector by another vector.
110
-- Component-wise multiplication not matrix multiplication.
111
-- @tparam vec3 a Left hand operand
112
-- @tparam vec3 b Right hand operand
113
-- @treturn vec3 out
114
function vec3.mul(a, b)
×
115
        return new(
×
116
                a.x * b.x,
×
117
                a.y * b.y,
×
118
                a.z * b.z
×
119
        )
120
end
121

122
--- Divide a vector by another.
123
-- Component-wise inv multiplication. Like a non-uniform scale().
124
-- @tparam vec3 a Left hand operand
125
-- @tparam vec3 b Right hand operand
126
-- @treturn vec3 out
127
function vec3.div(a, b)
×
128
        return new(
×
129
                a.x / b.x,
×
130
                a.y / b.y,
×
131
                a.z / b.z
×
132
        )
133
end
134

135
--- Scale a vector to unit length (1).
136
-- @tparam vec3 a vector to normalize
137
-- @treturn vec3 out
138
function vec3.normalize(a)
×
139
        if a:is_zero() then
×
140
                return new()
×
141
        end
142
        return a:scale(1 / a:len())
×
143
end
144

145
--- Scale a vector to unit length (1), and return the input length.
146
-- @tparam vec3 a vector to normalize
147
-- @treturn vec3 out
148
-- @treturn number input vector length
149
function vec3.normalize_len(a)
×
150
        if a:is_zero() then
×
151
                return new(), 0
×
152
        end
153
        local len = a:len()
×
154
        return a:scale(1 / len), len
×
155
end
156

157
--- Trim a vector to a given length
158
-- @tparam vec3 a vector to be trimmed
159
-- @tparam number len Length to trim the vector to
160
-- @treturn vec3 out
161
function vec3.trim(a, len)
×
162
        return a:normalize():scale(math.min(a:len(), len))
×
163
end
164

165
--- Get the cross product of two vectors.
166
-- Resulting direction is right-hand rule normal of plane defined by a and b.
167
-- Magnitude is the area spanned by the parallelograms that a and b span.
168
-- Order: Direction determined by right-hand rule.
169
-- @tparam vec3 a Left hand operand
170
-- @tparam vec3 b Right hand operand
171
-- @treturn vec3 out
172
function vec3.cross(a, b)
×
173
        return new(
×
174
                a.y * b.z - a.z * b.y,
×
175
                a.z * b.x - a.x * b.z,
×
176
                a.x * b.y - a.y * b.x
×
177
        )
178
end
179

180
--- Get the dot product of two vectors.
181
-- @tparam vec3 a Left hand operand
182
-- @tparam vec3 b Right hand operand
183
-- @treturn number dot
184
function vec3.dot(a, b)
×
185
        return a.x * b.x + a.y * b.y + a.z * b.z
×
186
end
187

188
--- Get the length of a vector.
189
-- @tparam vec3 a Vector to get the length of
190
-- @treturn number len
191
function vec3.len(a)
×
192
        return sqrt(a.x * a.x + a.y * a.y + a.z * a.z)
×
193
end
194

195
--- Get the squared length of a vector.
196
-- @tparam vec3 a Vector to get the squared length of
197
-- @treturn number len
198
function vec3.len2(a)
×
199
        return a.x * a.x + a.y * a.y + a.z * a.z
×
200
end
201

202
--- Get the distance between two vectors.
203
-- @tparam vec3 a Left hand operand
204
-- @tparam vec3 b Right hand operand
205
-- @treturn number dist
206
function vec3.dist(a, b)
×
207
        local dx = a.x - b.x
×
208
        local dy = a.y - b.y
×
209
        local dz = a.z - b.z
×
210
        return sqrt(dx * dx + dy * dy + dz * dz)
×
211
end
212

213
--- Get the squared distance between two vectors.
214
-- @tparam vec3 a Left hand operand
215
-- @tparam vec3 b Right hand operand
216
-- @treturn number dist
217
function vec3.dist2(a, b)
×
218
        local dx = a.x - b.x
×
219
        local dy = a.y - b.y
×
220
        local dz = a.z - b.z
×
221
        return dx * dx + dy * dy + dz * dz
×
222
end
223

224
--- Scale a vector by a scalar.
225
-- @tparam vec3 a Left hand operand
226
-- @tparam number b Right hand operand
227
-- @treturn vec3 out
228
function vec3.scale(a, b)
×
229
        return new(
×
230
                a.x * b,
×
231
                a.y * b,
×
232
                a.z * b
×
233
        )
234
end
235

236
--- Rotate vector about an axis.
237
-- @tparam vec3 a Vector to rotate
238
-- @tparam number phi Angle to rotate vector by (in radians)
239
-- @tparam vec3 axis Axis to rotate by
240
-- @treturn vec3 out
241
function vec3.rotate(a, phi, axis)
×
242
        if not vec3.is_vec3(axis) then
×
243
                return a
×
244
        end
245

246
        local u = axis:normalize()
×
247
        local c = cos(phi)
×
248
        local s = sin(phi)
×
249

250
        -- Calculate generalized rotation matrix
251
        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))
×
252
        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))
×
253
        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))      )
×
254

255
        return new(
×
256
                a:dot(m1),
×
257
                a:dot(m2),
×
258
                a:dot(m3)
×
259
        )
260
end
261

262
--- Get the perpendicular vector of a vector.
263
-- @tparam vec3 a Vector to get perpendicular axes from
264
-- @treturn vec3 out
265
function vec3.perpendicular(a)
×
266
        return new(-a.y, a.x, 0)
×
267
end
268

269
--- Lerp between two vectors.
270
-- @tparam vec3 a Left hand operand
271
-- @tparam vec3 b Right hand operand
272
-- @tparam number s Step value
273
-- @treturn vec3 out
274
function vec3.lerp(a, b, s)
×
275
        return a + (b - a) * s
×
276
end
277

278
-- Round all components to nearest int (or other precision).
279
-- @tparam vec3 a Vector to round.
280
-- @tparam precision Digits after the decimal (round numebr if unspecified)
281
-- @treturn vec3 Rounded vector
282
function vec3.round(a, precision)
×
283
        return vec3.new(private.round(a.x, precision), private.round(a.y, precision), private.round(a.z, precision))
×
284
end
285

286
--- Unpack a vector into individual components.
287
-- @tparam vec3 a Vector to unpack
288
-- @treturn number x
289
-- @treturn number y
290
-- @treturn number z
291
function vec3.unpack(a)
×
292
        return a.x, a.y, a.z
×
293
end
294

295
--- Return the component-wise minimum of two vectors.
296
-- @tparam vec3 a Left hand operand
297
-- @tparam vec3 b Right hand operand
298
-- @treturn vec3 A vector where each component is the lesser value for that component between the two given vectors.
299
function vec3.component_min(a, b)
×
300
        return new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z))
×
301
end
302

303
--- Return the component-wise maximum of two vectors.
304
-- @tparam vec3 a Left hand operand
305
-- @tparam vec3 b Right hand operand
306
-- @treturn vec3 A vector where each component is the lesser value for that component between the two given vectors.
307
function vec3.component_max(a, b)
×
308
        return new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
×
309
end
310

311
-- Negate x axis only of vector.
312
-- @tparam vec3 a Vector to x-flip.
313
-- @treturn vec3 x-flipped vector
314
function vec3.flip_x(a)
×
315
        return vec3.new(-a.x, a.y, a.z)
×
316
end
317

318
-- Negate y axis only of vector.
319
-- @tparam vec3 a Vector to y-flip.
320
-- @treturn vec3 y-flipped vector
321
function vec3.flip_y(a)
×
322
        return vec3.new(a.x, -a.y, a.z)
×
323
end
324

325
-- Negate z axis only of vector.
326
-- @tparam vec3 a Vector to z-flip.
327
-- @treturn vec3 z-flipped vector
328
function vec3.flip_z(a)
×
329
        return vec3.new(a.x, a.y, -a.z)
×
330
end
331

332
function vec3.angle_to(a, b)
×
333
        local v = a:normalize():dot(b:normalize())
×
334
        return math.acos(v)
×
335
end
336

337
--- Return a boolean showing if a table is or is not a vec3.
338
-- @tparam vec3 a Vector to be tested
339
-- @treturn boolean is_vec3
340
function vec3.is_vec3(a)
×
341
        if type(a) == "cdata" then
×
342
                return ffi.istype("cpml_vec3", a)
×
343
        end
344

345
        return
×
346
                type(a)   == "table"  and
×
347
                type(a.x) == "number" and
×
348
                type(a.y) == "number" and
×
349
                type(a.z) == "number"
×
350
end
351

352
--- Return a boolean showing if a table is or is not a zero vec3.
353
-- @tparam vec3 a Vector to be tested
354
-- @treturn boolean is_zero
355
function vec3.is_zero(a)
×
356
        return a.x == 0 and a.y == 0 and a.z == 0
×
357
end
358

359
--- Return whether any component is NaN
360
-- @tparam vec3 a Vector to be tested
361
-- @treturn boolean if x,y, or z are nan
362
function vec3.has_nan(a)
×
363
        return private.is_nan(a.x) or
×
364
                private.is_nan(a.y) or
×
365
                private.is_nan(a.z)
×
366
end
367

368
--- Return a formatted string.
369
-- @tparam vec3 a Vector to be turned into a string
370
-- @treturn string formatted
371
function vec3.to_string(a)
×
372
        return string.format("(%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z)
×
373
end
374

375
vec3_mt.__index    = vec3
×
376
vec3_mt.__tostring = vec3.to_string
×
377

378
function vec3_mt.__call(_, x, y, z)
×
379
        return vec3.new(x, y, z)
×
380
end
381

382
function vec3_mt.__unm(a)
×
383
        return new(-a.x, -a.y, -a.z)
×
384
end
385

386
function vec3_mt.__eq(a, b)
×
387
        if not vec3.is_vec3(a) or not vec3.is_vec3(b) then
×
388
                return false
×
389
        end
390
        return a.x == b.x and a.y == b.y and a.z == b.z
×
391
end
392

393
function vec3_mt.__add(a, b)
×
394
        precond.assert(vec3.is_vec3(a), "__add: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
×
395
        precond.assert(vec3.is_vec3(b), "__add: Wrong argument type '%s' for right hand operand. (<cpml.vec3> expected)", type(b))
×
396
        return a:add(b)
×
397
end
398

399
function vec3_mt.__sub(a, b)
×
400
        precond.assert(vec3.is_vec3(a), "__sub: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
×
401
        precond.assert(vec3.is_vec3(b), "__sub: Wrong argument type '%s' for right hand operand. (<cpml.vec3> expected)", type(b))
×
402
        return a:sub(b)
×
403
end
404

405
function vec3_mt.__mul(a, b)
×
406
        precond.assert(vec3.is_vec3(a), "__mul: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
×
407
        precond.assert(vec3.is_vec3(b) or type(b) == "number", "__mul: Wrong argument type '%s' for right hand operand. (<cpml.vec3> or <number> expected)", type(b))
×
408

409
        if vec3.is_vec3(b) then
×
410
                return a:mul(b)
×
411
        end
412

413
        return a:scale(b)
×
414
end
415

416
function vec3_mt.__div(a, b)
×
417
        precond.assert(vec3.is_vec3(a), "__div: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
×
418
        precond.assert(vec3.is_vec3(b) or type(b) == "number", "__div: Wrong argument type '%s' for right hand operand. (<cpml.vec3> or <number> expected)", type(b))
×
419

420
        if vec3.is_vec3(b) then
×
421
                return a:div(b)
×
422
        end
423

424
        return a:scale(1 / b)
×
425
end
426

427
if status then
×
428
        xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
×
429
                ffi.metatype(new, vec3_mt)
×
430
        end, function() end)
×
431
end
432

433
return setmetatable({}, vec3_mt)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc