]> git.lizzy.rs Git - worldedit.git/blob - worldedit/primitives.lua
Improve worldedit.spiral and the WorldEdit GUI,
[worldedit.git] / worldedit / primitives.lua
1 worldedit = worldedit or {}\r
2 local minetest = minetest --local copy of global\r
3 \r
4 --adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
5 worldedit.hollow_sphere = function(pos, radius, nodename)\r
6         --set up voxel manipulator\r
7         local manip = minetest.get_voxel_manip()\r
8         local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}\r
9         local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}\r
10         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
11         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
12 \r
13         --fill emerged area with ignore\r
14         local nodes = {}\r
15         local ignore = minetest.get_content_id("ignore")\r
16         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
17                 nodes[i] = ignore\r
18         end\r
19 \r
20         --fill selected area with node\r
21         local node_id = minetest.get_content_id(nodename)\r
22         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
23         local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z\r
24         local zstride, ystride = area.zstride, area.ystride\r
25         local count = 0\r
26         for z = -radius, radius do\r
27                 local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed\r
28                 for y = -radius, radius do\r
29                         local newy = newz + (y + offsety) * ystride\r
30                         for x = -radius, radius do\r
31                                 local squared = x * x + y * y + z * z\r
32                                 if squared >= min_radius and squared <= max_radius then --position is on surface of sphere\r
33                                         local i = newy + (x + offsetx)\r
34                                         nodes[i] = node_id\r
35                                         count = count + 1\r
36                                 end\r
37                         end\r
38                 end\r
39         end\r
40 \r
41         --update map nodes\r
42         manip:set_data(nodes)\r
43         manip:write_to_map()\r
44         manip:update_map()\r
45 \r
46         return count\r
47 end\r
48 \r
49 --adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
50 worldedit.sphere = function(pos, radius, nodename)\r
51         --set up voxel manipulator\r
52         local manip = minetest.get_voxel_manip()\r
53         local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}\r
54         local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}\r
55         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
56         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
57 \r
58         --fill emerged area with ignore\r
59         local nodes = {}\r
60         local ignore = minetest.get_content_id("ignore")\r
61         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
62                 nodes[i] = ignore\r
63         end\r
64 \r
65         --fill selected area with node\r
66         local node_id = minetest.get_content_id(nodename)\r
67         local max_radius = radius * (radius + 1)\r
68         local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z\r
69         local zstride, ystride = area.zstride, area.ystride\r
70         local count = 0\r
71         for z = -radius, radius do\r
72                 local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed\r
73                 for y = -radius, radius do\r
74                         local newy = newz + (y + offsety) * ystride\r
75                         for x = -radius, radius do\r
76                                 if x * x + y * y + z * z <= max_radius then --position is inside sphere\r
77                                         local i = newy + (x + offsetx)\r
78                                         nodes[i] = node_id\r
79                                         count = count + 1\r
80                                 end\r
81                         end\r
82                 end\r
83         end\r
84 \r
85         --update map nodes\r
86         manip:set_data(nodes)\r
87         manip:write_to_map()\r
88         manip:update_map()\r
89 \r
90         return count\r
91 end\r
92 \r
93 --adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
94 worldedit.hollow_dome = function(pos, radius, nodename)\r
95         --set up voxel manipulator\r
96         local manip = minetest.get_voxel_manip()\r
97         local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}\r
98         local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}\r
99         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
100         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
101 \r
102         --fill emerged area with ignore\r
103         local nodes = {}\r
104         local ignore = minetest.get_content_id("ignore")\r
105         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
106                 nodes[i] = ignore\r
107         end\r
108 \r
109         local miny, maxy = 0, radius\r
110         if radius < 0 then\r
111                 radius = -radius\r
112                 miny, maxy = -radius, 0\r
113         end\r
114 \r
115         --fill selected area with node\r
116         local node_id = minetest.get_content_id(nodename)\r
117         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
118         local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z\r
119         local zstride, ystride = area.zstride, area.ystride\r
120         local count = 0\r
121         for z = -radius, radius do\r
122                 local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed\r
123                 for y = miny, maxy do\r
124                         local newy = newz + (y + offsety) * ystride\r
125                         for x = -radius, radius do\r
126                                 local squared = x * x + y * y + z * z\r
127                                 if squared >= min_radius and squared <= max_radius then --position is on surface of sphere\r
128                                         local i = newy + (x + offsetx)\r
129                                         nodes[i] = node_id\r
130                                         count = count + 1\r
131                                 end\r
132                         end\r
133                 end\r
134         end\r
135 \r
136         --update map nodes\r
137         manip:set_data(nodes)\r
138         manip:write_to_map()\r
139         manip:update_map()\r
140 \r
141         return count\r
142 end\r
143 \r
144 --adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
145 worldedit.dome = function(pos, radius, nodename)\r
146         --set up voxel manipulator\r
147         local manip = minetest.get_voxel_manip()\r
148         local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}\r
149         local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}\r
150         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
151         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
152 \r
153         --fill emerged area with ignore\r
154         local nodes = {}\r
155         local ignore = minetest.get_content_id("ignore")\r
156         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
157                 nodes[i] = ignore\r
158         end\r
159 \r
160         local miny, maxy = 0, radius\r
161         if radius < 0 then\r
162                 radius = -radius\r
163                 miny, maxy = -radius, 0\r
164         end\r
165 \r
166         --fill selected area with node\r
167         local node_id = minetest.get_content_id(nodename)\r
168         local max_radius = radius * (radius + 1)\r
169         local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z\r
170         local zstride, ystride = area.zstride, area.ystride\r
171         local count = 0\r
172         for z = -radius, radius do\r
173                 local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed\r
174                 for y = miny, maxy do\r
175                         local newy = newz + (y + offsety) * ystride\r
176                         for x = -radius, radius do\r
177                                 if x * x + y * y + z * z <= max_radius then --position is inside sphere\r
178                                         local i = newy + (x + offsetx)\r
179                                         nodes[i] = node_id\r
180                                         count = count + 1\r
181                                 end\r
182                         end\r
183                 end\r
184         end\r
185 \r
186         --update map nodes\r
187         manip:set_data(nodes)\r
188         manip:write_to_map()\r
189         manip:update_map()\r
190 \r
191         return count\r
192 end\r
193 \r
194 --adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added\r
195 worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)\r
196         local other1, other2\r
197         if axis == "x" then\r
198                 other1, other2 = "y", "z"\r
199         elseif axis == "y" then\r
200                 other1, other2 = "x", "z"\r
201         else --axis == "z"\r
202                 other1, other2 = "x", "y"\r
203         end\r
204 \r
205         --handle negative lengths\r
206         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
207         if length < 0 then\r
208                 length = -length\r
209                 currentpos[axis] = currentpos[axis] - length\r
210         end\r
211 \r
212         --set up voxel manipulator\r
213         local manip = minetest.get_voxel_manip()\r
214         local pos1 = {\r
215                 [axis]=currentpos[axis],\r
216                 [other1]=currentpos[other1] - radius,\r
217                 [other2]=currentpos[other2] - radius\r
218         }\r
219         local pos2 = {\r
220                 [axis]=currentpos[axis] + length - 1,\r
221                 [other1]=currentpos[other1] + radius,\r
222                 [other2]=currentpos[other2] + radius\r
223         }\r
224         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
225         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
226 \r
227         --fill emerged area with ignore\r
228         local nodes = {}\r
229         local ignore = minetest.get_content_id("ignore")\r
230         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
231                 nodes[i] = ignore\r
232         end\r
233 \r
234         --fill selected area with node\r
235         local node_id = minetest.get_content_id(nodename)\r
236         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
237         local stride = {x=1, y=area.ystride, z=area.zstride}\r
238         local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}\r
239         local min_slice, max_slice = offset[axis], offset[axis] + length - 1\r
240         local count = 0\r
241         for index2 = -radius, radius do\r
242                 local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed\r
243                 for index3 = -radius, radius do\r
244                         local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]\r
245                         local squared = index2 * index2 + index3 * index3\r
246                         if squared >= min_radius and squared <= max_radius then --position is on surface of cylinder\r
247                                 for index1 = min_slice, max_slice do --add column along axis\r
248                                         local i = newindex3 + index1 * stride[axis]\r
249                                         nodes[i] = node_id\r
250                                 end\r
251                                 count = count + length\r
252                         end\r
253                 end\r
254         end\r
255 \r
256         --update map nodes\r
257         manip:set_data(nodes)\r
258         manip:write_to_map()\r
259         manip:update_map()\r
260 \r
261         return count\r
262 end\r
263 \r
264 --adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added\r
265 worldedit.cylinder = function(pos, axis, length, radius, nodename)\r
266         local other1, other2\r
267         if axis == "x" then\r
268                 other1, other2 = "y", "z"\r
269         elseif axis == "y" then\r
270                 other1, other2 = "x", "z"\r
271         else --axis == "z"\r
272                 other1, other2 = "x", "y"\r
273         end\r
274 \r
275         --handle negative lengths\r
276         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
277         if length < 0 then\r
278                 length = -length\r
279                 currentpos[axis] = currentpos[axis] - length\r
280         end\r
281 \r
282         --set up voxel manipulator\r
283         local manip = minetest.get_voxel_manip()\r
284         local pos1 = {\r
285                 [axis]=currentpos[axis],\r
286                 [other1]=currentpos[other1] - radius,\r
287                 [other2]=currentpos[other2] - radius\r
288         }\r
289         local pos2 = {\r
290                 [axis]=currentpos[axis] + length - 1,\r
291                 [other1]=currentpos[other1] + radius,\r
292                 [other2]=currentpos[other2] + radius\r
293         }\r
294         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
295         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
296 \r
297         --fill emerged area with ignore\r
298         local nodes = {}\r
299         local ignore = minetest.get_content_id("ignore")\r
300         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
301                 nodes[i] = ignore\r
302         end\r
303 \r
304         --fill selected area with node\r
305         local node_id = minetest.get_content_id(nodename)\r
306         local max_radius = radius * (radius + 1)\r
307         local stride = {x=1, y=area.ystride, z=area.zstride}\r
308         local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}\r
309         local min_slice, max_slice = offset[axis], offset[axis] + length - 1\r
310         local count = 0\r
311         for index2 = -radius, radius do\r
312                 local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed\r
313                 for index3 = -radius, radius do\r
314                         local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]\r
315                         if index2 * index2 + index3 * index3 <= max_radius then --position is within cylinder\r
316                                 for index1 = min_slice, max_slice do --add column along axis\r
317                                         local i = newindex3 + index1 * stride[axis]\r
318                                         nodes[i] = node_id\r
319                                 end\r
320                                 count = count + length\r
321                         end\r
322                 end\r
323         end\r
324 \r
325         --update map nodes\r
326         manip:set_data(nodes)\r
327         manip:write_to_map()\r
328         manip:update_map()\r
329 \r
330         return count\r
331 end\r
332 \r
333 --adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
334 worldedit.pyramid = function(pos, axis, height, nodename)\r
335         local other1, other2\r
336         if axis == "x" then\r
337                 other1, other2 = "y", "z"\r
338         elseif axis == "y" then\r
339                 other1, other2 = "x", "z"\r
340         else --axis == "z"\r
341                 other1, other2 = "x", "y"\r
342         end\r
343 \r
344         local pos1 = {x=pos.x - height, y=pos.y - height, z=pos.z - height}\r
345         local pos2 = {x=pos.x + height, y=pos.y + height, z=pos.z + height}\r
346 \r
347         --handle inverted pyramids\r
348         local startaxis, endaxis, step\r
349         if height > 0 then\r
350                 height = height - 1\r
351                 step = 1\r
352                 pos1[axis] = pos[axis] --upper half of box\r
353         else\r
354                 height = height + 1\r
355                 step = -1\r
356                 pos2[axis] = pos[axis] --lower half of box\r
357         end\r
358 \r
359         --set up voxel manipulator\r
360         local manip = minetest.get_voxel_manip()\r
361         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
362         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
363 \r
364         --fill emerged area with ignore\r
365         local nodes = {}\r
366         local ignore = minetest.get_content_id("ignore")\r
367         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
368                 nodes[i] = ignore\r
369         end\r
370 \r
371         --fill selected area with node\r
372         local node_id = minetest.get_content_id(nodename)\r
373         local stride = {x=1, y=area.ystride, z=area.zstride}\r
374         local offset = {x=pos.x - emerged_pos1.x, y=pos.y - emerged_pos1.y, z=pos.z - emerged_pos1.z}\r
375         local size = height * step\r
376         local count = 0\r
377         for index1 = 0, height, step do --go through each level of the pyramid\r
378                 local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed\r
379                 for index2 = -size, size do\r
380                         local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]\r
381                         for index3 = -size, size do\r
382                                 local i = newindex2 + (index3 + offset[other2]) * stride[other2]\r
383                                 nodes[i] = node_id\r
384                         end\r
385                 end\r
386                 count = count + (size * 2 + 1) ^ 2\r
387                 size = size - 1\r
388         end\r
389 \r
390         --update map nodes\r
391         manip:set_data(nodes)\r
392         manip:write_to_map()\r
393         manip:update_map()\r
394 \r
395         return count\r
396 end\r
397 \r
398 --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\r
399 worldedit.spiral = function(pos, length, height, spacer, nodename)\r
400         local extent = math.ceil(length / 2)\r
401         local pos1 = {x=pos.x - extent, y=pos.y, z=pos.z - extent}\r
402         local pos2 = {x=pos.x + extent, y=pos.y + height, z=pos.z + extent}\r
403 \r
404         --set up voxel manipulator\r
405         local manip = minetest.get_voxel_manip()\r
406         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
407         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
408 \r
409         --fill emerged area with ignore\r
410         local nodes = {}\r
411         local ignore = minetest.get_content_id("ignore")\r
412         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
413                 nodes[i] = ignore\r
414         end\r
415 \r
416         --set up variables\r
417         local node_id = minetest.get_content_id(nodename)\r
418         local stride = {x=1, y=area.ystride, z=area.zstride}\r
419         local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z\r
420         local i = offsetz * stride.z + offsety * stride.y + offsetx + 1\r
421 \r
422         --add first column\r
423         local count = height\r
424         local column = i\r
425         for y = 1, height do\r
426                 nodes[column] = node_id\r
427                 column = column + stride.y\r
428         end\r
429 \r
430         --add spiral segments\r
431         local strideaxis, strideother = stride.x, stride.z\r
432         local sign = -1\r
433         local segment_length = 0\r
434         spacer = spacer + 1\r
435         for segment = 1, math.floor(length / spacer) * 2 do --go through each segment except the last\r
436                 if segment % 2 == 1 then --change sign and length every other turn starting with the first\r
437                         sign = -sign\r
438                         segment_length = segment_length + spacer\r
439                 end\r
440                 for index = 1, segment_length do --fill segment\r
441                         i = i + strideaxis * sign --move along the direction of the segment\r
442                         local column = i\r
443                         for y = 1, height do --add column\r
444                                 nodes[column] = node_id\r
445                                 column = column + stride.y\r
446                         end\r
447                 end\r
448                 count = count + segment_length * height\r
449                 strideaxis, strideother = strideother, strideaxis --swap axes\r
450         end\r
451 \r
452         --add shorter final segment\r
453         sign = -sign\r
454         for index = 1, segment_length do\r
455                 i = i + strideaxis * sign\r
456                 local column = i\r
457                 for y = 1, height do --add column\r
458                         nodes[column] = node_id\r
459                         column = column + stride.y\r
460                 end\r
461         end\r
462         count = count + segment_length * height\r
463 \r
464         --update map nodes\r
465         manip:set_data(nodes)\r
466         manip:write_to_map()\r
467         manip:update_map()\r
468 \r
469         return count\r
470 end