mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-30 19:13:53 +01:00
f32d8588e0
Together, these classes provide a way to represent a mesh of faces generated from the Minetest world. This way, the generation of a mesh can be abstracted away from any potential output file writers, thereby allowing for multiple different output file formats to be supported.
400 lines
14 KiB
Lua
400 lines
14 KiB
Lua
--- A 3-dimensional vector.
|
|
local Vector3 = {}
|
|
Vector3.__index = Vector3
|
|
|
|
function Vector3.new(x, y, z)
|
|
if type(x) ~= "number" then
|
|
error("Error: Expected number for the value of x, but received argument of type "..type(x)..".")
|
|
end
|
|
if type(y) ~= "number" then
|
|
error("Error: Expected number for the value of y, but received argument of type "..type(y)..".")
|
|
end
|
|
if type(z) ~= "number" then
|
|
error("Error: Expected number for the value of z, but received argument of type "..type(z)..".")
|
|
end
|
|
|
|
local result = {
|
|
x = x,
|
|
y = y,
|
|
z = z
|
|
}
|
|
setmetatable(result, Vector3)
|
|
return result
|
|
end
|
|
|
|
--- 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
|
|
|
|
--- 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.
|
|
--
|
|
-- 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
|
|
--
|
|
-- @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
|
|
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.divide.
|
|
function Vector3.div(a, b) return Vector3.divide(a, b) end
|
|
|
|
|
|
--- 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
|
|
--- 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
|
|
|
|
|
|
--- 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)
|
|
return (a / grid_size):round() * grid_size
|
|
end
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- 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
|
|
|
|
--- Sorts the components of the given vectors.
|
|
-- pos1 will contain the minimum values, and pos2 the maximum values.
|
|
-- Returns 2 new vectors.
|
|
-- Note that the vectors provided do not *have* to be instances of Vector3.
|
|
-- 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.
|
|
-- @returns Vector3,Vector3 The 2 sorted vectors.
|
|
function Vector3.sort(pos1, pos2)
|
|
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
|
|
|
|
|
|
--- 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.
|
|
-- @param pos1 Vector3 pos1 of the defined region.
|
|
-- @param pos2 Vector3 pos2 of the defined region.
|
|
-- @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.
|
|
-- @param pos1 Vector3 The first vector to operate on.
|
|
-- @param pos2 Vector3 The second vector to operate on.
|
|
-- @return Vector3 The minimum values from the input vectors
|
|
function Vector3.min(pos1, pos2)
|
|
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)
|
|
return Vector3.new(
|
|
math.max(pos1.x, pos2.x),
|
|
math.max(pos1.y, pos2.y),
|
|
math.max(pos1.z, pos2.z)
|
|
)
|
|
end
|
|
|
|
|
|
|
|
-- ██████ ██████ ███████ ██████ █████ ████████ ██████ ██████
|
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
-- ██ ██ ██████ █████ ██████ ███████ ██ ██ ██ ██████
|
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
-- ██████ ██ ███████ ██ ██ ██ ██ ██ ██████ ██ ██
|
|
--
|
|
-- ██████ ██ ██ ███████ ██████ ██████ ██ ██████ ███████ ███████
|
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
-- ██ ██ ██ ██ █████ ██████ ██████ ██ ██ ██ █████ ███████
|
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
-- ██████ ████ ███████ ██ ██ ██ ██ ██ ██████ ███████ ███████
|
|
|
|
function Vector3.__call(x, y, z) return Vector3.new(x, y, z) end
|
|
|
|
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
|
|
|
|
function Vector3.__eq(a, b)
|
|
return Vector3.equals(a, b)
|
|
end
|
|
|
|
--- Returns the current Vector3 as a string.
|
|
function Vector3.__tostring(a)
|
|
return "("..a.x..", "..a.y..", "..a.z..")"
|
|
end
|
|
|
|
|
|
|
|
if worldeditadditions then
|
|
worldeditadditions.Vector3 = Vector3
|
|
else
|
|
return Vector3
|
|
end
|