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

excessive / cpml / 1

21 Apr 2022 05:46PM UTC coverage: 54.321% (+0.7%) from 53.574%
1

push

github

web-flow
Merge pull request #76 from xiejiangzhi/xjz

Add Vec3.angle_to

23 of 23 new or added lines in 2 files covered. (100.0%)

130 existing lines in 7 files now uncovered.

5424 of 9985 relevant lines covered (54.32%)

290.75 hits per line

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

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

4
local modules = (...):gsub('%.[^%.]+$', '') .. "."
77✔
5
local precond = require(modules .. "_private_precond")
77✔
6
local private = require(modules .. "_private_utils")
77✔
7
local sqrt    = math.sqrt
77✔
8
local cos     = math.cos
77✔
9
local sin     = math.sin
77✔
10
local vec3    = {}
77✔
11
local vec3_mt = {}
77✔
12

13
-- Private constructor.
14
local function new(x, y, z)
15
        return setmetatable({
8,484✔
16
                x = x or 0,
4,242✔
17
                y = y or 0,
4,242✔
18
                z = z or 0
4,242✔
19
        }, vec3_mt)
8,484✔
20
end
21

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

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

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

58
                return new(x, y, z)
1,435✔
59

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

67
                return new(xx, yy, zz)
42✔
68

69
        -- number
70
        elseif type(x) == "number" then
343✔
71
                return new(x, x, x)
182✔
72
        else
73
                return new()
161✔
74
        end
75
end
76

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

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

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

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

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

136
--- Get the normal of a vector.
137
-- @tparam vec3 a Vector to normalize
138
-- @treturn vec3 out
139
function vec3.normalize(a)
77✔
140
        if a:is_zero() then
273✔
UNCOV
141
                return new()
×
142
        end
143
        return a:scale(1 / a:len())
273✔
144
end
145

146
--- Trim a vector to a given length
147
-- @tparam vec3 a Vector to be trimmed
148
-- @tparam number len Length to trim the vector to
149
-- @treturn vec3 out
150
function vec3.trim(a, len)
77✔
151
        return a:normalize():scale(math.min(a:len(), len))
7✔
152
end
153

154
--- Get the cross product of two vectors.
155
-- Resulting direction is right-hand rule normal of plane defined by a and b.
156
-- Magnitude is the area spanned by the parallelograms that a and b span.
157
-- Order: Direction determined by right-hand rule.
158
-- @tparam vec3 a Left hand operand
159
-- @tparam vec3 b Right hand operand
160
-- @treturn vec3 out
161
function vec3.cross(a, b)
77✔
162
        return new(
364✔
163
                a.y * b.z - a.z * b.y,
182✔
164
                a.z * b.x - a.x * b.z,
182✔
165
                a.x * b.y - a.y * b.x
182✔
166
        )
167
end
168

169
--- Get the dot product of two vectors.
170
-- @tparam vec3 a Left hand operand
171
-- @tparam vec3 b Right hand operand
172
-- @treturn number dot
173
function vec3.dot(a, b)
77✔
174
        return a.x * b.x + a.y * b.y + a.z * b.z
511✔
175
end
176

177
--- Get the length of a vector.
178
-- @tparam vec3 a Vector to get the length of
179
-- @treturn number len
180
function vec3.len(a)
77✔
181
        return sqrt(a.x * a.x + a.y * a.y + a.z * a.z)
378✔
182
end
183

184
--- Get the squared length of a vector.
185
-- @tparam vec3 a Vector to get the squared length of
186
-- @treturn number len
187
function vec3.len2(a)
77✔
188
        return a.x * a.x + a.y * a.y + a.z * a.z
91✔
189
end
190

191
--- Get the distance between two vectors.
192
-- @tparam vec3 a Left hand operand
193
-- @tparam vec3 b Right hand operand
194
-- @treturn number dist
195
function vec3.dist(a, b)
77✔
196
        local dx = a.x - b.x
77✔
197
        local dy = a.y - b.y
77✔
198
        local dz = a.z - b.z
77✔
199
        return sqrt(dx * dx + dy * dy + dz * dz)
77✔
200
end
201

202
--- Get the squared 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.dist2(a, b)
77✔
207
        local dx = a.x - b.x
7✔
208
        local dy = a.y - b.y
7✔
209
        local dz = a.z - b.z
7✔
210
        return dx * dx + dy * dy + dz * dz
7✔
211
end
212

213
--- Scale a vector by a scalar.
214
-- @tparam vec3 a Left hand operand
215
-- @tparam number b Right hand operand
216
-- @treturn vec3 out
217
function vec3.scale(a, b)
77✔
218
        return new(
1,176✔
219
                a.x * b,
588✔
220
                a.y * b,
588✔
221
                a.z * b
588✔
222
        )
223
end
224

225
--- Rotate vector about an axis.
226
-- @tparam vec3 a Vector to rotate
227
-- @tparam number phi Angle to rotate vector by (in radians)
228
-- @tparam vec3 axis Axis to rotate by
229
-- @treturn vec3 out
230
function vec3.rotate(a, phi, axis)
77✔
231
        if not vec3.is_vec3(axis) then
21✔
232
                return a
7✔
233
        end
234

235
        local u = axis:normalize()
14✔
236
        local c = cos(phi)
14✔
237
        local s = sin(phi)
14✔
238

239
        -- Calculate generalized rotation matrix
240
        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))
14✔
241
        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))
14✔
242
        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))      )
14✔
243

244
        return new(
28✔
245
                a:dot(m1),
14✔
246
                a:dot(m2),
14✔
247
                a:dot(m3)
14✔
248
        )
249
end
250

251
--- Get the perpendicular vector of a vector.
252
-- @tparam vec3 a Vector to get perpendicular axes from
253
-- @treturn vec3 out
254
function vec3.perpendicular(a)
77✔
255
        return new(-a.y, a.x, 0)
7✔
256
end
257

258
--- Lerp between two vectors.
259
-- @tparam vec3 a Left hand operand
260
-- @tparam vec3 b Right hand operand
261
-- @tparam number s Step value
262
-- @treturn vec3 out
263
function vec3.lerp(a, b, s)
77✔
264
        return a + (b - a) * s
7✔
265
end
266

267
-- Round all components to nearest int (or other precision).
268
-- @tparam vec3 a Vector to round.
269
-- @tparam precision Digits after the decimal (round numebr if unspecified)
270
-- @treturn vec3 Rounded vector
271
function vec3.round(a, precision)
77✔
272
        return vec3.new(private.round(a.x, precision), private.round(a.y, precision), private.round(a.z, precision))
21✔
273
end
274

275
--- Unpack a vector into individual components.
276
-- @tparam vec3 a Vector to unpack
277
-- @treturn number x
278
-- @treturn number y
279
-- @treturn number z
280
function vec3.unpack(a)
77✔
281
        return a.x, a.y, a.z
21✔
282
end
283

284
--- Return the component-wise minimum of two vectors.
285
-- @tparam vec3 a Left hand operand
286
-- @tparam vec3 b Right hand operand
287
-- @treturn vec3 A vector where each component is the lesser value for that component between the two given vectors.
288
function vec3.component_min(a, b)
77✔
289
        return new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z))
63✔
290
end
291

292
--- Return the component-wise maximum of two vectors.
293
-- @tparam vec3 a Left hand operand
294
-- @tparam vec3 b Right hand operand
295
-- @treturn vec3 A vector where each component is the lesser value for that component between the two given vectors.
296
function vec3.component_max(a, b)
77✔
297
        return new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
63✔
298
end
299

300
-- Negate x axis only of vector.
301
-- @tparam vec2 a Vector to x-flip.
302
-- @treturn vec2 x-flipped vector
303
function vec3.flip_x(a)
77✔
304
        return vec3.new(-a.x, a.y, a.z)
7✔
305
end
306

307
-- Negate y axis only of vector.
308
-- @tparam vec2 a Vector to y-flip.
309
-- @treturn vec2 y-flipped vector
310
function vec3.flip_y(a)
77✔
311
        return vec3.new(a.x, -a.y, a.z)
7✔
312
end
313

314
-- Negate z axis only of vector.
315
-- @tparam vec2 a Vector to z-flip.
316
-- @treturn vec2 z-flipped vector
317
function vec3.flip_z(a)
77✔
318
        return vec3.new(a.x, a.y, -a.z)
7✔
319
end
320

321
function vec3.angle_to(a, b)
77✔
322
        local v = a:normalize():dot(b:normalize())
98✔
323
        return math.acos(v)
98✔
324
end
325

326
--- Return a boolean showing if a table is or is not a vec3.
327
-- @tparam vec3 a Vector to be tested
328
-- @treturn boolean is_vec3
329
function vec3.is_vec3(a)
77✔
330
        if type(a) == "cdata" then
2,849✔
UNCOV
331
                return ffi.istype("cpml_vec3", a)
×
332
        end
333

UNCOV
334
        return
×
335
                type(a)   == "table"  and
2,849✔
336
                type(a.x) == "number" and
2,177✔
337
                type(a.y) == "number" and
2,100✔
338
                type(a.z) == "number"
2,849✔
339
end
340

341
--- Return a boolean showing if a table is or is not a zero vec3.
342
-- @tparam vec3 a Vector to be tested
343
-- @treturn boolean is_zero
344
function vec3.is_zero(a)
77✔
345
        return a.x == 0 and a.y == 0 and a.z == 0
280✔
346
end
347

348
--- Return whether any component is NaN
349
-- @tparam vec3 a Vector to be tested
350
-- @treturn boolean if x,y, or z are nan
351
function vec3.has_nan(a)
77✔
352
        return private.is_nan(a.x) or
7✔
UNCOV
353
                private.is_nan(a.y) or
×
354
                private.is_nan(a.z)
7✔
355
end
356

357
--- Return a formatted string.
358
-- @tparam vec3 a Vector to be turned into a string
359
-- @treturn string formatted
360
function vec3.to_string(a)
77✔
361
        return string.format("(%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z)
7✔
362
end
363

364
vec3_mt.__index    = vec3
77✔
365
vec3_mt.__tostring = vec3.to_string
77✔
366

367
function vec3_mt.__call(_, x, y, z)
77✔
368
        return vec3.new(x, y, z)
1,771✔
369
end
370

371
function vec3_mt.__unm(a)
77✔
372
        return new(-a.x, -a.y, -a.z)
7✔
373
end
374

375
function vec3_mt.__eq(a, b)
77✔
376
        if not vec3.is_vec3(a) or not vec3.is_vec3(b) then
217✔
UNCOV
377
                return false
×
378
        end
379
        return a.x == b.x and a.y == b.y and a.z == b.z
217✔
380
end
381

382
function vec3_mt.__add(a, b)
77✔
383
        precond.assert(vec3.is_vec3(a), "__add: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
301✔
384
        precond.assert(vec3.is_vec3(b), "__add: Wrong argument type '%s' for right hand operand. (<cpml.vec3> expected)", type(b))
301✔
385
        return a:add(b)
301✔
386
end
387

388
function vec3_mt.__sub(a, b)
77✔
389
        precond.assert(vec3.is_vec3(a), "__sub: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
322✔
390
        precond.assert(vec3.is_vec3(b), "__sub: Wrong argument type '%s' for right hand operand. (<cpml.vec3> expected)", type(b))
322✔
391
        return a:sub(b)
322✔
392
end
393

394
function vec3_mt.__mul(a, b)
77✔
395
        precond.assert(vec3.is_vec3(a), "__mul: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
266✔
396
        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))
266✔
397

398
        if vec3.is_vec3(b) then
266✔
UNCOV
399
                return a:mul(b)
×
400
        end
401

402
        return a:scale(b)
266✔
403
end
404

405
function vec3_mt.__div(a, b)
77✔
406
        precond.assert(vec3.is_vec3(a), "__div: Wrong argument type '%s' for left hand operand. (<cpml.vec3> expected)", type(a))
42✔
407
        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))
42✔
408

409
        if vec3.is_vec3(b) then
42✔
410
                return a:div(b)
7✔
411
        end
412

413
        return a:scale(1 / b)
35✔
414
end
415

416
if status then
77✔
UNCOV
417
        xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
×
UNCOV
418
                ffi.metatype(new, vec3_mt)
×
UNCOV
419
        end, function() end)
×
420
end
421

422
return setmetatable({}, vec3_mt)
77✔
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