]> git.lizzy.rs Git - worldedit.git/blob - worldedit/primitives.lua
Even bigger speed gains by using LuaVoxelManipulator in a few choice places! Faster...
[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         manip:read_from_map(\r
9                 {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius},\r
10                 {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius},\r
11         )\r
12 \r
13         local insert = table.insert\r
14         local node_id = minetest.get_content_id(nodename)\r
15         local ignore_id = minetest.get_content_id("ignore")\r
16         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
17         local nodes = {}\r
18         local count = 0\r
19         for x = -radius, radius do\r
20                 for y = -radius, radius do\r
21                         for z = -radius, radius do\r
22                                 local squared = x * x + y * y + z * z\r
23                                 if squared >= min_radius and squared <= max_radius then --surface of sphere\r
24                                         insert(nodes, node_id)\r
25                                         count = count + 1\r
26                                 else\r
27                                         insert(nodes, ignore_id)\r
28                                 end\r
29                         end\r
30                 end\r
31         end\r
32 \r
33         --update map nodes\r
34         manip:set_data(nodes)\r
35         manip:write_to_map()\r
36         manip:update_map()\r
37 \r
38         return count\r
39 end\r
40 \r
41 --adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
42 worldedit.sphere = function(pos, radius, nodename)\r
43         --set up voxel manipulator\r
44         local manip = minetest.get_voxel_manip()\r
45         manip:read_from_map(\r
46                 {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius},\r
47                 {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius},\r
48         )\r
49 \r
50         local insert = table.insert\r
51         local node_id = minetest.get_content_id(nodename)\r
52         local ignore_id = minetest.get_content_id("ignore")\r
53         local max_radius = radius * (radius + 1)\r
54         local nodes = {}\r
55         local count = 0\r
56         for x = -radius, radius do\r
57                 for y = -radius, radius do\r
58                         for z = -radius, radius do\r
59                                 if x * x + y * y + z * z <= max_radius then --inside sphere\r
60                                         insert(nodes, node_id)\r
61                                         count = count + 1\r
62                                 else\r
63                                         insert(nodes, ignore_id)\r
64                                 end\r
65                         end\r
66                 end\r
67         end\r
68 \r
69         --update map nodes\r
70         manip:set_data(nodes)\r
71         manip:write_to_map()\r
72         manip:update_map()\r
73 \r
74         return count\r
75 end\r
76 \r
77 --adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
78 worldedit.hollow_dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
79         --set up voxel manipulator\r
80         local manip = minetest.get_voxel_manip()\r
81         manip:read_from_map(\r
82                 {x=pos.x - radius, y=pos.y, z=pos.z - radius},\r
83                 {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius},\r
84         )\r
85 \r
86         local insert = table.insert\r
87         local node_id = minetest.get_content_id(nodename)\r
88         local ignore_id = minetest.get_content_id("ignore")\r
89         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
90         local nodes = {}\r
91         local count = 0\r
92         for x = -radius, radius do\r
93                 for y = 0, radius do\r
94                         for z = -radius, radius do\r
95                                 local squared = x * x + y * y + z * z\r
96                                 if squared >= min_radius and squared <= max_radius then --surface of dome\r
97                                         insert(nodes, node_id)\r
98                                         count = count + 1\r
99                                 else\r
100                                         insert(nodes, ignore_id)\r
101                                 end\r
102                         end\r
103                 end\r
104         end\r
105 \r
106         --update map nodes\r
107         manip:set_data(nodes)\r
108         manip:write_to_map()\r
109         manip:update_map()\r
110 \r
111         return count\r
112 end\r
113 \r
114 --adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
115 worldedit.dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
116         --set up voxel manipulator\r
117         local manip = minetest.get_voxel_manip()\r
118         manip:read_from_map(\r
119                 {x=pos.x - radius, y=pos.y, z=pos.z - radius},\r
120                 {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius},\r
121         )\r
122 \r
123         local insert = table.insert\r
124         local node_id = minetest.get_content_id(nodename)\r
125         local ignore_id = minetest.get_content_id("ignore")\r
126         local max_radius = radius * (radius + 1)\r
127         local nodes = {}\r
128         local count = 0\r
129         for x = -radius, radius do\r
130                 for y = 0, radius do\r
131                         for z = -radius, radius do\r
132                                 if x * x + y * y + z * z <= max_radius then --inside dome\r
133                                         insert(nodes, node_id)\r
134                                         count = count + 1\r
135                                 else\r
136                                         insert(nodes, ignore_id)\r
137                                 end\r
138                         end\r
139                 end\r
140         end\r
141 \r
142         --update map nodes\r
143         manip:set_data(nodes)\r
144         manip:write_to_map()\r
145         manip:update_map()\r
146 \r
147         return count\r
148 end\r
149 \r
150 --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
151 worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)\r
152         local other1, other2\r
153         if axis == "x" then\r
154                 other1, other2 = "y", "z"\r
155         elseif axis == "y" then\r
156                 other1, other2 = "x", "z"\r
157         else --axis == "z"\r
158                 other1, other2 = "x", "y"\r
159         end\r
160 \r
161         --handle negative lengths\r
162         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
163         if length < 0 then\r
164                 length = -length\r
165                 currentpos[axis] = currentpos[axis] - length\r
166         end\r
167 \r
168         --create schematic for single node column along the axis\r
169         local node = {name=nodename, param1=0, param2=0}\r
170         local nodes = {}\r
171         for i = 1, length do\r
172                 nodes[i] = node\r
173         end\r
174         local schematic = {size={[axis]=length, [other1]=1, [other2]=1}, data=nodes}\r
175 \r
176         --add columns in a circle around axis to form cylinder\r
177         local place_schematic = minetest.place_schematic\r
178         local count = 0\r
179         local offset1, offset2 = 0, radius\r
180         local delta = -radius\r
181         while offset1 <= offset2 do\r
182                 --add node at each octant\r
183                 local first1, first2 = pos[other1] + offset1, pos[other1] - offset1\r
184                 local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
185                 currentpos[other1], currentpos[other2] = first1, second1\r
186                 place_schematic(currentpos, schematic) --octant 1\r
187                 currentpos[other1] = first2\r
188                 place_schematic(currentpos, schematic) --octant 4\r
189                 currentpos[other2] = second2\r
190                 place_schematic(currentpos, schematic) --octant 5\r
191                 currentpos[other1] = first1\r
192                 place_schematic(currentpos, schematic) --octant 8\r
193                 local first1, first2 = pos[other1] + offset2, pos[other1] - offset2\r
194                 local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
195                 currentpos[other1], currentpos[other2] = first1, second1\r
196                 place_schematic(currentpos, schematic) --octant 2\r
197                 currentpos[other1] = first2\r
198                 place_schematic(currentpos, schematic) --octant 3\r
199                 currentpos[other2] = second2\r
200                 place_schematic(currentpos, schematic) --octant 6\r
201                 currentpos[other1] = first1\r
202                 place_schematic(currentpos, schematic) --octant 7\r
203 \r
204                 count = count + 8 --wip: broken because sometimes currentpos is repeated\r
205 \r
206                 --move to next location\r
207                 delta = delta + (offset1 * 2) + 1\r
208                 if delta >= 0 then\r
209                         offset2 = offset2 - 1\r
210                         delta = delta - (offset2 * 2)\r
211                 end\r
212                 offset1 = offset1 + 1\r
213         end\r
214         count = count * length --apply the length to the number of nodes\r
215         return count\r
216 end\r
217 \r
218 --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
219 worldedit.cylinder = function(pos, axis, length, radius, nodename, env)\r
220         local other1, other2\r
221         if axis == "x" then\r
222                 other1, other2 = "y", "z"\r
223         elseif axis == "y" then\r
224                 other1, other2 = "x", "z"\r
225         else --axis == "z"\r
226                 other1, other2 = "x", "y"\r
227         end\r
228 \r
229         --handle negative lengths\r
230         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
231         if length < 0 then\r
232                 length = -length\r
233                 currentpos[axis] = currentpos[axis] - length\r
234         end\r
235 \r
236         --create schematic for single node column along the axis\r
237         local node = {name=nodename, param1=0, param2=0}\r
238         local nodes = {}\r
239         for i = 1, length do\r
240                 nodes[i] = node\r
241         end\r
242         local schematic = {size={[axis]=length, [other1]=1, [other2]=1}, data=nodes}\r
243 \r
244         local place_schematic = minetest.place_schematic\r
245         local count = 0\r
246         local offset1, offset2 = 0, radius\r
247         local delta = -radius\r
248         while offset1 <= offset2 do\r
249                 --connect each pair of octants taking advantage of symmetry along two axes\r
250                 currentpos[other1] = pos[other1] - offset1\r
251                 local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
252                 for i = 0, offset1 * 2 do\r
253                         currentpos[other2] = second1\r
254                         place_schematic(currentpos, schematic) --octant 1 to 4\r
255                         currentpos[other2] = second2\r
256                         place_schematic(currentpos, schematic) --octant 5 to 8\r
257                         currentpos[other1] = currentpos[other1] + 1\r
258                 end\r
259                 currentpos[other1] = pos[other1] - offset2\r
260                 local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
261                 for i = 0, offset2 * 2 do\r
262                         currentpos[other2] = second1\r
263                         place_schematic(currentpos, schematic) --octant 2 to 3\r
264                         currentpos[other2] = second2\r
265                         place_schematic(currentpos, schematic) --octant 6 to 7\r
266                         currentpos[other1] = currentpos[other1] + 1\r
267                 end\r
268 \r
269                 count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken since node positions may coincide\r
270 \r
271                 --move to next location\r
272                 delta = delta + (offset1 * 2) + 1\r
273                 offset1 = offset1 + 1\r
274                 if delta >= 0 then\r
275                         offset2 = offset2 - 1\r
276                         delta = delta - (offset2 * 2)\r
277                 end\r
278         end\r
279         count = count * length --apply the length to the number of nodes\r
280         return count\r
281 end\r
282 \r
283 --adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
284 worldedit.pyramid = function(pos, height, nodename, env)\r
285         local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height\r
286         local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height\r
287         local pos = {x=0, y=pos1y, z=0}\r
288 \r
289         --wip: make this faster using base sized schematics that are then resized while moving upwards, or if that's not possible, add new rows/columns while looping\r
290         local count = 0\r
291         local node = {name=nodename}\r
292         if env == nil then env = minetest.env end\r
293         while pos.y <= pos2y do --each vertical level of the pyramid\r
294                 pos.x = pos1x\r
295                 while pos.x <= pos2x do\r
296                         pos.z = pos1z\r
297                         while pos.z <= pos2z do\r
298                                 env:add_node(pos, node)\r
299                                 pos.z = pos.z + 1\r
300                         end\r
301                         pos.x = pos.x + 1\r
302                 end\r
303                 count = count + ((pos2y - pos.y) * 2 + 1) ^ 2\r
304                 pos.y = pos.y + 1\r
305 \r
306                 pos1x, pos2x = pos1x + 1, pos2x - 1\r
307                 pos1z, pos2z = pos1z + 1, pos2z - 1\r
308 \r
309         end\r
310         return count\r
311 end\r
312 \r
313 --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
314 worldedit.spiral = function(pos, width, height, spacer, nodename, env) --wip: clean this up\r
315         -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua\r
316         --wip: rewrite this whole thing, nobody can understand it anyways\r
317         av, sn = math.abs, function(s) return s~=0 and s/av(s) or 0 end\r
318         local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards\r
319                 if z == -x and z >= x then return (2*z+1)^2 end\r
320                 local l = math.max(av(z), av(x))\r
321                 return (2*l-1)^2+4*l+2*l*sn(x+z)+sn(z^2-x^2)*(l-(av(z)==l and sn(z)*x or sn(x)*z)) -- OH GOD WHAT\r
322         end\r
323         local function spiralt(side)\r
324                 local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)\r
325                 for i = 1, side do\r
326                         for j = 1, side do\r
327                                 local id = side^2 - sindex(stop - i + 1,start + j - 1)\r
328                                 ret[id] = {x=i,z=j}\r
329                         end\r
330                 end\r
331                 return ret\r
332         end\r
333         if env == nil then env = minetest.env end\r
334         -- connect the joined parts\r
335         local spiral = spiralt(width)\r
336         height = tonumber(height)\r
337         if height < 1 then height = 1 end\r
338         spacer = tonumber(spacer)-1\r
339         if spacer < 1 then spacer = 1 end\r
340         local count = 0\r
341         local node = {name=nodename}\r
342         local np,lp\r
343         for y=0,height do\r
344                 lp = nil\r
345                 for _,v in ipairs(spiral) do\r
346                         np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}\r
347                         if lp~=nil then\r
348                                 if lp.x~=np.x then \r
349                                         if lp.x<np.x then \r
350                                                 for i=lp.x+1,np.x do\r
351                                                         env:add_node({x=i, y=np.y, z=np.z}, node)\r
352                                                         count = count + 1\r
353                                                 end\r
354                                         else\r
355                                                 for i=np.x,lp.x-1 do\r
356                                                         env:add_node({x=i, y=np.y, z=np.z}, node)\r
357                                                         count = count + 1\r
358                                                 end\r
359                                         end\r
360                                 end\r
361                                 if lp.z~=np.z then \r
362                                         if lp.z<np.z then \r
363                                                 for i=lp.z+1,np.z do\r
364                                                         env:add_node({x=np.x, y=np.y, z=i}, node)\r
365                                                         count = count + 1\r
366                                                 end\r
367                                         else\r
368                                                 for i=np.z,lp.z-1 do\r
369                                                         env:add_node({x=np.x, y=np.y, z=i}, node)\r
370                                                         count = count + 1\r
371                                                 end\r
372                                         end\r
373                                 end\r
374                         end\r
375                         lp = np\r
376                 end\r
377         end\r
378         return count\r
379 end\r