1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
--- Functions for creating primitive shapes.
-- @module worldedit.primitives
local mh = worldedit.manip_helpers
--- Adds a cube
-- @param pos Position of ground level center of cube
-- @param width Cube width. (x)
-- @param height Cube height. (y)
-- @param length Cube length. (z)
-- @param node_name Name of node to make cube of.
-- @param hollow Whether the cube should be hollow.
-- @return The number of nodes added.
function worldedit.cube(pos, width, height, length, node_name, hollow)
-- Set up voxel manipulator
local basepos = vector.subtract(pos, {x=math.floor(width/2), y=0, z=math.floor(length/2)})
local manip, area = mh.init(basepos, vector.add(basepos, {x=width, y=height, z=length}))
local data = mh.get_empty_data(area)
-- Add cube
local node_id = minetest.get_content_id(node_name)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = vector.subtract(basepos, area.MinEdge)
local count = 0
for z = 0, length-1 do
local index_z = (offset.z + z) * stride.z + 1 -- +1 for 1-based indexing
for y = 0, height-1 do
local index_y = index_z + (offset.y + y) * stride.y
for x = 0, width-1 do
local is_wall = z == 0 or z == length-1
or y == 0 or y == height-1
or x == 0 or x == width-1
if not hollow or is_wall then
local i = index_y + (offset.x + x)
data[i] = node_id
count = count + 1
end
end
end
end
mh.finish(manip, data)
return count
end
--- Adds a sphere of `node_name` centered at `pos`.
-- @param pos Position to center sphere at.
-- @param radius Sphere radius.
-- @param node_name Name of node to make shere of.
-- @param hollow Whether the sphere should be hollow.
-- @return The number of nodes added.
function worldedit.sphere(pos, radius, node_name, hollow)
local manip, area = mh.init_radius(pos, radius)
local data = mh.get_empty_data(area)
-- Fill selected area with node
local node_id = minetest.get_content_id(node_name)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
local stride_z, stride_y = area.zstride, area.ystride
local count = 0
for z = -radius, radius do
-- Offset contributed by z plus 1 to make it 1-indexed
local new_z = (z + offset_z) * stride_z + 1
for y = -radius, radius do
local new_y = new_z + (y + offset_y) * stride_y
for x = -radius, radius do
local squared = x * x + y * y + z * z
if squared <= max_radius and (not hollow or squared >= min_radius) then
-- Position is on surface of sphere
local i = new_y + (x + offset_x)
data[i] = node_id
count = count + 1
end
end
end
end
mh.finish(manip, data)
return count
end
--- Adds a dome.
-- @param pos Position to center dome at.
-- @param radius Dome radius. Negative for concave domes.
-- @param node_name Name of node to make dome of.
-- @param hollow Whether the dome should be hollow.
-- @return The number of nodes added.
-- TODO: Add axis option.
function worldedit.dome(pos, radius, node_name, hollow)
local min_y, max_y = 0, radius
if radius < 0 then
radius = -radius
min_y, max_y = -radius, 0
end
local manip, area = mh.init_axis_radius(pos, "y", radius)
local data = mh.get_empty_data(area)
-- Add dome
local node_id = minetest.get_content_id(node_name)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
local stride_z, stride_y = area.zstride, area.ystride
local count = 0
for z = -radius, radius do
local new_z = (z + offset_z) * stride_z + 1 --offset contributed by z plus 1 to make it 1-indexed
for y = min_y, max_y do
local new_y = new_z + (y + offset_y) * stride_y
for x = -radius, radius do
local squared = x * x + y * y + z * z
if squared <= max_radius and (not hollow or squared >= min_radius) then
-- Position is in dome
local i = new_y + (x + offset_x)
data[i] = node_id
count = count + 1
end
end
end
end
mh.finish(manip, data)
return count
end
--- Adds a cylinder.
-- @param pos Position to center base of cylinder at.
-- @param axis Axis ("x", "y", or "z")
-- @param length Cylinder length.
-- @param radius1 Cylinder base radius.
-- @param radius2 Cylinder top radius.
-- @param node_name Name of node to make cylinder of.
-- @param hollow Whether the cylinder should be hollow.
-- @return The number of nodes added.
function worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, hollow)
local other1, other2 = worldedit.get_axis_others(axis)
-- Backwards compatibility
if type(radius2) == "string" then
hollow = node_name
node_name = radius2
radius2 = radius1 -- straight cylinder
end
-- Handle negative lengths
local current_pos = {x=pos.x, y=pos.y, z=pos.z}
if length < 0 then
length = -length
current_pos[axis] = current_pos[axis] - length
radius1, radius2 = radius2, radius1
end
-- Set up voxel manipulator
local manip, area = mh.init_axis_radius_length(current_pos, axis, math.max(radius1, radius2), length)
local data = mh.get_empty_data(area)
-- Add desired shape (anything inbetween cylinder & cone)
local node_id = minetest.get_content_id(node_name)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {
x = current_pos.x - area.MinEdge.x,
y = current_pos.y - area.MinEdge.y,
z = current_pos.z - area.MinEdge.z,
}
local count = 0
for i = 0, length - 1 do
-- Calulate radius for this "height" in the cylinder
local radius = radius1 + (radius2 - radius1) * (i + 1) / length
radius = math.floor(radius + 0.5) -- round
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
for index2 = -radius, radius do
-- Offset contributed by other axis 1 plus 1 to make it 1-indexed
local new_index2 = (index2 + offset[other1]) * stride[other1] + 1
for index3 = -radius, radius do
local new_index3 = new_index2 + (index3 + offset[other2]) * stride[other2]
local squared = index2 * index2 + index3 * index3
if squared <= max_radius and (not hollow or squared >= min_radius) then
-- Position is in cylinder, add node here
local vi = new_index3 + (offset[axis] + i) * stride[axis]
data[vi] = node_id
count = count + 1
end
end
end
end
mh.finish(manip, data)
return count
end
--- Adds a pyramid.
-- @param pos Position to center base of pyramid at.
-- @param axis Axis ("x", "y", or "z")
-- @param height Pyramid height.
-- @param node_name Name of node to make pyramid of.
-- @param hollow Whether the pyramid should be hollow.
-- @return The number of nodes added.
function worldedit.pyramid(pos, axis, height, node_name, hollow)
local other1, other2 = worldedit.get_axis_others(axis)
-- Set up voxel manipulator
-- FIXME: passing negative <radius> causes mis-sorted pos to be passed
-- into mh.init() which is technically not allowed but works
local manip, area = mh.init_axis_radius(pos, axis, height)
local data = mh.get_empty_data(area)
-- Handle inverted pyramids
local step
if height > 0 then
height = height - 1
step = 1
else
height = height + 1
step = -1
end
-- Add pyramid
local node_id = minetest.get_content_id(node_name)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {
x = pos.x - area.MinEdge.x,
y = pos.y - area.MinEdge.y,
z = pos.z - area.MinEdge.z,
}
local size = math.abs(height * step)
local count = 0
-- For each level of the pyramid
for index1 = 0, height, step do
-- Offset contributed by axis plus 1 to make it 1-indexed
local new_index1 = (index1 + offset[axis]) * stride[axis] + 1
for index2 = -size, size do
local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1]
for index3 = -size, size do
local i = new_index2 + (index3 + offset[other2]) * stride[other2]
if (not hollow or size - math.abs(index2) < 2 or size - math.abs(index3) < 2) then
data[i] = node_id
count = count + 1
end
end
end
size = size - 1
end
mh.finish(manip, data)
return count
end
--- Adds a spiral.
-- @param pos Position to center spiral at.
-- @param length Spral length.
-- @param height Spiral height.
-- @param spacer Space between walls.
-- @param node_name Name of node to make spiral of.
-- @return Number of nodes added.
-- TODO: Add axis option.
function worldedit.spiral(pos, length, height, spacer, node_name)
local extent = math.ceil(length / 2)
local manip, area = mh.init_axis_radius_length(pos, "y", extent, height)
local data = mh.get_empty_data(area)
-- Set up variables
local node_id = minetest.get_content_id(node_name)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
local i = offset_z * stride.z + offset_y * stride.y + offset_x + 1
-- Add first column
local count = height
local column = i
for y = 1, height do
data[column] = node_id
column = column + stride.y
end
-- Add spiral segments
local stride_axis, stride_other = stride.x, stride.z
local sign = -1
local segment_length = 0
spacer = spacer + 1
-- Go through each segment except the last
for segment = 1, math.floor(length / spacer) * 2 do
-- Change sign and length every other turn starting with the first
if segment % 2 == 1 then
sign = -sign
segment_length = segment_length + spacer
end
-- Fill segment
for index = 1, segment_length do
-- Move along the direction of the segment
i = i + stride_axis * sign
local column = i
-- Add column
for y = 1, height do
data[column] = node_id
column = column + stride.y
end
end
count = count + segment_length * height
stride_axis, stride_other = stride_other, stride_axis -- Swap axes
end
-- Add shorter final segment
sign = -sign
for index = 1, segment_length do
i = i + stride_axis * sign
local column = i
-- Add column
for y = 1, height do
data[column] = node_id
column = column + stride.y
end
end
count = count + segment_length * height
mh.finish(manip, data)
return count
end
|