2021-06-26 23:03:55 +02:00
--- A 3-dimensional vector.
2023-07-02 20:03:46 +02:00
-- @class worldeditadditions_core.Vector3
2021-06-26 15:10:28 +02:00
local Vector3 = { }
Vector3.__index = Vector3
2021-12-31 02:38:24 +01:00
Vector3.__name = " Vector3 " -- A hack to allow identification in wea.inspect
2021-06-26 15:10:28 +02:00
2021-07-03 01:42:19 +02:00
--- Creates a new Vector3 instance.
-- @param x number The x co-ordinate value.
-- @param y number The y co-ordinate value.
-- @param z number The z co-ordinate value.
2021-06-26 15:10:28 +02:00
function Vector3 . new ( x , y , z )
2021-07-05 00:30:29 +02:00
x = x or 0
y = y or 0
z = z or 0
2021-06-26 15:10:28 +02:00
local result = {
x = x ,
y = y ,
z = z
}
setmetatable ( result , Vector3 )
return result
end
2021-06-26 16:23:16 +02:00
--- Returns a new instance of this vector.
-- @param a Vector3 The vector to clone.
-- @returns Vector3 A new vector whose values are identical to those of the original vector.
function Vector3 . clone ( a )
return Vector3.new ( a.x , a.y , a.z )
end
2021-06-26 15:10:28 +02:00
--- Adds the specified vectors or numbers together.
-- Returns the result as a new vector.
-- If 1 of the inputs is a number and the other a vector, then the number will
-- be added to each of the components of the vector.
-- @param a Vector3|number The first item to add.
-- @param a Vector3|number The second item to add.
-- @returns Vector3 The result as a new Vector3 object.
function Vector3 . add ( a , b )
if type ( a ) == " number " then
return Vector3.new ( b.x + a , b.y + a , b.z + a )
elseif type ( b ) == " number " then
return Vector3.new ( a.x + b , a.y + b , a.z + b )
end
return Vector3.new ( a.x + b.x , a.y + b.y , a.z + b.z )
end
--- Subtracts the specified vectors or numbers together.
-- Returns the result as a new vector.
-- If 1 of the inputs is a number and the other a vector, then the number will
-- be subtracted to each of the components of the vector.
-- @param a Vector3|number The first item to subtract.
-- @param a Vector3|number The second item to subtract.
-- @returns Vector3 The result as a new Vector3 object.
function Vector3 . subtract ( a , b )
if type ( a ) == " number " then
return Vector3.new ( b.x - a , b.y - a , b.z - a )
elseif type ( b ) == " number " then
return Vector3.new ( a.x - b , a.y - b , a.z - b )
end
return Vector3.new ( a.x - b.x , a.y - b.y , a.z - b.z )
end
--- Alias for Vector3.subtract.
function Vector3 . sub ( a , b ) return Vector3.subtract ( a , b ) end
--- Multiplies the specified vectors or numbers together.
-- Returns the result as a new vector.
-- If 1 of the inputs is a number and the other a vector, then the number will
-- be multiplied to each of the components of the vector.
2021-08-05 19:41:35 +02:00
--
2021-06-26 15:10:28 +02:00
-- If both of the inputs are vectors, then the components are multiplied
-- by each other (NOT the cross product). In other words:
-- a.x * b.x, a.y * b.y, a.z * b.z
2021-08-05 19:41:35 +02:00
--
2021-06-26 15:10:28 +02:00
-- @param a Vector3|number The first item to multiply.
-- @param a Vector3|number The second item to multiply.
-- @returns Vector3 The result as a new Vector3 object.
function Vector3 . multiply ( a , b )
if type ( a ) == " number " then
return Vector3.new ( b.x * a , b.y * a , b.z * a )
elseif type ( b ) == " number " then
return Vector3.new ( a.x * b , a.y * b , a.z * b )
end
return Vector3.new ( a.x * b.x , a.y * b.y , a.z * b.z )
end
--- Alias for Vector3.multiply.
function Vector3 . mul ( a , b ) return Vector3.multiply ( a , b ) end
--- Divides the specified vectors or numbers together.
-- Returns the result as a new vector.
-- If 1 of the inputs is a number and the other a vector, then the number will
-- be divided to each of the components of the vector.
-- @param a Vector3|number The first item to divide.
-- @param a Vector3|number The second item to divide.
-- @returns Vector3 The result as a new Vector3 object.
function Vector3 . divide ( a , b )
if type ( a ) == " number " then
2022-09-19 01:18:03 +02:00
return Vector3.new ( a / b.x , a / b.y , a / b.z )
-- return Vector3.new(b.x / a, b.y / a, b.z / a) -- BUG: is this a bug?
2021-06-26 15:10:28 +02:00
elseif type ( b ) == " number " then
return Vector3.new ( a.x / b , a.y / b , a.z / b )
end
return Vector3.new ( a.x / b.x , a.y / b.y , a.z / b.z )
end
--- Alias for Vector3.divide.
function Vector3 . div ( a , b ) return Vector3.divide ( a , b ) end
2021-06-26 15:19:18 +02:00
--- Rounds the components of this vector down.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 A new instance with the x/y/z components rounded down.
function Vector3 . floor ( a )
return Vector3.new ( math.floor ( a.x ) , math.floor ( a.y ) , math.floor ( a.z ) )
end
--- Rounds the components of this vector up.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 A new instance with the x/y/z components rounded up.
function Vector3 . ceil ( a )
return Vector3.new ( math.ceil ( a.x ) , math.ceil ( a.y ) , math.ceil ( a.z ) )
end
2021-06-26 15:22:26 +02:00
--- Rounds the components of this vector.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 A new instance with the x/y/z components rounded.
function Vector3 . round ( a )
return Vector3.new ( math.floor ( a.x + 0.5 ) , math.floor ( a.y + 0.5 ) , math.floor ( a.z + 0.5 ) )
end
2021-06-26 15:19:18 +02:00
2023-01-21 00:43:07 +01:00
--- Rounds the components of this vector to the specified number of decimal places.
-- @param a Vector3 The vector to round.
-- @param dp number The number of decimal places to round to.
-- @returns Vector3 A new instance with the components rounded to the specified number of decimal places.
function Vector3 . round_dp ( a , dp )
local power = 10 ^ dp
return ( a * power ) : round ( ) / power
end
2021-06-26 15:40:14 +02:00
--- Snaps this Vector3 to an imaginary square grid with the specified sized
-- squares.
-- @param a Vector3 The vector to operate on.
-- @param number grid_size The size of the squares on the imaginary grid to which to snap.
-- @returns Vector3 A new Vector3 instance snapped to an imaginary grid of the specified size.
function Vector3 . snap_to ( a , grid_size )
2021-06-26 15:44:40 +02:00
return ( a / grid_size ) : round ( ) * grid_size
2021-06-26 15:40:14 +02:00
end
2021-06-26 15:26:40 +02:00
--- Returns the area of this vector.
-- In other words, multiplies all the components together and returns a scalar value.
-- @param a Vector3 The vector to return the area of.
-- @returns number The area of this vector.
function Vector3 . area ( a )
return a.x * a.y * a.z
end
2021-06-26 16:15:32 +02:00
--- Returns the scalar length of this vector squared.
-- @param a Vector3 The vector to operate on.
-- @returns number The length squared of this vector as a scalar value.
function Vector3 . length_squared ( a )
return a.x * a.x + a.y * a.y + a.z * a.z
end
--- Square roots each component of this vector.
-- @param a Vector3 The vector to operate on.
-- @returns number A new vector with each component square rooted.
function Vector3 . sqrt ( a )
return Vector3.new ( math.sqrt ( a.x ) , math.sqrt ( a.y ) , math.sqrt ( a.z ) )
end
--- Calculates the scalar length of this vector.
-- @param a Vector3 The vector to operate on.
-- @returns number The length of this vector as a scalar value.
function Vector3 . length ( a )
return math.sqrt ( a : length_squared ( ) )
end
2023-07-04 23:45:02 +02:00
--- Calculates the volume of the region bounded by 2 points.
2022-09-24 03:33:10 +02:00
-- @param a Vector3 The first point bounding the target region.
-- @param b Vector3 The second point bounding the target region.
-- @returns number The volume of the defined region.
function Vector3 . volume ( a , b )
local pos1 , pos2 = Vector3.sort ( a , b )
local vol = pos2 - pos1
return vol.x * vol.y * vol.z
end
2021-06-26 16:47:53 +02:00
--- Calculates the dot product of this vector and another vector.
-- @param a Vector3 The first vector to operate on.
-- @param a Vector3 The second vector to operate on.
-- @returns number The dot product of this vector as a scalar value.
function Vector3 . dot ( a , b )
return a.x * b.x + a.y * b.y + a.z * b.z ;
end
--- Alias of Vector3.dot.
function Vector3 . dot_product ( a , b )
return Vector3.dot ( a , b )
end
2021-06-26 17:28:39 +02:00
--- Determines if 2 vectors are equal to each other.
-- 2 vectors are equal if their values are identical.
-- @param a Vector3 The first vector to test.
-- @param a Vector3 The second vector to test.
-- @returns bool Whether the 2 vectors are equal or not.
function Vector3 . equals ( a , b )
return a.x == b.x
and a.y == b.y
and a.z == b.z
end
2021-06-26 16:32:47 +02:00
--- Returns a new vector whose length clamped to the given length.
-- The direction in which the vector is pointing is not changed.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 A new Vector3 instance limited to the specified length.
function Vector3 . limit_to ( a , length )
if type ( length ) ~= " number " then error ( " Error: Expected number, but found " .. type ( length ) .. " . " ) end
if a : length ( ) > length then
return ( a / a : length ( ) ) * length
end
return a : clone ( )
end
2021-06-26 16:43:42 +02:00
--- Returns a new vector whose length clamped to the given length.
-- The direction in which the vector is pointing is not changed.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 A new Vector3 instance limited to the specified length.
function Vector3 . set_to ( a , length )
if type ( length ) ~= " number " then error ( " Error: Expected number, but found " .. type ( length ) .. " . " ) end
return ( a / a : length ( ) ) * length
end
2021-06-26 17:56:26 +02:00
--- Returns the unit vector of this vector.
-- The unit vector is a vector with a length of 1.
-- Returns a new vector.
-- Does not change the direction of the vector.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 The unit vector of this vector.
function Vector3 . unit ( a )
return a / a : length ( )
end
--- Alias of Vector3.unit.
function Vector3 . normalise ( a ) return a : unit ( ) end
2021-06-26 16:43:42 +02:00
2021-06-26 16:39:47 +02:00
--- Return a vector that is amount distance towards b from a.
-- @param a Vector3 The vector to move from.
-- @param b Vector3 The vector to move towards.
-- @param amount number The amount to move.
function Vector3 . move_towards ( a , b , amount )
return a + ( b - a ) : limit_to ( amount )
end
2021-06-26 18:02:35 +02:00
--- Returns the value of the minimum component of the vector.
-- Returns a scalar value.
-- @param a Vector3 The vector to operate on.
-- @returns number The value of the minimum component of the vector.
function Vector3 . min_component ( a )
return math.min ( a.x , a.y , a.z )
end
--- Returns the value of the maximum component of the vector.
-- Returns a scalar value.
-- @param a Vector3 The vector to operate on.
-- @returns number The value of the maximum component of the vector.
function Vector3 . max_component ( a )
return math.max ( a.x , a.y , a.z )
end
2021-06-26 18:06:53 +02:00
--- Returns the absolute form of this vector.
-- In other words, it removes the minus sign from all components of the vector.
-- @param a Vector3 The vector to operate on.
-- @returns Vector3 The absolute form of the given vector.
function Vector3 . abs ( a )
return Vector3.new ( math.abs ( a.x ) , math.abs ( a.y ) , math.abs ( a.z ) )
end
2021-06-26 18:48:39 +02:00
--- Sorts the components of the given vectors.
-- pos1 will contain the minimum values, and pos2 the maximum values.
-- Returns 2 new vectors.
2021-08-04 02:41:51 +02:00
-- Note that for this specific function
-- the vectors provided do not *have* to be instances of Vector3.
2021-06-26 18:48:39 +02:00
-- It is only required that they have the keys x, y, and z.
-- Vector3 instances are always returned.
-- This enables convenient ingesting of positions from outside.
-- @param pos1 Vector3 The first vector to operate on.
-- @param pos2 Vector3 The second vector to operate on.
2021-08-05 19:41:35 +02:00
-- @returns Vector3,Vector3 The 2 sorted vectors (min, max).
2021-06-26 18:48:39 +02:00
function Vector3 . sort ( pos1 , pos2 )
2022-09-25 17:16:03 +02:00
-- Cloning is important because Vector3's API does not mutate
2021-06-26 18:48:39 +02:00
local pos1_new = Vector3.clone ( pos1 ) -- This way we can accept non-Vector3 instances
local pos2_new = Vector3.clone ( pos2 ) -- This way we can accept non-Vector3 instances
if pos1_new.x > pos2_new.x then
pos1_new.x , pos2_new.x = pos2_new.x , pos1_new.x
end
if pos1_new.y > pos2_new.y then
pos1_new.y , pos2_new.y = pos2_new.y , pos1_new.y
end
if pos1_new.z > pos2_new.z then
pos1_new.z , pos2_new.z = pos2_new.z , pos1_new.z
end
return pos1_new , pos2_new
end
--- Determines if this vector is contained within the region defined by the given vectors.
-- @param a Vector3 The target vector to check.
-- @param pos1 Vector3 pos1 of the defined region.
-- @param pos2 Vector3 pos2 of the defined region.
-- @return boolean Whether the given target is contained within the defined worldedit region.
function Vector3 . is_contained ( target , pos1 , pos2 )
local pos1 , pos2 = Vector3.sort ( pos1 , pos2 )
return pos1.x <= target.x
and pos1.y <= target.y
and pos1.z <= target.z
and pos2.x >= target.x
and pos2.y >= target.y
and pos2.z >= target.z
end
2021-12-27 03:18:56 +01:00
--- Clamps the given point to fall within the region defined by 2 points.
-- @param a Vector3 The target vector to clamp.
-- @param pos1 Vector3 pos1 of the defined region.
-- @param pos2 Vector3 pos2 of the defined region.
-- @returns Vector3 The target vector, clamped to fall within the defined region.
function Vector3 . clamp ( a , pos1 , pos2 )
pos1 , pos2 = Vector3.sort ( pos1 , pos2 )
return Vector3.min ( Vector3.max ( a , pos1 ) , pos2 )
end
2021-06-26 18:48:39 +02:00
--- Expands the defined region to include the given point.
-- @param target Vector3 The target vector to include.
-- @param pos1 Vector3 pos1 of the defined region.
-- @param pos2 Vector3 pos2 of the defined region.
-- @returns Vector3,Vector3 2 vectors that represent the expand_region.
function Vector3 . expand_region ( target , pos1 , pos2 )
local pos1 , pos2 = Vector3.sort ( pos1 , pos2 )
if target.x < pos1.x then pos1.x = target.x end
if target.y < pos1.y then pos1.y = target.y end
if target.z < pos1.z then pos1.z = target.z end
if target.x > pos2.x then pos2.x = target.x end
if target.y > pos2.y then pos2.y = target.y end
if target.z > pos2.z then pos2.z = target.z end
return pos1 , pos2
end
--- Returns the mean (average) of 2 positions.
-- In other words, returns the centre of 2 points.
2021-08-04 22:23:01 +02:00
-- @param pos1 Vector3|number pos1 of the defined region.
-- @param pos2 Vector3|number pos2 of the defined region.
2021-06-26 18:48:39 +02:00
-- @param target Vector3 Centre coordinates.
function Vector3 . mean ( pos1 , pos2 )
return ( pos1 + pos2 ) / 2
end
--- Returns a vector of the min components of 2 vectors.
2021-08-04 22:23:01 +02:00
-- @param pos1 Vector3|number The first vector to operate on.
-- @param pos2 Vector3|number The second vector to operate on.
2021-06-26 18:48:39 +02:00
-- @return Vector3 The minimum values from the input vectors
function Vector3 . min ( pos1 , pos2 )
2021-08-04 22:23:01 +02:00
if type ( pos1 ) == " number " then
pos1 = Vector3.new ( pos1 , pos1 , pos1 )
end
if type ( pos2 ) == " number " then
pos2 = Vector3.new ( pos2 , pos2 , pos2 )
end
2021-06-26 18:48:39 +02:00
return Vector3.new (
math.min ( pos1.x , pos2.x ) ,
math.min ( pos1.y , pos2.y ) ,
math.min ( pos1.z , pos2.z )
)
end
--- Returns a vector of the max values of 2 vectors.
-- @param pos1 Vector3 The first vector to operate on.
-- @param pos2 Vector3 The second vector to operate on.
-- @return Vector3 The maximum values from the input vectors.
function Vector3 . max ( pos1 , pos2 )
2021-08-04 22:23:01 +02:00
if type ( pos1 ) == " number " then
pos1 = Vector3.new ( pos1 , pos1 , pos1 )
end
if type ( pos2 ) == " number " then
pos2 = Vector3.new ( pos2 , pos2 , pos2 )
end
2021-06-26 18:48:39 +02:00
return Vector3.new (
math.max ( pos1.x , pos2.x ) ,
math.max ( pos1.y , pos2.y ) ,
math.max ( pos1.z , pos2.z )
)
end
2021-10-31 17:59:52 +01:00
--- Given 2 angles and a length, return a Vector3 pointing in that direction.
-- Consider a sphere, with the 2 angles defining a point on the sphere's surface.
-- This function returns that point as a Vector3.
-- @source https://math.stackexchange.com/a/1881767/221181
-- @param angle_x number The X angle.
-- @param angle_y number The Y angle.
-- @param length number The radius of the sphere in question.
-- @returns Vector3 The point on the sphere defined by the aforementioned parameters.
function Vector3 . fromBearing ( angle_x , angle_y , length )
return Vector3.new ( -- X and Y swapped
length * math.cos ( angle_x ) ,
length * math.sin ( angle_x ) * math.sin ( angle_y ) ,
length * math.sin ( angle_x ) * math.cos ( angle_y )
)
end
2023-01-21 00:43:07 +01:00
--- Rotate a given point around an origin point in 3d space.
-- Consider 3 axes (X, Y, and Z) that are centred on origin. This function
-- rotates point around these axes in the aforementioned order.
2023-11-28 23:38:26 +01:00
--
2023-01-21 00:43:07 +01:00
-- NOTE: This function is not as intuitive as it sounds.
-- A whiteboard and a head for mathematics is recommended before using this
-- function. Either that, or Blender 3 (https://blender.org/) is quite useful to visualise what's going on.
-- @source GitHub Copilot, generated 2023-01-17
-- @warning Not completely tested! Pending a thorough evaluation. Seems to basically work, after some tweaks to the fluff around the edges?
2023-07-02 18:03:39 +02:00
-- @param origin Vector3 The origin point to rotate around
-- @param point Vector3 The point to rotate.
2023-11-29 01:36:50 +01:00
-- @param rotate Vector3 Rotate this much around the 3 different axes, x, y, and z. Axial rotations are handled in this order: X→Y→Z. Values MUST be in radians!
2023-07-02 18:03:39 +02:00
-- @return Vector3 The rotated point.
2023-01-21 00:43:07 +01:00
function Vector3 . rotate3d ( origin , point , rotate )
local point_norm = point - origin
-- rotate around x
local x1 = point_norm.x
local y1 = point_norm.y * math.cos ( rotate.x ) - point_norm.z * math.sin ( rotate.x )
local z1 = point_norm.y * math.sin ( rotate.x ) + point_norm.z * math.cos ( rotate.x )
-- rotate around y
local x2 = x1 * math.cos ( rotate.y ) + z1 * math.sin ( rotate.y )
local y2 = y1
local z2 = - x1 * math.sin ( rotate.y ) + z1 * math.cos ( rotate.y )
-- rotate around z
local x3 = x2 * math.cos ( rotate.z ) - y2 * math.sin ( rotate.z )
local y3 = x2 * math.sin ( rotate.z ) + y2 * math.cos ( rotate.z )
local z3 = z2
return Vector3.new ( x3 , y3 , z3 ) + origin
-- return [x3+origin[0], y3+origin[1], z3+origin[2]];
end
2021-06-26 16:32:47 +02:00
2021-06-26 15:10:28 +02:00
-- ██████ ██████ ███████ ██████ █████ ████████ ██████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██████ █████ ██████ ███████ ██ ██ ██ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██ ███████ ██ ██ ██ ██ ██ ██████ ██ ██
2021-08-05 19:41:35 +02:00
--
2021-06-26 15:10:28 +02:00
-- ██████ ██ ██ ███████ ██████ ██████ ██ ██████ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ █████ ██████ ██████ ██ ██ ██ █████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ████ ███████ ██ ██ ██ ██ ██ ██████ ███████ ███████
2021-06-26 17:47:22 +02:00
function Vector3 . __call ( x , y , z ) return Vector3.new ( x , y , z ) end
2021-06-26 15:10:28 +02:00
function Vector3 . __add ( a , b )
return Vector3.add ( a , b )
end
function Vector3 . __sub ( a , b )
return Vector3.sub ( a , b )
end
function Vector3 . __mul ( a , b )
return Vector3.mul ( a , b )
end
function Vector3 . __div ( a , b )
return Vector3.divide ( a , b )
end
2021-06-26 17:28:39 +02:00
function Vector3 . __eq ( a , b )
return Vector3.equals ( a , b )
end
2021-06-26 15:10:28 +02:00
--- Returns the current Vector3 as a string.
function Vector3 . __tostring ( a )
return " ( " .. a.x .. " , " .. a.y .. " , " .. a.z .. " ) "
end
2021-07-15 03:18:21 +02:00
function Vector3 . __concat ( a , b )
if type ( a ) ~= " string " then a = tostring ( a ) end
if type ( b ) ~= " string " then b = tostring ( b ) end
return a .. b
end
2021-06-26 15:10:28 +02:00
2021-06-28 02:00:38 +02:00
return Vector3