2022-09-25 02:18:40 +02:00
|
|
|
local wea_c = worldeditadditions_core
|
|
|
|
local Vector3 = wea_c.Vector3
|
|
|
|
|
|
|
|
-- ███████ ██████ ██ ██ ███ ██ ███████
|
|
|
|
-- ██ ██ ██ ██ ██ ████ ██ ██
|
|
|
|
-- ███████ ██████ ██ ██ ██ ██ ██ █████
|
|
|
|
-- ██ ██ ██ ██ ██ ██ ██ ██
|
|
|
|
-- ███████ ██ ███████ ██ ██ ████ ███████
|
|
|
|
|
|
|
|
--- Creates a spline that follows the given list of points.
|
|
|
|
-- @param pos_list Vector3[] The list of points that define the path the spline should follow. Minimum: 2
|
|
|
|
-- @param width_start number The width of the spline at the start.
|
|
|
|
-- @param width_end number The width of the spline at the end. If nil, then width_start is used.
|
|
|
|
-- @param steps number The number of smoothing passes to apply to the list of points.
|
|
|
|
-- @param target_node Vector3 The *normalised* name of the node to use to build the square spiral with.
|
|
|
|
-- @returns bool,number|string A success boolean value, followed by either the number of the nodes set or an error message string.
|
|
|
|
function worldeditadditions.spline(pos_list, width_start, width_end, steps, target_node)
|
|
|
|
if #pos_list < 2 then return false, "Error: At least 2 positions are required to make a spline." end
|
|
|
|
if not width_end then width_end = width_start end
|
|
|
|
|
|
|
|
---
|
|
|
|
-- 0: Find bounding box
|
|
|
|
---
|
|
|
|
-- We can't use wea_c.pos.get_bounds 'cause that requires a player name
|
|
|
|
local pos1, pos2 = pos_list[1], pos_list[2]
|
|
|
|
for _, pos in pairs(pos_list) do
|
|
|
|
pos1, pos2 = Vector3.expand_region(pos, pos1, pos2)
|
|
|
|
end
|
|
|
|
local max_width = math.max(width_start, width_end)
|
|
|
|
pos1 = pos1 - max_width
|
|
|
|
pos2 = pos2 + max_width
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
-- 1: Interpolate points & widths
|
|
|
|
---
|
|
|
|
local pos_list_chaikin = wea_c.chaikin(pos_list, steps)
|
|
|
|
|
|
|
|
local widths_lerped = {}
|
|
|
|
for i = 1, #pos_list_chaikin do
|
|
|
|
table.insert(
|
|
|
|
widths_lerped,
|
|
|
|
wea_c.lerp(width_start, width_end, (1/#pos_list_chaikin)*(i-1))
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
---
|
|
|
|
-- 2: Fetch VoxelManipulator, prepare for replacement
|
|
|
|
---
|
|
|
|
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
|
|
|
local data = manip:get_data()
|
|
|
|
|
|
|
|
local node_id = minetest.get_content_id(target_node)
|
|
|
|
|
|
|
|
local count = 0 -- The number of nodes replaced
|
|
|
|
|
|
|
|
---
|
|
|
|
-- 3: Replace nodes
|
|
|
|
---
|
|
|
|
|
2022-09-25 16:30:16 +02:00
|
|
|
local pos_prev = pos_list_chaikin[1]:floor()
|
2022-09-25 18:07:37 +02:00
|
|
|
local width_prev = widths_lerped[1]
|
|
|
|
for i = 2, #pos_list_chaikin do
|
2022-09-25 16:30:16 +02:00
|
|
|
local pos_next = pos_list_chaikin[i]:floor()
|
2022-09-25 02:18:40 +02:00
|
|
|
local width_next = widths_lerped[i]
|
|
|
|
|
|
|
|
-- For now, just plot a point at each node
|
2022-09-25 18:07:37 +02:00
|
|
|
data[area:indexp(pos_next)] = node_id
|
|
|
|
data[area:indexp(pos_prev)] = node_id
|
2022-09-25 02:18:40 +02:00
|
|
|
|
2022-09-25 18:07:37 +02:00
|
|
|
local subline_length = (pos_next - pos_prev):length()
|
|
|
|
print("DEBUG:spline DRAW pos", pos_next, "width", width_next, "length", subline_length)
|
|
|
|
if subline_length > 0 then
|
|
|
|
|
|
|
|
-- Iterate a box around the subline and draw it
|
|
|
|
local width_max = math.ceil(math.max(width_prev, width_next))
|
|
|
|
local subpos1, subpos2 = Vector3.sort(pos_next, pos_prev)
|
|
|
|
subpos1 = subpos1 - width_max
|
|
|
|
subpos2 = subpos2 + width_max
|
|
|
|
|
|
|
|
print("subpos1", subpos1, "subpos2", subpos2, "width_prev", width_prev, "width_next", width_next)
|
|
|
|
|
|
|
|
for z = subpos2.z, subpos1.z, -1 do
|
|
|
|
for y = subpos2.y, subpos1.y, -1 do
|
|
|
|
for x = subpos2.x, subpos1.x, -1 do
|
|
|
|
local here = Vector3.new(x, y, z)
|
|
|
|
local D = (pos_next - pos_prev):normalise()
|
|
|
|
local d = Vector3.dot(here - pos_prev, D)
|
|
|
|
local closest_on_line = pos_prev + (D * d)
|
|
|
|
local distance = (here - closest_on_line):length()
|
|
|
|
|
|
|
|
local distance_to_prev = (closest_on_line - pos_prev):length()
|
|
|
|
|
|
|
|
local percentage_along_line = distance_to_prev / subline_length
|
|
|
|
local width_here = wea_c.lerp(width_prev, width_next, percentage_along_line)
|
|
|
|
if distance < width_here then
|
|
|
|
-- print("POINT ", here, "width_here", width_here, "distance from line", distance)
|
|
|
|
data[area:indexp(here)] = node_id
|
|
|
|
-- May hit some nodes multiplle times, but there's nothing doing about that without a big performance (and/or memory) hit
|
|
|
|
count = count + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
pos_prev = pos_next:clone()
|
|
|
|
width_prev = width_next
|
|
|
|
end
|
2022-09-25 02:18:40 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
---
|
|
|
|
-- 4: Save changes and return
|
|
|
|
---
|
|
|
|
|
|
|
|
-- Save the modified nodes back to disk & return
|
|
|
|
worldedit.manip_helpers.finish(manip, data)
|
|
|
|
|
|
|
|
return true, count
|
|
|
|
end
|