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

excessive / cpml / 6132372428

09 Sep 2023 05:51PM UTC coverage: 58.701% (+0.6%) from 58.146%
6132372428

push

github

web-flow
Merge pull request #1 from mcclure/euler-quat

quat.to_euler_angles and quat.to_euler_angles_unpack

14 of 14 new or added lines in 1 file covered. (100.0%)

4662 of 7942 relevant lines covered (58.7%)

165.78 hits per line

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

93.51
/modules/vec2.lua
1
--- A 2 component vector.
2
-- @module vec2
3

4
local modules = (...):gsub('%.[^%.]+$', '') .. "."
24✔
5
local vec3    = require(modules .. "vec3")
24✔
6
local precond = require(modules .. "_private_precond")
24✔
7
local private = require(modules .. "_private_utils")
24✔
8
local acos    = math.acos
24✔
9
local atan2   = math.atan2 or math.atan
24✔
10
local sqrt    = math.sqrt
24✔
11
local cos     = math.cos
24✔
12
local sin     = math.sin
24✔
13
local vec2    = {}
24✔
14
local vec2_mt = {}
24✔
15

16
-- Private constructor.
17
local function new(x, y)
18
        return setmetatable({
548✔
19
                x = x or 0,
274✔
20
                y = y or 0
274✔
21
        }, vec2_mt)
548✔
22
end
23

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

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

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

56
                return new(x, y)
336✔
57

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

64
                return new(xx, yy)
12✔
65

66
        -- number
67
        elseif type(x) == "number" then
12✔
68
                return new(x, x)
6✔
69
        else
70
                return new()
6✔
71
        end
72
end
73

74
--- Convert point from polar to cartesian.
75
-- @tparam number radius Radius of the point
76
-- @tparam number theta Angle of the point (in radians)
77
-- @treturn vec2 out
78
function vec2.from_cartesian(radius, theta)
24✔
79
        return new(radius * cos(theta), radius * sin(theta))
3✔
80
end
81

82
--- Clone a vector.
83
-- @tparam vec2 a Vector to be cloned
84
-- @treturn vec2 out
85
function vec2.clone(a)
24✔
86
        return new(a.x, a.y)
213✔
87
end
88

89
--- Add two vectors.
90
-- @tparam vec2 a Left hand operand
91
-- @tparam vec2 b Right hand operand
92
-- @treturn vec2 out
93
function vec2.add(a, b)
24✔
94
        return new(
56✔
95
                a.x + b.x,
42✔
96
                a.y + b.y
42✔
97
        )
28✔
98
end
99

100
--- Subtract one vector from another.
101
-- Order: If a and b are positions, computes the direction and distance from b
102
-- to a.
103
-- @tparam vec2 a Left hand operand
104
-- @tparam vec2 b Right hand operand
105
-- @treturn vec2 out
106
function vec2.sub(a, b)
24✔
107
        return new(
36✔
108
                a.x - b.x,
27✔
109
                a.y - b.y
27✔
110
        )
18✔
111
end
112

113
--- Multiply a vector by another vector.
114
-- Component-size multiplication not matrix multiplication.
115
-- @tparam vec2 a Left hand operand
116
-- @tparam vec2 b Right hand operand
117
-- @treturn vec2 out
118
function vec2.mul(a, b)
24✔
119
        return new(
×
120
                a.x * b.x,
×
121
                a.y * b.y
×
122
        )
123
end
124

125
--- Divide a vector by another vector.
126
-- Component-size inv multiplication. Like a non-uniform scale().
127
-- @tparam vec2 a Left hand operand
128
-- @tparam vec2 b Right hand operand
129
-- @treturn vec2 out
130
function vec2.div(a, b)
24✔
131
        return new(
8✔
132
                a.x / b.x,
6✔
133
                a.y / b.y
6✔
134
        )
4✔
135
end
136

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

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

155
--- Get the cross product of two vectors.
156
-- Order: Positive if a is clockwise from b. Magnitude is the area spanned by
157
-- the parallelograms that a and b span.
158
-- @tparam vec2 a Left hand operand
159
-- @tparam vec2 b Right hand operand
160
-- @treturn number magnitude
161
function vec2.cross(a, b)
24✔
162
        return a.x * b.y - a.y * b.x
3✔
163
end
164

165
--- Get the dot product of two vectors.
166
-- @tparam vec2 a Left hand operand
167
-- @tparam vec2 b Right hand operand
168
-- @treturn number dot
169
function vec2.dot(a, b)
24✔
170
        return a.x * b.x + a.y * b.y
51✔
171
end
172

173
--- Get the length of a vector.
174
-- @tparam vec2 a Vector to get the length of
175
-- @treturn number len
176
function vec2.len(a)
24✔
177
        return sqrt(a.x * a.x + a.y * a.y)
114✔
178
end
179

180
--- Get the squared length of a vector.
181
-- @tparam vec2 a Vector to get the squared length of
182
-- @treturn number len
183
function vec2.len2(a)
24✔
184
        return a.x * a.x + a.y * a.y
3✔
185
end
186

187
--- Get the distance between two vectors.
188
-- @tparam vec2 a Left hand operand
189
-- @tparam vec2 b Right hand operand
190
-- @treturn number dist
191
function vec2.dist(a, b)
24✔
192
        local dx = a.x - b.x
3✔
193
        local dy = a.y - b.y
3✔
194
        return sqrt(dx * dx + dy * dy)
3✔
195
end
196

197
--- Get the squared distance between two vectors.
198
-- @tparam vec2 a Left hand operand
199
-- @tparam vec2 b Right hand operand
200
-- @treturn number dist
201
function vec2.dist2(a, b)
24✔
202
        local dx = a.x - b.x
3✔
203
        local dy = a.y - b.y
3✔
204
        return dx * dx + dy * dy
3✔
205
end
206

207
--- Scale a vector by a scalar.
208
-- @tparam vec2 a Left hand operand
209
-- @tparam number b Right hand operand
210
-- @treturn vec2 out
211
function vec2.scale(a, b)
24✔
212
        return new(
44✔
213
                a.x * b,
33✔
214
                a.y * b
33✔
215
        )
22✔
216
end
217

218
--- Rotate a vector.
219
-- @tparam vec2 a Vector to rotate
220
-- @tparam number phi Angle to rotate vector by (in radians)
221
-- @treturn vec2 out
222
function vec2.rotate(a, phi)
24✔
223
        local c = cos(phi)
6✔
224
        local s = sin(phi)
6✔
225
        return new(
8✔
226
                c * a.x - s * a.y,
6✔
227
                s * a.x + c * a.y
6✔
228
        )
4✔
229
end
230

231
--- Get the perpendicular vector of a vector.
232
-- @tparam vec2 a Vector to get perpendicular axes from
233
-- @treturn vec2 out
234
function vec2.perpendicular(a)
24✔
235
        return new(-a.y, a.x)
3✔
236
end
237

238
--- Signed angle from one vector to another.
239
-- Rotations from +x to +y are positive.
240
-- @tparam vec2 a Vector
241
-- @tparam vec2 b Vector
242
-- @treturn number angle in (-pi, pi]
243
function vec2.angle_to(a, b)
24✔
244
        if b then
48✔
245
                local angle = atan2(b.y, b.x) - atan2(a.y, a.x)
48✔
246
                -- convert to (-pi, pi]
247
                if angle > math.pi       then
48✔
248
                        angle = angle - 2 * math.pi
3✔
249
                elseif angle <= -math.pi then
45✔
250
                        angle = angle + 2 * math.pi
9✔
251
                end
252
                return angle
48✔
253
        end
254

255
        return atan2(a.y, a.x)
×
256
end
257

258
--- Unsigned angle between two vectors.
259
-- Directionless and thus commutative.
260
-- @tparam vec2 a Vector
261
-- @tparam vec2 b Vector
262
-- @treturn number angle in [0, pi]
263
function vec2.angle_between(a, b)
24✔
264
        if b then
48✔
265
                if vec2.is_vec2(a) then
80✔
266
                        return acos(a:dot(b) / (a:len() * b:len()))
240✔
267
                end
268

269
                return acos(vec3.dot(a, b) / (vec3.len(a) * vec3.len(b)))
×
270
        end
271

272
        return 0
×
273
end
274

275
--- Lerp between two vectors.
276
-- @tparam vec2 a Left hand operand
277
-- @tparam vec2 b Right hand operand
278
-- @tparam number s Step value
279
-- @treturn vec2 out
280
function vec2.lerp(a, b, s)
24✔
281
        return a + (b - a) * s
9✔
282
end
283

284
--- Unpack a vector into individual components.
285
-- @tparam vec2 a Vector to unpack
286
-- @treturn number x
287
-- @treturn number y
288
function vec2.unpack(a)
24✔
289
        return a.x, a.y
3✔
290
end
291

292
--- Return the component-wise minimum of two vectors.
293
-- @tparam vec2 a Left hand operand
294
-- @tparam vec2 b Right hand operand
295
-- @treturn vec2 A vector where each component is the lesser value for that component between the two given vectors.
296
function vec2.component_min(a, b)
24✔
297
        return new(math.min(a.x, b.x), math.min(a.y, b.y))
27✔
298
end
299

300
--- Return the component-wise maximum of two vectors.
301
-- @tparam vec2 a Left hand operand
302
-- @tparam vec2 b Right hand operand
303
-- @treturn vec2 A vector where each component is the lesser value for that component between the two given vectors.
304
function vec2.component_max(a, b)
24✔
305
        return new(math.max(a.x, b.x), math.max(a.y, b.y))
27✔
306
end
307

308

309
--- Return a boolean showing if a table is or is not a vec2.
310
-- @tparam vec2 a Vector to be tested
311
-- @treturn boolean is_vec2
312
function vec2.is_vec2(a)
24✔
313
        if type(a) == "cdata" then
397✔
314
                return ffi.istype("cpml_vec2", a)
238✔
315
        end
316

317
        return
×
318
                type(a)   == "table"  and
159✔
319
                type(a.x) == "number" and
117✔
320
                type(a.y) == "number"
159✔
321
end
322

323
--- Return a boolean showing if a table is or is not a zero vec2.
324
-- @tparam vec2 a Vector to be tested
325
-- @treturn boolean is_zero
326
function vec2.is_zero(a)
24✔
327
        return a.x == 0 and a.y == 0
9✔
328
end
329

330
--- Return whether either value is NaN
331
-- @tparam vec2 a Vector to be tested
332
-- @treturn boolean if x or y is nan
333
function vec2.has_nan(a)
24✔
334
        return private.is_nan(a.x) or
5✔
335
                private.is_nan(a.y)
3✔
336
end
337

338
--- Convert point from cartesian to polar.
339
-- @tparam vec2 a Vector to convert
340
-- @treturn number radius
341
-- @treturn number theta
342
function vec2.to_polar(a)
24✔
343
        local radius = sqrt(a.x^2 + a.y^2)
3✔
344
        local theta  = atan2(a.y, a.x)
3✔
345
        theta = theta > 0 and theta or theta + 2 * math.pi
3✔
346
        return radius, theta
3✔
347
end
348

349
-- Round all components to nearest int (or other precision).
350
-- @tparam vec2 a Vector to round.
351
-- @tparam precision Digits after the decimal (integer if unspecified)
352
-- @treturn vec2 Rounded vector
353
function vec2.round(a, precision)
24✔
354
        return vec2.new(private.round(a.x, precision), private.round(a.y, precision))
21✔
355
end
356

357
-- Negate x axis only of vector.
358
-- @tparam vec2 a Vector to x-flip.
359
-- @treturn vec2 x-flipped vector
360
function vec2.flip_x(a)
24✔
361
        return vec2.new(-a.x, a.y)
3✔
362
end
363

364
-- Negate y axis only of vector.
365
-- @tparam vec2 a Vector to y-flip.
366
-- @treturn vec2 y-flipped vector
367
function vec2.flip_y(a)
24✔
368
        return vec2.new(a.x, -a.y)
3✔
369
end
370

371
-- Convert vec2 to vec3.
372
-- @tparam vec2 a Vector to convert.
373
-- @tparam number the new z component, or nil for 0
374
-- @treturn vec3 Converted vector
375
function vec2.to_vec3(a, z)
24✔
376
        return vec3(a.x, a.y, z or 0)
6✔
377
end
378

379
--- Return a formatted string.
380
-- @tparam vec2 a Vector to be turned into a string
381
-- @treturn string formatted
382
function vec2.to_string(a)
24✔
383
        return string.format("(%+0.3f,%+0.3f)", a.x, a.y)
3✔
384
end
385

386
--provide basic compatibility with other vector libraries
387
local vec_enum = {
24✔
388
        "x",
8✔
389
        "y"
390
}
8✔
391
vec2_mt.__index    = function(vec, i)
392
        if vec2[i] then return vec2[i] end
732✔
393
        if type(i) == "number" and i < 3 then
×
394
                return vec[vec_enum[i]]
×
395
        end
396
end
397
vec2_mt.__tostring = vec2.to_string
24✔
398

399
function vec2_mt.__call(_, x, y)
24✔
400
        return vec2.new(x, y)
342✔
401
end
402

403
function vec2_mt.__unm(a)
24✔
404
        return new(-a.x, -a.y)
3✔
405
end
406

407
function vec2_mt.__eq(a, b)
24✔
408
        if not vec2.is_vec2(a) or not vec2.is_vec2(b) then
174✔
409
                return false
×
410
        end
411
        return a.x == b.x and a.y == b.y
74✔
412
end
413

414
function vec2_mt.__add(a, b)
24✔
415
        precond.assert(vec2.is_vec2(a), "__add: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
65✔
416
        precond.assert(vec2.is_vec2(b), "__add: Wrong argument type '%s' for right hand operand. (<cpml.vec2> expected)", type(b))
65✔
417
        return a:add(b)
65✔
418
end
419

420
function vec2_mt.__sub(a, b)
24✔
421
        precond.assert(vec2.is_vec2(a), "__add: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
40✔
422
        precond.assert(vec2.is_vec2(b), "__add: Wrong argument type '%s' for right hand operand. (<cpml.vec2> expected)", type(b))
40✔
423
        return a:sub(b)
40✔
424
end
425

426
function vec2_mt.__mul(a, b)
24✔
427
        precond.assert(vec2.is_vec2(a), "__mul: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
10✔
428
        assert(vec2.is_vec2(b) or type(b) == "number", "__mul: Wrong argument type for right hand operand. (<cpml.vec2> or <number> expected)")
10✔
429

430
        if vec2.is_vec2(b) then
10✔
431
                return a:mul(b)
×
432
        end
433

434
        return a:scale(b)
10✔
435
end
436

437
function vec2_mt.__div(a, b)
24✔
438
        precond.assert(vec2.is_vec2(a), "__div: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
30✔
439
        assert(vec2.is_vec2(b) or type(b) == "number", "__div: Wrong argument type for right hand operand. (<cpml.vec2> or <number> expected)")
30✔
440

441
        if vec2.is_vec2(b) then
30✔
442
                return a:div(b)
5✔
443
        end
444

445
        return a:scale(1 / b)
25✔
446
end
447

448
if status then
24✔
449
        xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
32✔
450
                ffi.metatype(new, vec2_mt)
16✔
451
        end, function() end)
32✔
452
end
453

454
return setmetatable({}, vec2_mt)
24✔
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