• 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.58
/modules/vec2.lua
1
--- A 2 component vector.
2
-- @module vec2
3

4
local vec3    = require(modules .. "vec3")
3✔
5
local precond = require(modules .. "_private_precond")
×
6
local private = require(modules .. "_private_utils")
×
7
local acos    = math.acos
×
8
local atan2   = math.atan2 or math.atan
×
9
local sqrt    = math.sqrt
×
10
local cos     = math.cos
×
11
local sin     = math.sin
×
12
local vec2    = {}
×
13
local vec2_mt = {}
×
14

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

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

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

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

55
                return new(x, y)
×
56

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

63
                return new(xx, yy)
×
64

65
        -- number
66
        elseif type(x) == "number" then
×
67
                return new(x, x)
×
68
        else
69
                return new()
×
70
        end
71
end
72

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

271
        return 0
×
272
end
273

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

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

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

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

307

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

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

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

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

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

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

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

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

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

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

385
vec2_mt.__index    = vec2
×
386
vec2_mt.__tostring = vec2.to_string
×
387

388
function vec2_mt.__call(_, x, y)
×
389
        return vec2.new(x, y)
×
390
end
391

392
function vec2_mt.__unm(a)
×
393
        return new(-a.x, -a.y)
×
394
end
395

396
function vec2_mt.__eq(a, b)
×
397
        if not vec2.is_vec2(a) or not vec2.is_vec2(b) then
×
398
                return false
×
399
        end
400
        return a.x == b.x and a.y == b.y
×
401
end
402

403
function vec2_mt.__add(a, b)
×
404
        precond.assert(vec2.is_vec2(a), "__add: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
×
405
        precond.assert(vec2.is_vec2(b), "__add: Wrong argument type '%s' for right hand operand. (<cpml.vec2> expected)", type(b))
×
406
        return a:add(b)
×
407
end
408

409
function vec2_mt.__sub(a, b)
×
410
        precond.assert(vec2.is_vec2(a), "__add: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
×
411
        precond.assert(vec2.is_vec2(b), "__add: Wrong argument type '%s' for right hand operand. (<cpml.vec2> expected)", type(b))
×
412
        return a:sub(b)
×
413
end
414

415
function vec2_mt.__mul(a, b)
×
416
        precond.assert(vec2.is_vec2(a), "__mul: Wrong argument type '%s' for left hand operand. (<cpml.vec2> expected)", type(a))
×
417
        assert(vec2.is_vec2(b) or type(b) == "number", "__mul: Wrong argument type for right hand operand. (<cpml.vec2> or <number> expected)")
×
418

419
        if vec2.is_vec2(b) then
×
420
                return a:mul(b)
×
421
        end
422

423
        return a:scale(b)
×
424
end
425

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

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

434
        return a:scale(1 / b)
×
435
end
436

437
if status then
×
438
        xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
×
439
                ffi.metatype(new, vec2_mt)
×
440
        end, function() end)
×
441
end
442

443
return setmetatable({}, vec2_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