pick_and_place-cd2025/schematic_rotate.lua
Buckaroo Banzai 9cd093046d
schematic rotation (#1)
* schematic rotation

* fixes

* remove auto-rotation code

* schematic encoding v2

* testing

* working encode/decode

* rotate schematic

* spec

* rotate dialog

* skip if rotation == 0

* wip

* wip

* wip

* fix order

* fix metadata rotation

---------

Co-authored-by: BuckarooBanzay <BuckarooBanzay@users.noreply.github.com>
2024-02-29 13:15:18 +01:00

121 lines
3.8 KiB
Lua

local zero = { x=0, y=0, z=0 }
-- creates a buffer for the rotation
local function create_buffer(data, size, max)
local area_src = VoxelArea:new({MinEdge=zero, MaxEdge=size})
local area_dst = VoxelArea:new({MinEdge=zero, MaxEdge=max})
local buf = {}
for z=0,size.z do
for x=0,size.x do
for y=0,size.y do
local i_src = area_src:index(x,y,z)
local i_dst = area_dst:index(x,y,z)
buf[i_dst] = data[i_src]
end
end
end
return buf
end
-- extracts the rotated new size from the buffer
local function extract_from_buffer(buf, size, max, offset)
local area_src = VoxelArea:new({MinEdge=zero, MaxEdge=max})
local area_dst = VoxelArea:new({MinEdge=zero, MaxEdge=size})
local data = {}
for z=0,size.z do
for x=0,size.x do
for y=0,size.y do
local i_src = area_src:index(x+offset.x,y+offset.y,z+offset.z)
local i_dst = area_dst:index(x,y,z)
data[i_dst] = buf[i_src]
end
end
end
return data
end
local function apply_offset(metadata_map, offset)
local new_metadata_map = {}
for pos_str, metadata in pairs(metadata_map) do
local pos = minetest.string_to_pos(pos_str)
local new_pos = vector.subtract(pos, offset)
local new_pos_str = minetest.pos_to_string(new_pos)
new_metadata_map[new_pos_str] = metadata
end
return new_metadata_map
end
function pick_and_place.schematic_rotate(schematic, rotation)
if rotation <= 0 or rotation > 270 then
-- invalid or no rotation
return
end
local other1, other2 = "x", "z"
local rotated_size = pick_and_place.rotate_size(schematic.size, rotation)
local metadata = schematic.metadata
local max_xz_axis = math.max(schematic.size.x, schematic.size.z)
local max = { x=max_xz_axis-1, y=schematic.size.y-1, z=max_xz_axis-1 }
-- create transform buffers
local node_id_buf = create_buffer(schematic.node_id_data, vector.subtract(schematic.size, 1), max)
local param2_buf = create_buffer(schematic.param2_data, vector.subtract(schematic.size, 1), max)
-- rotate
if rotation == 90 then
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other1)
pick_and_place.schematic_transpose(node_id_buf, param2_buf, metadata, max, other1, other2)
elseif rotation == 180 then
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other1)
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other2)
elseif rotation == 270 then
pick_and_place.schematic_transpose(node_id_buf, param2_buf, metadata, max, other1, other2)
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other1)
end
-- extract from buffer with offset
local offset = {x=0, y=0, z=0}
local z_larger = schematic.size.z > schematic.size.x
local x_larger = schematic.size.z < schematic.size.x
local xz_diff = math.abs(schematic.size.x - schematic.size.z)
if rotation == 90 then
if z_larger then
offset.z = xz_diff
end
elseif rotation == 180 then
if x_larger then
offset.z = xz_diff
elseif z_larger then
offset.x = xz_diff
end
elseif rotation == 270 then
if x_larger then
offset.x = xz_diff
end
end
schematic.node_id_data = extract_from_buffer(node_id_buf, vector.subtract(rotated_size, 1), max, offset)
schematic.param2_data = extract_from_buffer(param2_buf, vector.subtract(rotated_size, 1), max, offset)
schematic.metadata = apply_offset(metadata, offset)
-- rotate size
schematic.size = rotated_size
-- orient rotated schematic
pick_and_place.schematic_orient(
schematic.node_id_data,
schematic.param2_data,
vector.subtract(rotated_size, 1),
rotation
)
end