edit-cd2025/line.lua
2024-02-19 20:17:38 -06:00

191 lines
4.7 KiB
Lua

-- https://www.geeksforgeeks.org/bresenhams-algorithm-for-3-d-line-drawing/
-- This Site is affiliated under CCBY-SA https://www.geeksforgeeks.org/legal/copyright-information/
-- JS code for generating points on a 3-D line
-- using Bresenham's Algorithm
-- Converted from the original to Lua
function edit.calculate_line_points(p1, p2)
p1 = table.copy(p1)
p2 = table.copy(p2)
local output = {table.copy(p1)}
local d = vector.apply(vector.subtract(p1, p2), math.abs)
local s = vector.new(
p2.x > p1.x and 1 or -1,
p2.y > p1.y and 1 or -1,
p2.z > p1.z and 1 or -1
)
-- Driving axis is X-axis
if d.x >= d.y and d.x >= d.z then
local n1 = 2 * d.y - d.x
local n2 = 2 * d.z - d.x
while p1.x ~= p2.x do
p1.x = p1.x + s.x
if n1 >= 0 then
p1.y = p1.y + s.y
n1 = n1 - 2 * d.x
end
if n2 >= 0 then
p1.z = p1.z + s.z
n2 = n2 - 2 * d.x
end
n1 = n1 + 2 * d.y
n2 = n2 + 2 * d.z
table.insert(output, table.copy(p1))
end
-- Driving axis is Y-axis
elseif d.y >= d.x and d.y >= d.z then
local n1 = 2 * d.x - d.y
local n2 = 2 * d.z - d.y
while p1.y ~= p2.y do
p1.y = p1.y + s.y
if n1 >= 0 then
p1.x = p1.x + s.x
n1 = n1 - 2 * d.y
end
if n2 >= 0 then
p1.z = p1.z + s.z
n2 = n2 - 2 * d.y
end
n1 = n1 + 2 * d.x
n2 = n2 + 2 * d.z
table.insert(output, table.copy(p1))
end
-- Driving axis is Z-axis
else
local n1 = 2 * d.y - d.z
local n2 = 2 * d.x - d.z
while p1.z ~= p2.z do
p1.z = p1.z + s.z
if n1 >= 0 then
p1.y = p1.y + s.y
n1 = n1 - 2 * d.z
end
if n2 >= 0 then
p1.x = p1.x + s.x
n2 = n2 - 2 * d.z
end
n1 = n1 + 2 * d.y
n2 = n2 + 2 * d.x
table.insert(output, table.copy(p1))
end
end
return output
end
minetest.register_entity("edit:line", {
initial_properties = {
visual = "cube",
visual_size = { x = 1.1, y = 1.1 },
physical = false,
collide_with_objects = false,
static_save = false,
use_texture_alpha = true,
glow = -1,
backface_culling = false,
hp_max = 1,
textures = {
"edit_line.png",
"edit_line.png",
"edit_line.png",
"edit_line.png",
"edit_line.png",
"edit_line.png",
},
},
on_deactivate = function(self)
local player_data = edit.player_data[self._placer]
self.remove_called = true
if player_data then
local line1 = player_data.line1
if line1 and not line1.remove_called then
line1.object:remove()
end
player_data.line1 = nil
local line2 = player_data.line2
if line2 and not line2.remove_called then
line2.object:remove()
end
player_data.line2 = nil
player_data.old_pointed_pos = nil
end
end,
})
local function place_line(player, item_name)
local player_data = edit.player_data[player]
if not player_data.line1 then return end
if not item_name then
player_data.line1.object:remove()
return
end
local pos1 = player_data.line1._pos
local pos2 = player_data.line2._pos
local size = vector.add(vector.apply(vector.subtract(pos1, pos2), math.abs), vector.new(1, 1, 1))
local pos = vector.new(
math.min(pos1.x, pos2.x),
math.min(pos1.y, pos2.y),
math.min(pos1.z, pos2.z)
)
player_data.undo_schematic = edit.schematic_from_map(pos, size)
local line_points = edit.calculate_line_points(pos1, pos2)
local item = {name = item_name}
for i, pos in pairs(line_points) do
edit.place_item_like_player(player, item, pos)
end
player_data.line1.object:remove()
end
local function line_on_place(itemstack, player, pointed_thing)
if not edit.on_place_checks(player) then return end
if not pointed_thing.above then
pointed_thing = edit.get_pointed_thing_node(player)
end
local pos = edit.pointed_thing_to_pos(pointed_thing)
if not pos then return end
local player_data = edit.player_data[player]
if not player_data.line1 then
player_data.line1 = edit.add_marker("edit:line", pos, player)
if not player_data.line1 then return end
else
player_data.line2 = edit.add_marker("edit:line", pos, player)
if not player_data.line2 then return end
local diff = vector.subtract(player_data.line1._pos, pos)
local volume = vector.add(vector.apply(diff, math.abs), 1)
if volume.x * volume.y * volume.z > edit.max_operation_volume then
edit.display_size_error(player)
player_data.line1.object:remove()
return
end
edit.player_select_item(player, "Select item to fill the line", place_line)
end
edit.old_pointed_pos = nil
end
minetest.register_tool("edit:line", {
description = "Edit Line",
tiles = {"edit_line.png"},
inventory_image = "edit_line.png",
range = 10,
groups = {edit_place_preview = 1,},
on_place = line_on_place,
on_secondary_use = line_on_place,
_edit_get_selection_points = function(player)
local d = edit.player_data[player]
return d.line1 and d.line1._pos, d.line2 and d.line2._pos
end
})