Skip to content

Commit b0bf52e

Browse files
committed
Rewrite spirals from scratch and fix upside-down pyramids. Use voxelmanip for markers to ensure area is emerged.
1 parent 3c51ec8 commit b0bf52e

File tree

8 files changed

+181
-104
lines changed

8 files changed

+181
-104
lines changed

Chat Commands.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height
155155
//pyramid 5 glass
156156
//pyramid 2 mesecons:wire_00000000_off
157157

158-
### //spiral <width> <height> <spacer> <node>
158+
### //spiral <length> <height> <spacer> <node>
159159

160-
Add spiral centered at WorldEdit position 1 with width <width>, height <height>, space between walls <spacer>, composed of <node>.
160+
Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <spacer>, composed of <node>.
161161

162162
//spiral 20 5 3 Diamond Block
163163
//spiral 5 2 1 glass

WorldEdit API.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with
134134

135135
Returns the number of nodes added.
136136

137-
### count = worldedit.spiral(pos, width, height, spacer, nodename)
137+
### count = worldedit.spiral(pos, length, height, spacer, nodename)
138138

139-
Adds a spiral centered at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`.
139+
Adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`.
140140

141141
Returns the number of nodes added.
142142

worldedit/compatibility.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ worldedit.metaload = function(originpos, filename)
1717
if err then return 0 end
1818
local data = file:read("*a")
1919
return worldedit.deserialize(originpos, data)
20-
end
20+
end

worldedit/manipulations.lua

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
worldedit = worldedit or {}
22
local minetest = minetest --local copy of global
33

4-
--wip: remove env parameter where no longer needed in chat commands module
54
--wip: fix the queue
65

76
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
@@ -112,11 +111,68 @@ worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode)
112111
return count
113112
end
114113

114+
worldedit.copy = function(pos1, pos2, axis, amount)
115+
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
116+
117+
if amount == 0 then
118+
return
119+
end
120+
121+
local other1, other2
122+
if axis == "x" then
123+
other1, other2 = "y", "z"
124+
elseif axis == "y" then
125+
other1, other2 = "x", "z"
126+
else --axis == "z"
127+
other1, other2 = "x", "y"
128+
end
129+
130+
--make area stay loaded
131+
local manip = minetest.get_voxel_manip()
132+
manip:read_from_map(pos1, pos2)
133+
134+
--prepare slice along axis
135+
local extent = {
136+
[axis] = 1,
137+
[other1]=pos2[other1] - pos1[other1] + 1,
138+
[other2]=pos2[other2] - pos1[other2] + 1,
139+
}
140+
local nodes = {}
141+
local schematic = {size=extent, data=nodes}
142+
143+
local currentpos = {x=pos1.x, y=pos1.y, z=pos1.z}
144+
local stride = {x=1, y=extent.x, z=extent.x * extent.y}
145+
local get_node = minetest.get_node
146+
for index1 = 1, extent[axis] do --go through each slice
147+
--copy slice into schematic
148+
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
149+
for index2 = 1, extent[other1] do
150+
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
151+
for index3 = 1, extent[other2] do
152+
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
153+
nodes[i] = get_node(pos)
154+
end
155+
end
156+
157+
--copy schematic to target
158+
currentpos[axis] = currentpos[axis] + amount
159+
place_schematic(currentpos, schematic)
160+
161+
--wip: copy meta
162+
163+
currentpos[axis] = currentpos[axis] + 1
164+
end
165+
return worldedit.volume(pos1, pos2)
166+
end
167+
115168
--copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied
116169
worldedit.copy = function(pos1, pos2, axis, amount)
117170
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
118171

119-
--wip: copy slice by slice using schematic method in the copy axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time), use voxelmanip to keep area loaded
172+
--make area stay loaded
173+
local manip = minetest.get_voxel_manip()
174+
manip:read_from_map(pos1, pos2)
175+
120176
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
121177
if amount < 0 then
122178
local pos = {x=pos1.x, y=0, z=0}
@@ -166,7 +222,11 @@ end
166222
worldedit.move = function(pos1, pos2, axis, amount)
167223
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
168224

169-
--wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method), use voxelmanip to keep area loaded
225+
--make area stay loaded
226+
local manip = minetest.get_voxel_manip()
227+
manip:read_from_map(pos1, pos2)
228+
229+
--wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method)
170230
local get_node, get_meta, add_node, remove_node = minetest.get_node, minetest.get_meta, minetest.add_node, minetest.remove_node
171231
if amount < 0 then
172232
local pos = {x=pos1.x, y=0, z=0}
@@ -215,7 +275,7 @@ worldedit.move = function(pos1, pos2, axis, amount)
215275
end
216276

217277
--duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times, returning the number of nodes stacked
218-
worldedit.stack = function(pos1, pos2, axis, count, env)
278+
worldedit.stack = function(pos1, pos2, axis, count)
219279
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
220280
local length = pos2[axis] - pos1[axis] + 1
221281
if count < 0 then
@@ -226,7 +286,7 @@ worldedit.stack = function(pos1, pos2, axis, count, env)
226286
local copy = worldedit.copy
227287
for i = 1, count do
228288
amount = amount + length
229-
copy(pos1, pos2, axis, amount, env)
289+
copy(pos1, pos2, axis, amount)
230290
end
231291
return worldedit.volume(pos1, pos2) * count
232292
end
@@ -291,7 +351,7 @@ worldedit.scale = function(pos1, pos2, factor)
291351
end
292352

293353
--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2
294-
worldedit.transpose = function(pos1, pos2, axis1, axis2, env)
354+
worldedit.transpose = function(pos1, pos2, axis1, axis2)
295355
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
296356

297357
local compare
@@ -350,7 +410,7 @@ worldedit.transpose = function(pos1, pos2, axis1, axis2, env)
350410
end
351411

352412
--flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z"), returning the number of nodes flipped
353-
worldedit.flip = function(pos1, pos2, axis, env)
413+
worldedit.flip = function(pos1, pos2, axis)
354414
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
355415

356416
--make area stay loaded
@@ -388,7 +448,7 @@ worldedit.flip = function(pos1, pos2, axis, env)
388448
end
389449

390450
--rotates a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise around axis `axis` (90 degree increment), returning the number of nodes rotated
391-
worldedit.rotate = function(pos1, pos2, axis, angle, env)
451+
worldedit.rotate = function(pos1, pos2, axis, angle)
392452
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
393453

394454
local axis1, axis2
@@ -403,20 +463,20 @@ worldedit.rotate = function(pos1, pos2, axis, angle, env)
403463

404464
local count
405465
if angle == 90 then
406-
worldedit.flip(pos1, pos2, axis1, env)
407-
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2, env)
466+
worldedit.flip(pos1, pos2, axis1)
467+
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
408468
elseif angle == 180 then
409-
worldedit.flip(pos1, pos2, axis1, env)
410-
count = worldedit.flip(pos1, pos2, axis2, env)
469+
worldedit.flip(pos1, pos2, axis1)
470+
count = worldedit.flip(pos1, pos2, axis2)
411471
elseif angle == 270 then
412-
worldedit.flip(pos1, pos2, axis2, env)
413-
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2, env)
472+
worldedit.flip(pos1, pos2, axis2)
473+
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
414474
end
415475
return count, pos1, pos2
416476
end
417477

418478
--rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented
419-
worldedit.orient = function(pos1, pos2, angle, env) --wip: support 6D facedir rotation along arbitrary axis
479+
worldedit.orient = function(pos1, pos2, angle) --wip: support 6D facedir rotation along arbitrary axis
420480
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
421481
local registered_nodes = minetest.registered_nodes
422482

@@ -477,7 +537,7 @@ worldedit.orient = function(pos1, pos2, angle, env) --wip: support 6D facedir ro
477537
end
478538

479539
--fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated
480-
worldedit.fixlight = function(pos1, pos2, env)
540+
worldedit.fixlight = function(pos1, pos2)
481541
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
482542

483543
--make area stay loaded

worldedit/primitives.lua

Lines changed: 72 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ worldedit.cylinder = function(pos, axis, length, radius, nodename)
326326
local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]
327327
if index2 * index2 + index3 * index3 <= max_radius then
328328
for index1 = min_slice, max_slice do --add column along axis
329-
local i = newindex3 + index1 * stride[axis] + 1
329+
local i = newindex3 + index1 * stride[axis]
330330
nodes[i] = node_id
331331
end
332332
count = count + length
@@ -358,18 +358,14 @@ worldedit.pyramid = function(pos, axis, height, nodename)
358358

359359
--handle inverted pyramids
360360
local startaxis, endaxis, step
361-
local currentpos = {x=pos.x, y=pos.y, z=pos.z}
362361
if height > 0 then
363362
height = height - 1
364-
startaxis, endaxis = 0, height
365363
step = 1
366364
pos1[axis] = pos[axis] --upper half of box
367365
else
368-
height = -height - 1
369-
startaxis, endaxis = height, 0
366+
height = height + 1
370367
step = -1
371-
pos2[axis] = pos[axis] + 1 --lower half of box
372-
currentpos[axis] = pos[axis] - height --bottom of box
368+
pos2[axis] = pos[axis] --lower half of box
373369
end
374370

375371
--set up voxel manipulator
@@ -387,19 +383,20 @@ worldedit.pyramid = function(pos, axis, height, nodename)
387383
--fill selected area with node
388384
local node_id = minetest.get_content_id(nodename)
389385
local stride = {x=1, y=area.ystride, z=area.zstride}
390-
local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}
386+
local offset = {x=pos.x - emerged_pos1.x, y=pos.y - emerged_pos1.y, z=pos.z - emerged_pos1.z}
387+
local size = height * step
391388
local count = 0
392-
for index1 = startaxis, endaxis, step do --go through each level of the pyramid
389+
for index1 = 0, height, step do --go through each level of the pyramid
393390
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
394-
for index2 = -height, height do
391+
for index2 = -size, size do
395392
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
396-
for index3 = -height, height do
393+
for index3 = -size, size do
397394
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
398395
nodes[i] = node_id
399396
end
400397
end
401-
count = count + (height * 2 + 1) ^ 2
402-
height = height - 1
398+
count = count + (size * 2 + 1) ^ 2
399+
size = size - 1
403400
end
404401

405402
--update map nodes
@@ -410,70 +407,72 @@ worldedit.pyramid = function(pos, axis, height, nodename)
410407
return count
411408
end
412409

413-
--adds a spiral centered at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added
414-
worldedit.spiral = function(pos, width, height, spacer, nodename, env) --wip: rewrite this whole thing, nobody can understand it anyways
415-
-- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua
416-
local abs = math.abs
417-
local sign = function(s) return s ~= 0 and s / av(s) or 0 end
418-
local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards
419-
if z == -x and z >= x then return (2*z+1)^2 end
420-
local longest = math.max(abs(z), abs(x))
421-
return (2*longest-1)^2 + 4*longest + 2*longest*sign(x+z) + sign(z^2-x^2)*(longest-(abs(z)==longest and sign(z)*x or sign(x)*z)) -- OH GOD WHAT
410+
--adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added
411+
worldedit.spiral = function(pos, length, height, spacer, nodename)
412+
local extent = math.ceil(length / 2)
413+
local pos1 = {x=pos.x - extent, y=pos.y, z=pos.z - extent}
414+
local pos2 = {x=pos.x + extent, y=pos.y + height, z=pos.z + extent}
415+
416+
--set up voxel manipulator
417+
local manip = minetest.get_voxel_manip()
418+
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
419+
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
420+
421+
--fill emerged area with ignore
422+
local nodes = {}
423+
local ignore = minetest.get_content_id("ignore")
424+
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
425+
nodes[i] = ignore
422426
end
423-
local function spiralt(side)
424-
local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)
425-
for i = 1, side do
426-
for j = 1, side do
427-
local id = side^2 - sindex(stop - i + 1,start + j - 1)
428-
ret[id] = {x=i,z=j}
427+
428+
--
429+
local node_id = minetest.get_content_id(nodename)
430+
local stride = {x=1, y=area.ystride, z=area.zstride}
431+
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
432+
local i = offsetz * stride.z + offsety * stride.y + offsetx + 1
433+
434+
--add first column
435+
local column = i
436+
for y = 1, height do
437+
nodes[column] = node_id
438+
column = column + stride.y
439+
end
440+
441+
--add spiral segments
442+
local axis, other = "x", "z"
443+
local sign = 1
444+
local count = height
445+
for segment = 1, length / spacer - 1 do --go through each segment except the last
446+
for index = 1, segment * spacer do --fill segment
447+
i = i + stride[axis] * sign
448+
local column = i
449+
for y = 1, height do --add column
450+
nodes[column] = node_id
451+
column = column + stride.y
429452
end
453+
count = count + height
454+
end
455+
axis, other = other, axis --swap axes
456+
if segment % 2 == 1 then --change sign every other turn
457+
sign = -sign
430458
end
431-
return ret
432459
end
433-
if env == nil then env = minetest.env end
434-
-- connect the joined parts
435-
local spiral = spiralt(width)
436-
height = tonumber(height)
437-
if height < 1 then height = 1 end
438-
spacer = tonumber(spacer)-1
439-
if spacer < 1 then spacer = 1 end
440-
local count = 0
441-
local node = {name=nodename}
442-
local np,lp
443-
for y=0,height do
444-
lp = nil
445-
for _,v in ipairs(spiral) do
446-
np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}
447-
if lp~=nil then
448-
if lp.x~=np.x then
449-
if lp.x<np.x then
450-
for i=lp.x+1,np.x do
451-
env:add_node({x=i, y=np.y, z=np.z}, node)
452-
count = count + 1
453-
end
454-
else
455-
for i=np.x,lp.x-1 do
456-
env:add_node({x=i, y=np.y, z=np.z}, node)
457-
count = count + 1
458-
end
459-
end
460-
end
461-
if lp.z~=np.z then
462-
if lp.z<np.z then
463-
for i=lp.z+1,np.z do
464-
env:add_node({x=np.x, y=np.y, z=i}, node)
465-
count = count + 1
466-
end
467-
else
468-
for i=np.z,lp.z-1 do
469-
env:add_node({x=np.x, y=np.y, z=i}, node)
470-
count = count + 1
471-
end
472-
end
473-
end
474-
end
475-
lp = np
460+
461+
--add shorter final segment
462+
for index = 1, (math.floor(length / spacer) - 2) * spacer do
463+
i = i + stride[axis] * sign
464+
local column = i
465+
for y = 1, height do --add column
466+
nodes[column] = node_id
467+
column = column + stride.y
476468
end
469+
count = count + height
477470
end
471+
print(minetest.serialize(nodes))
472+
--update map nodes
473+
manip:set_data(nodes)
474+
manip:write_to_map()
475+
manip:update_map()
476+
478477
return count
479-
end
478+
end

0 commit comments

Comments
 (0)