]> git.lizzy.rs Git - worldedit.git/blob - worldedit/primitives.lua
Avoid using slower and deprecated EnvRef, fix schematic data MapNodes, huge speed...
[worldedit.git] / worldedit / primitives.lua
1 worldedit = worldedit or {}\r
2 \r
3 --adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
4 worldedit.hollow_sphere = function(pos, radius, nodename)\r
5         local insert = table.insert\r
6         local node = {name=nodename, param1=0, param2=0}\r
7         local ignore = {name="ignore", param1=0, param2=0}\r
8         local nodes = {}\r
9         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
10         for x = -radius, radius do\r
11                 for y = -radius, radius do\r
12                         for z = -radius, radius do\r
13                                 local squared = x * x + y * y + z * z\r
14                                 if squared >= min_radius and squared <= max_radius then\r
15                                         insert(nodes, node)\r
16                                 else\r
17                                         insert(nodes, ignore)\r
18                                 end\r
19                         end\r
20                 end\r
21         end\r
22         minetest.place_schematic({x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}, {size={x=radius * 2, y=radius * 2, z=radius * 2}, data=nodes})\r
23         return #nodes\r
24 end\r
25 \r
26 --adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
27 worldedit.sphere = function(pos, radius, nodename)\r
28         local insert = table.insert\r
29         local node = {name=nodename, param1=0, param2=0}\r
30         local ignore = {name="ignore", param1=0, param2=0}\r
31         local nodes = {}\r
32         local max_radius = radius * (radius + 1)\r
33         for x = -radius, radius do\r
34                 for y = -radius, radius do\r
35                         for z = -radius, radius do\r
36                                 if x * x + y * y + z * z <= max_radius then\r
37                                         insert(nodes, node)\r
38                                 else\r
39                                         insert(nodes, ignore)\r
40                                 end\r
41                         end\r
42                 end\r
43         end\r
44         minetest.place_schematic({x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}, {size={x=radius * 2, y=radius * 2, z=radius * 2}, data=nodes})\r
45         return #nodes\r
46 end\r
47 \r
48 --adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
49 worldedit.hollow_dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
50         local insert = table.insert\r
51         local node = {name=nodename, param1=0, param2=0}\r
52         local ignore = {name="ignore", param1=0, param2=0}\r
53         local nodes = {}\r
54         local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)\r
55         for x = -radius, radius do\r
56                 for y = 0, radius do\r
57                         for z = -radius, radius do\r
58                                 local squared = x * x + y * y + z * z\r
59                                 if squared >= min_radius and squared <= max_radius then\r
60                                         insert(nodes, node)\r
61                                 else\r
62                                         insert(nodes, ignore)\r
63                                 end\r
64                         end\r
65                 end\r
66         end\r
67         minetest.place_schematic({x=pos.x - radius, y=pos.y, z=pos.z - radius}, {size={x=radius * 2, y=radius * 2, z=radius * 2}, data=nodes})\r
68         return #nodes\r
69 end\r
70 \r
71 --adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
72 worldedit.dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
73         local insert = table.insert\r
74         local node = {name=nodename, param1=0, param2=0}\r
75         local ignore = {name="ignore", param1=0, param2=0}\r
76         local nodes = {}\r
77         local max_radius = radius * (radius + 1)\r
78         for x = -radius, radius do\r
79                 for y = 0, radius do\r
80                         for z = -radius, radius do\r
81                                 if x * x + y * y + z * z <= max_radius then\r
82                                         insert(nodes, node)\r
83                                 else\r
84                                         insert(nodes, ignore)\r
85                                 end\r
86                         end\r
87                 end\r
88         end\r
89         minetest.place_schematic({x=pos.x - radius, y=pos.y, z=pos.z - radius}, {size={x=radius * 2, y=radius * 2, z=radius * 2}, data=nodes})\r
90         return #nodes\r
91 end\r
92 \r
93 --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
94 worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)\r
95         local other1, other2\r
96         if axis == "x" then\r
97                 other1, other2 = "y", "z"\r
98         elseif axis == "y" then\r
99                 other1, other2 = "x", "z"\r
100         else --axis == "z"\r
101                 other1, other2 = "x", "y"\r
102         end\r
103 \r
104         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
105         if length < 0 then\r
106                 length = -length\r
107                 currentpos[axis] = currentpos[axis] - length\r
108         end\r
109 \r
110         --create schematic for single node column along the axis\r
111         local node = {name=nodename, param1=0, param2=0}\r
112         local nodes = {}\r
113         for i = 1, length do\r
114                 nodes[i] = node\r
115         end\r
116         local schematic = {size={[axis]=length, [other1]=1, [other2]=1}, data=nodes}\r
117 \r
118         --add columns in a circle around axis to form cylinder\r
119         local place_schematic = minetest.place_schematic\r
120         local count = 0\r
121         local offset1, offset2 = 0, radius\r
122         local delta = -radius\r
123         while offset1 <= offset2 do\r
124                 --add node at each octant\r
125                 local first1, first2 = pos[other1] + offset1, pos[other1] - offset1\r
126                 local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
127                 currentpos[other1], currentpos[other2] = first1, second1\r
128                 place_schematic(currentpos, schematic) --octant 1\r
129                 currentpos[other1] = first2\r
130                 place_schematic(currentpos, schematic) --octant 4\r
131                 currentpos[other2] = second2\r
132                 place_schematic(currentpos, schematic) --octant 5\r
133                 currentpos[other1] = first1\r
134                 place_schematic(currentpos, schematic) --octant 8\r
135                 local first1, first2 = pos[other1] + offset2, pos[other1] - offset2\r
136                 local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
137                 currentpos[other1], currentpos[other2] = first1, second1\r
138                 place_schematic(currentpos, schematic) --octant 2\r
139                 currentpos[other1] = first2\r
140                 place_schematic(currentpos, schematic) --octant 3\r
141                 currentpos[other2] = second2\r
142                 place_schematic(currentpos, schematic) --octant 6\r
143                 currentpos[other1] = first1\r
144                 place_schematic(currentpos, schematic) --octant 7\r
145 \r
146                 count = count + (length *8) --wip: broken because sometimes currentpos is repeated\r
147 \r
148                 --move to next location\r
149                 delta = delta + (offset1 * 2) + 1\r
150                 if delta >= 0 then\r
151                         offset2 = offset2 - 1\r
152                         delta = delta - (offset2 * 2)\r
153                 end\r
154                 offset1 = offset1 + 1\r
155         end\r
156         return count\r
157 end\r
158 \r
159 --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
160 worldedit.cylinder = function(pos, axis, length, radius, nodename, env)\r
161         local other1, other2\r
162         if axis == "x" then\r
163                 other1, other2 = "y", "z"\r
164         elseif axis == "y" then\r
165                 other1, other2 = "x", "z"\r
166         else --axis == "z"\r
167                 other1, other2 = "x", "y"\r
168         end\r
169 \r
170         --wip: make this faster using the schematic method by adding columns in a circle pattern like in hollow_cylinder, or by adding whole 2D slices using custom sized schematics\r
171         if env == nil then env = minetest.env end\r
172         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
173         local node = {name=nodename}\r
174         local count = 0\r
175         local step = 1\r
176         if length < 0 then\r
177                 length = -length\r
178                 step = -1\r
179         end\r
180         for i = 1, length do\r
181                 local offset1, offset2 = 0, radius\r
182                 local delta = -radius\r
183                 while offset1 <= offset2 do\r
184                         --connect each pair of octants\r
185                         currentpos[other1] = pos[other1] - offset1\r
186                         local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
187                         for i = 0, offset1 * 2 do\r
188                                 currentpos[other2] = second1\r
189                                 env:add_node(currentpos, node) --octant 1 to 4\r
190                                 currentpos[other2] = second2\r
191                                 env:add_node(currentpos, node) --octant 5 to 8\r
192                                 currentpos[other1] = currentpos[other1] + 1\r
193                         end\r
194                         currentpos[other1] = pos[other1] - offset2\r
195                         local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
196                         for i = 0, offset2 * 2 do\r
197                                 currentpos[other2] = second1\r
198                                 env:add_node(currentpos, node) --octant 2 to 3\r
199                                 currentpos[other2] = second2\r
200                                 env:add_node(currentpos, node) --octant 6 to 7\r
201                                 currentpos[other1] = currentpos[other1] + 1\r
202                         end\r
203 \r
204                         count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken\r
205 \r
206                         --move to next location\r
207                         delta = delta + (offset1 * 2) + 1\r
208                         offset1 = offset1 + 1\r
209                         if delta >= 0 then\r
210                                 offset2 = offset2 - 1\r
211                                 delta = delta - (offset2 * 2)\r
212                         end\r
213                 end\r
214                 currentpos[axis] = currentpos[axis] + step\r
215         end\r
216         return count\r
217 end\r
218 \r
219 --adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
220 worldedit.pyramid = function(pos, height, nodename, env)\r
221         local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height\r
222         local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height\r
223         local pos = {x=0, y=pos1y, z=0}\r
224 \r
225         --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
226         local count = 0\r
227         local node = {name=nodename}\r
228         if env == nil then env = minetest.env end\r
229         while pos.y <= pos2y do --each vertical level of the pyramid\r
230                 pos.x = pos1x\r
231                 while pos.x <= pos2x do\r
232                         pos.z = pos1z\r
233                         while pos.z <= pos2z do\r
234                                 env:add_node(pos, node)\r
235                                 pos.z = pos.z + 1\r
236                         end\r
237                         pos.x = pos.x + 1\r
238                 end\r
239                 count = count + ((pos2y - pos.y) * 2 + 1) ^ 2\r
240                 pos.y = pos.y + 1\r
241 \r
242                 pos1x, pos2x = pos1x + 1, pos2x - 1\r
243                 pos1z, pos2z = pos1z + 1, pos2z - 1\r
244 \r
245         end\r
246         return count\r
247 end\r
248 \r
249 --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
250 worldedit.spiral = function(pos, width, height, spacer, nodename, env) --wip: clean this up\r
251         -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua\r
252         --wip: rewrite this whole thing, nobody can understand it anyways\r
253         av, sn = math.abs, function(s) return s~=0 and s/av(s) or 0 end\r
254         local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards\r
255                 if z == -x and z >= x then return (2*z+1)^2 end\r
256                 local l = math.max(av(z), av(x))\r
257                 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
258         end\r
259         local function spiralt(side)\r
260                 local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)\r
261                 for i = 1, side do\r
262                         for j = 1, side do\r
263                                 local id = side^2 - sindex(stop - i + 1,start + j - 1)\r
264                                 ret[id] = {x=i,z=j}\r
265                         end\r
266                 end\r
267                 return ret\r
268         end\r
269     if env == nil then env = minetest.env end\r
270         -- connect the joined parts\r
271         local spiral = spiralt(width)\r
272         height = tonumber(height)\r
273         if height < 1 then height = 1 end\r
274         spacer = tonumber(spacer)-1\r
275         if spacer < 1 then spacer = 1 end\r
276         local count = 0\r
277         local node = {name=nodename}\r
278         local np,lp\r
279         for y=0,height do\r
280                 lp = nil\r
281                 for _,v in ipairs(spiral) do\r
282                         np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}\r
283                         if lp~=nil then\r
284                                 if lp.x~=np.x then \r
285                                         if lp.x<np.x then \r
286                                                 for i=lp.x+1,np.x do\r
287                                                         env:add_node({x=i, y=np.y, z=np.z}, node)\r
288                                                         count = count + 1\r
289                                                 end\r
290                                         else\r
291                                                 for i=np.x,lp.x-1 do\r
292                                                         env:add_node({x=i, y=np.y, z=np.z}, node)\r
293                                                         count = count + 1\r
294                                                 end\r
295                                         end\r
296                                 end\r
297                                 if lp.z~=np.z then \r
298                                         if lp.z<np.z then \r
299                                                 for i=lp.z+1,np.z do\r
300                                                         env:add_node({x=np.x, y=np.y, z=i}, node)\r
301                                                         count = count + 1\r
302                                                 end\r
303                                         else\r
304                                                 for i=np.z,lp.z-1 do\r
305                                                         env:add_node({x=np.x, y=np.y, z=i}, node)\r
306                                                         count = count + 1\r
307                                                 end\r
308                                         end\r
309                                 end\r
310                         end\r
311                         lp = np\r
312                 end\r
313         end\r
314         return count\r
315 end\r