]> git.lizzy.rs Git - worldedit.git/blob - worldedit/primitives.lua
//pyramid works along any axis and backwards. Working //clearobjects and //cylinder...
[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) --wip: rewrite this using voxelmanip\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         --make area stay loaded\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         manip:read_from_map(pos1, pos2)\r
225 \r
226         --create schematic for single node column along the axis\r
227         local node = {name=nodename, param1=0, param2=0}\r
228         local nodes = {}\r
229         for i = 1, length do\r
230                 nodes[i] = node\r
231         end\r
232         local schematic = {size={[axis]=length, [other1]=1, [other2]=1}, data=nodes}\r
233 \r
234         --add columns in a circle around axis to form cylinder\r
235         local place_schematic = minetest.place_schematic\r
236         local count = 0\r
237         local offset1, offset2 = 0, radius\r
238         local delta = -radius\r
239         while offset1 <= offset2 do\r
240                 --add node at each octant\r
241                 local first1, first2 = pos[other1] + offset1, pos[other1] - offset1\r
242                 local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
243                 currentpos[other1], currentpos[other2] = first1, second1\r
244                 place_schematic(currentpos, schematic) --octant 1\r
245                 currentpos[other1] = first2\r
246                 place_schematic(currentpos, schematic) --octant 4\r
247                 currentpos[other2] = second2\r
248                 place_schematic(currentpos, schematic) --octant 5\r
249                 currentpos[other1] = first1\r
250                 place_schematic(currentpos, schematic) --octant 8\r
251                 local first1, first2 = pos[other1] + offset2, pos[other1] - offset2\r
252                 local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
253                 currentpos[other1], currentpos[other2] = first1, second1\r
254                 place_schematic(currentpos, schematic) --octant 2\r
255                 currentpos[other1] = first2\r
256                 place_schematic(currentpos, schematic) --octant 3\r
257                 currentpos[other2] = second2\r
258                 place_schematic(currentpos, schematic) --octant 6\r
259                 currentpos[other1] = first1\r
260                 place_schematic(currentpos, schematic) --octant 7\r
261 \r
262                 count = count + 8 --wip: broken because sometimes currentpos is repeated\r
263 \r
264                 --move to next location\r
265                 delta = delta + (offset1 * 2) + 1\r
266                 if delta >= 0 then\r
267                         offset2 = offset2 - 1\r
268                         delta = delta - (offset2 * 2)\r
269                 end\r
270                 offset1 = offset1 + 1\r
271         end\r
272         count = count * length --apply the length to the number of nodes\r
273         return count\r
274 end\r
275 \r
276 --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
277 worldedit.cylinder = function(pos, axis, length, radius, nodename)\r
278         local other1, other2\r
279         if axis == "x" then\r
280                 other1, other2 = "y", "z"\r
281         elseif axis == "y" then\r
282                 other1, other2 = "x", "z"\r
283         else --axis == "z"\r
284                 other1, other2 = "x", "y"\r
285         end\r
286 \r
287         --handle negative lengths\r
288         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
289         if length < 0 then\r
290                 length = -length\r
291                 currentpos[axis] = currentpos[axis] - length\r
292         end\r
293 \r
294         --set up voxel manipulator\r
295         local manip = minetest.get_voxel_manip()\r
296         local pos1 = {\r
297                 [axis]=currentpos[axis],\r
298                 [other1]=currentpos[other1] - radius,\r
299                 [other2]=currentpos[other2] - radius\r
300         }\r
301         local pos2 = {\r
302                 [axis]=currentpos[axis] + length - 1,\r
303                 [other1]=currentpos[other1] + radius,\r
304                 [other2]=currentpos[other2] + radius\r
305         }\r
306         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
307         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
308 \r
309         --fill emerged area with ignore\r
310         local nodes = {}\r
311         local ignore = minetest.get_content_id("ignore")\r
312         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
313                 nodes[i] = ignore\r
314         end\r
315 \r
316         --fill selected area with node\r
317         local node_id = minetest.get_content_id(nodename)\r
318         local max_radius = radius * (radius + 1)\r
319         local stride = {x=1, y=area.ystride, z=area.zstride}\r
320         local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}\r
321         local min_slice, max_slice = offset[axis], offset[axis] + length - 1\r
322         local count = 0\r
323         for index2 = -radius, radius do\r
324                 local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed\r
325                 for index3 = -radius, radius do\r
326                         local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]\r
327                         if index2 * index2 + index3 * index3 <= max_radius then\r
328                                 for index1 = min_slice, max_slice do --add column along axis\r
329                                         local i = newindex3 + index1 * stride[axis] + 1\r
330                                         nodes[i] = node_id\r
331                                 end\r
332                                 count = count + length\r
333                         end\r
334                 end\r
335         end\r
336 \r
337         --update map nodes\r
338         manip:set_data(nodes)\r
339         manip:write_to_map()\r
340         manip:update_map()\r
341 \r
342         return count\r
343 end\r
344 \r
345 --adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
346 worldedit.pyramid = function(pos, axis, height, nodename)\r
347         local other1, other2\r
348         if axis == "x" then\r
349                 other1, other2 = "y", "z"\r
350         elseif axis == "y" then\r
351                 other1, other2 = "x", "z"\r
352         else --axis == "z"\r
353                 other1, other2 = "x", "y"\r
354         end\r
355 \r
356         local pos1 = {x=pos.x - height, y=pos.y - height, z=pos.z - height}\r
357         local pos2 = {x=pos.x + height, y=pos.y + height, z=pos.z + height}\r
358 \r
359         --handle inverted pyramids\r
360         local startaxis, endaxis, step\r
361         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
362         if height > 0 then\r
363                 height = height - 1\r
364                 startaxis, endaxis = 0, height\r
365                 step = 1\r
366                 pos1[axis] = pos[axis] --upper half of box\r
367         else\r
368                 height = -height - 1\r
369                 startaxis, endaxis = height, 0\r
370                 step = -1\r
371                 pos2[axis] = pos[axis] + 1 --lower half of box\r
372                 currentpos[axis] = pos[axis] - height --bottom of box\r
373         end\r
374 \r
375         --set up voxel manipulator\r
376         local manip = minetest.get_voxel_manip()\r
377         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
378         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
379 \r
380         --fill emerged area with ignore\r
381         local nodes = {}\r
382         local ignore = minetest.get_content_id("ignore")\r
383         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
384                 nodes[i] = ignore\r
385         end\r
386 \r
387         --fill selected area with node\r
388         local node_id = minetest.get_content_id(nodename)\r
389         local stride = {x=1, y=area.ystride, z=area.zstride}\r
390         local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}\r
391         local count = 0\r
392         for index1 = startaxis, endaxis, step do --go through each level of the pyramid\r
393                 local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed\r
394                 for index2 = -height, height do\r
395                         local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]\r
396                         for index3 = -height, height do\r
397                                 local i = newindex2 + (index3 + offset[other2]) * stride[other2]\r
398                                 nodes[i] = node_id\r
399                         end\r
400                 end\r
401                 count = count + (height * 2 + 1) ^ 2\r
402                 height = height - 1\r
403         end\r
404 \r
405         --update map nodes\r
406         manip:set_data(nodes)\r
407         manip:write_to_map()\r
408         manip:update_map()\r
409 \r
410         return count\r
411 end\r
412 \r
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\r
414 worldedit.spiral = function(pos, width, height, spacer, nodename, env) --wip: rewrite this whole thing, nobody can understand it anyways\r
415         -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua\r
416         local abs = math.abs\r
417         local sign = function(s) return s ~= 0 and s / av(s) or 0 end\r
418         local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards\r
419                 if z == -x and z >= x then return (2*z+1)^2 end\r
420                 local longest = math.max(abs(z), abs(x))\r
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\r
422         end\r
423         local function spiralt(side)\r
424                 local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)\r
425                 for i = 1, side do\r
426                         for j = 1, side do\r
427                                 local id = side^2 - sindex(stop - i + 1,start + j - 1)\r
428                                 ret[id] = {x=i,z=j}\r
429                         end\r
430                 end\r
431                 return ret\r
432         end\r
433         if env == nil then env = minetest.env end\r
434         -- connect the joined parts\r
435         local spiral = spiralt(width)\r
436         height = tonumber(height)\r
437         if height < 1 then height = 1 end\r
438         spacer = tonumber(spacer)-1\r
439         if spacer < 1 then spacer = 1 end\r
440         local count = 0\r
441         local node = {name=nodename}\r
442         local np,lp\r
443         for y=0,height do\r
444                 lp = nil\r
445                 for _,v in ipairs(spiral) do\r
446                         np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}\r
447                         if lp~=nil then\r
448                                 if lp.x~=np.x then \r
449                                         if lp.x<np.x then \r
450                                                 for i=lp.x+1,np.x do\r
451                                                         env:add_node({x=i, y=np.y, z=np.z}, node)\r
452                                                         count = count + 1\r
453                                                 end\r
454                                         else\r
455                                                 for i=np.x,lp.x-1 do\r
456                                                         env:add_node({x=i, y=np.y, z=np.z}, node)\r
457                                                         count = count + 1\r
458                                                 end\r
459                                         end\r
460                                 end\r
461                                 if lp.z~=np.z then \r
462                                         if lp.z<np.z then \r
463                                                 for i=lp.z+1,np.z do\r
464                                                         env:add_node({x=np.x, y=np.y, z=i}, node)\r
465                                                         count = count + 1\r
466                                                 end\r
467                                         else\r
468                                                 for i=np.z,lp.z-1 do\r
469                                                         env:add_node({x=np.x, y=np.y, z=i}, node)\r
470                                                         count = count + 1\r
471                                                 end\r
472                                         end\r
473                                 end\r
474                         end\r
475                         lp = np\r
476                 end\r
477         end\r
478         return count\r
479 end\r