]> git.lizzy.rs Git - worldedit.git/blob - worldedit/manipulations.lua
57c7a764e8de015b2ed8093823d59000d066b147
[worldedit.git] / worldedit / manipulations.lua
1 --- Generic node manipulations.\r
2 -- @module worldedit.manipulations\r
3 \r
4 local mh = worldedit.manip_helpers\r
5 \r
6 \r
7 --- Sets a region to `node_names`.\r
8 -- @param pos1\r
9 -- @param pos2\r
10 -- @param node_names Node name or list of node names.\r
11 -- @return The number of nodes set.\r
12 function worldedit.set(pos1, pos2, node_names)\r
13         pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
14 \r
15         local manip, area = mh.init(pos1, pos2)\r
16         local data = mh.get_empty_data(area)\r
17 \r
18         if type(node_names) == "string" then -- Only one type of node\r
19                 local id = minetest.get_content_id(node_names)\r
20                 -- Fill area with node\r
21                 for i in area:iterp(pos1, pos2) do\r
22                         data[i] = id\r
23                 end\r
24         else -- Several types of nodes specified\r
25                 local node_ids = {}\r
26                 for i, v in ipairs(node_names) do\r
27                         node_ids[i] = minetest.get_content_id(v)\r
28                 end\r
29                 -- Fill area randomly with nodes\r
30                 local id_count, rand = #node_ids, math.random\r
31                 for i in area:iterp(pos1, pos2) do\r
32                         data[i] = node_ids[rand(id_count)]\r
33                 end\r
34         end\r
35 \r
36         mh.finish(manip, data)\r
37 \r
38         return worldedit.volume(pos1, pos2)\r
39 end\r
40 \r
41 \r
42 --- Replaces all instances of `search_node` with `replace_node` in a region.\r
43 -- When `inverse` is `true`, replaces all instances that are NOT `search_node`.\r
44 -- @return The number of nodes replaced.\r
45 function worldedit.replace(pos1, pos2, search_node, replace_node, inverse)\r
46         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
47 \r
48         local manip, area = mh.init(pos1, pos2)\r
49         local data = manip:get_data()\r
50 \r
51         local search_id = minetest.get_content_id(search_node)\r
52         local replace_id = minetest.get_content_id(replace_node)\r
53 \r
54         local count = 0\r
55 \r
56         --- TODO: This could be shortened by checking `inverse` in the loop,\r
57         -- but that would have a speed penalty.  Is the penalty big enough\r
58         -- to matter?\r
59         if not inverse then\r
60                 for i in area:iterp(pos1, pos2) do\r
61                         if data[i] == search_id then\r
62                                 data[i] = replace_id\r
63                                 count = count + 1\r
64                         end\r
65                 end\r
66         else\r
67                 for i in area:iterp(pos1, pos2) do\r
68                         if data[i] ~= search_id then\r
69                                 data[i] = replace_id\r
70                                 count = count + 1\r
71                         end\r
72                 end\r
73         end\r
74 \r
75         mh.finish(manip, data)\r
76 \r
77         return count\r
78 end\r
79 \r
80 \r
81 --- Duplicates a region `amount` times with offset vector `direction`.\r
82 -- Stacking is spread across server steps, one copy per step.\r
83 -- @return The number of nodes stacked.\r
84 function worldedit.stack2(pos1, pos2, direction, amount, finished)\r
85         local i = 0\r
86         local translated = {x=0, y=0, z=0}\r
87         local function next_one()\r
88                 if i < amount then\r
89                         i = i + 1\r
90                         translated.x = translated.x + direction.x\r
91                         translated.y = translated.y + direction.y\r
92                         translated.z = translated.z + direction.z\r
93                         worldedit.copy2(pos1, pos2, translated)\r
94                         minetest.after(0, next_one)\r
95                 else\r
96                         if finished then\r
97                                 finished()\r
98                         end\r
99                 end\r
100         end\r
101         next_one()\r
102         return worldedit.volume(pos1, pos2) * amount\r
103 end\r
104 \r
105 \r
106 --- Copies a region along `axis` by `amount` nodes.\r
107 -- @param pos1\r
108 -- @param pos2\r
109 -- @param axis Axis ("x", "y", or "z")\r
110 -- @param amount\r
111 -- @return The number of nodes copied.\r
112 function worldedit.copy(pos1, pos2, axis, amount)\r
113         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
114 \r
115         worldedit.keep_loaded(pos1, pos2)\r
116 \r
117         local get_node, get_meta, set_node = minetest.get_node,\r
118                         minetest.get_meta, minetest.set_node\r
119         -- Copy things backwards when negative to avoid corruption.\r
120         -- FIXME: Lots of code duplication here.\r
121         if amount < 0 then\r
122                 local pos = {}\r
123                 pos.x = pos1.x\r
124                 while pos.x <= pos2.x do\r
125                         pos.y = pos1.y\r
126                         while pos.y <= pos2.y do\r
127                                 pos.z = pos1.z\r
128                                 while pos.z <= pos2.z do\r
129                                         local node = get_node(pos) -- Obtain current node\r
130                                         local meta = get_meta(pos):to_table() -- Get meta of current node\r
131                                         local value = pos[axis] -- Store current position\r
132                                         pos[axis] = value + amount -- Move along axis\r
133                                         set_node(pos, node) -- Copy node to new position\r
134                                         get_meta(pos):from_table(meta) -- Set metadata of new node\r
135                                         pos[axis] = value -- Restore old position\r
136                                         pos.z = pos.z + 1\r
137                                 end\r
138                                 pos.y = pos.y + 1\r
139                         end\r
140                         pos.x = pos.x + 1\r
141                 end\r
142         else\r
143                 local pos = {}\r
144                 pos.x = pos2.x\r
145                 while pos.x >= pos1.x do\r
146                         pos.y = pos2.y\r
147                         while pos.y >= pos1.y do\r
148                                 pos.z = pos2.z\r
149                                 while pos.z >= pos1.z do\r
150                                         local node = get_node(pos) -- Obtain current node\r
151                                         local meta = get_meta(pos):to_table() -- Get meta of current node\r
152                                         local value = pos[axis] -- Store current position\r
153                                         pos[axis] = value + amount -- Move along axis\r
154                                         set_node(pos, node) -- Copy node to new position\r
155                                         get_meta(pos):from_table(meta) -- Set metadata of new node\r
156                                         pos[axis] = value -- Restore old position\r
157                                         pos.z = pos.z - 1\r
158                                 end\r
159                                 pos.y = pos.y - 1\r
160                         end\r
161                         pos.x = pos.x - 1\r
162                 end\r
163         end\r
164         return worldedit.volume(pos1, pos2)\r
165 end\r
166 \r
167 --- Copies a region by offset vector `off`.\r
168 -- @param pos1\r
169 -- @param pos2\r
170 -- @param off\r
171 -- @return The number of nodes copied.\r
172 function worldedit.copy2(pos1, pos2, off)\r
173         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
174 \r
175         worldedit.keep_loaded(pos1, pos2)\r
176 \r
177         local get_node, get_meta, set_node = minetest.get_node,\r
178                         minetest.get_meta, minetest.set_node\r
179         local pos = {}\r
180         pos.x = pos2.x\r
181         while pos.x >= pos1.x do\r
182                 pos.y = pos2.y\r
183                 while pos.y >= pos1.y do\r
184                         pos.z = pos2.z\r
185                         while pos.z >= pos1.z do\r
186                                 local node = get_node(pos) -- Obtain current node\r
187                                 local meta = get_meta(pos):to_table() -- Get meta of current node\r
188                                 local newpos = vector.add(pos, off) -- Calculate new position\r
189                                 set_node(newpos, node) -- Copy node to new position\r
190                                 get_meta(newpos):from_table(meta) -- Set metadata of new node\r
191                                 pos.z = pos.z - 1\r
192                         end\r
193                         pos.y = pos.y - 1\r
194                 end\r
195                 pos.x = pos.x - 1\r
196         end\r
197         return worldedit.volume(pos1, pos2)\r
198 end\r
199 \r
200 --- Moves a region along `axis` by `amount` nodes.\r
201 -- @return The number of nodes moved.\r
202 function worldedit.move(pos1, pos2, axis, amount)\r
203         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
204 \r
205         worldedit.keep_loaded(pos1, pos2)\r
206 \r
207         --- TODO: Move slice by slice using schematic method in the move axis\r
208         -- and transfer metadata in separate loop (and if the amount is\r
209         -- greater than the length in the axis, copy whole thing at a time and\r
210         -- erase original after, using schematic method).\r
211         local get_node, get_meta, set_node, remove_node = minetest.get_node,\r
212                         minetest.get_meta, minetest.set_node, minetest.remove_node\r
213         -- Copy things backwards when negative to avoid corruption.\r
214         --- FIXME: Lots of code duplication here.\r
215         if amount < 0 then\r
216                 local pos = {}\r
217                 pos.x = pos1.x\r
218                 while pos.x <= pos2.x do\r
219                         pos.y = pos1.y\r
220                         while pos.y <= pos2.y do\r
221                                 pos.z = pos1.z\r
222                                 while pos.z <= pos2.z do\r
223                                         local node = get_node(pos) -- Obtain current node\r
224                                         local meta = get_meta(pos):to_table() -- Get metadata of current node\r
225                                         remove_node(pos) -- Remove current node\r
226                                         local value = pos[axis] -- Store current position\r
227                                         pos[axis] = value + amount -- Move along axis\r
228                                         set_node(pos, node) -- Move node to new position\r
229                                         get_meta(pos):from_table(meta) -- Set metadata of new node\r
230                                         pos[axis] = value -- Restore old position\r
231                                         pos.z = pos.z + 1\r
232                                 end\r
233                                 pos.y = pos.y + 1\r
234                         end\r
235                         pos.x = pos.x + 1\r
236                 end\r
237         else\r
238                 local pos = {}\r
239                 pos.x = pos2.x\r
240                 while pos.x >= pos1.x do\r
241                         pos.y = pos2.y\r
242                         while pos.y >= pos1.y do\r
243                                 pos.z = pos2.z\r
244                                 while pos.z >= pos1.z do\r
245                                         local node = get_node(pos) -- Obtain current node\r
246                                         local meta = get_meta(pos):to_table() -- Get metadata of current node\r
247                                         remove_node(pos) -- Remove current node\r
248                                         local value = pos[axis] -- Store current position\r
249                                         pos[axis] = value + amount -- Move along axis\r
250                                         set_node(pos, node) -- Move node to new position\r
251                                         get_meta(pos):from_table(meta) -- Set metadata of new node\r
252                                         pos[axis] = value -- Restore old position\r
253                                         pos.z = pos.z - 1\r
254                                 end\r
255                                 pos.y = pos.y - 1\r
256                         end\r
257                         pos.x = pos.x - 1\r
258                 end\r
259         end\r
260         return worldedit.volume(pos1, pos2)\r
261 end\r
262 \r
263 \r
264 --- Duplicates a region along `axis` `amount` times.\r
265 -- Stacking is spread across server steps, one copy per step.\r
266 -- @param pos1\r
267 -- @param pos2\r
268 -- @param axis Axis direction, "x", "y", or "z".\r
269 -- @param count\r
270 -- @return The number of nodes stacked.\r
271 function worldedit.stack(pos1, pos2, axis, count)\r
272         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
273         local length = pos2[axis] - pos1[axis] + 1\r
274         if count < 0 then\r
275                 count = -count\r
276                 length = -length\r
277         end\r
278         local amount = 0\r
279         local copy = worldedit.copy\r
280         local i = 1\r
281         function next_one()\r
282                 if i <= count then\r
283                         i = i + 1\r
284                         amount = amount + length\r
285                         copy(pos1, pos2, axis, amount)\r
286                         minetest.after(0, next_one)\r
287                 end\r
288         end\r
289         next_one()\r
290         return worldedit.volume(pos1, pos2) * count\r
291 end\r
292 \r
293 \r
294 --- Stretches a region by a factor of positive integers along the X, Y, and Z\r
295 -- axes, respectively, with `pos1` as the origin.\r
296 -- @param pos1\r
297 -- @param pos2\r
298 -- @param stretch_x Amount to stretch along X axis.\r
299 -- @param stretch_y Amount to stretch along Y axis.\r
300 -- @param stretch_z Amount to stretch along Z axis.\r
301 -- @return The number of nodes scaled.\r
302 -- @return The new scaled position 1.\r
303 -- @return The new scaled position 2.\r
304 function worldedit.stretch(pos1, pos2, stretch_x, stretch_y, stretch_z)\r
305         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
306 \r
307         -- Prepare schematic of large node\r
308         local get_node, get_meta, place_schematic = minetest.get_node,\r
309                         minetest.get_meta, minetest.place_schematic\r
310         local placeholder_node = {name="", param1=255, param2=0}\r
311         local nodes = {}\r
312         for i = 1, stretch_x * stretch_y * stretch_z do\r
313                 nodes[i] = placeholder_node\r
314         end\r
315         local schematic = {size={x=stretch_x, y=stretch_y, z=stretch_z}, data=nodes}\r
316 \r
317         local size_x, size_y, size_z = stretch_x - 1, stretch_y - 1, stretch_z - 1\r
318 \r
319         local new_pos2 = {\r
320                 x = pos1.x + (pos2.x - pos1.x) * stretch_x + size_x,\r
321                 y = pos1.y + (pos2.y - pos1.y) * stretch_y + size_y,\r
322                 z = pos1.z + (pos2.z - pos1.z) * stretch_z + size_z,\r
323         }\r
324         worldedit.keep_loaded(pos1, new_pos2)\r
325 \r
326         local pos = {x=pos2.x, y=0, z=0}\r
327         local big_pos = {x=0, y=0, z=0}\r
328         while pos.x >= pos1.x do\r
329                 pos.y = pos2.y\r
330                 while pos.y >= pos1.y do\r
331                         pos.z = pos2.z\r
332                         while pos.z >= pos1.z do\r
333                                 local node = get_node(pos) -- Get current node\r
334                                 local meta = get_meta(pos):to_table() -- Get meta of current node\r
335 \r
336                                 -- Calculate far corner of the big node\r
337                                 local pos_x = pos1.x + (pos.x - pos1.x) * stretch_x\r
338                                 local pos_y = pos1.y + (pos.y - pos1.y) * stretch_y\r
339                                 local pos_z = pos1.z + (pos.z - pos1.z) * stretch_z\r
340 \r
341                                 -- Create large node\r
342                                 placeholder_node.name = node.name\r
343                                 placeholder_node.param2 = node.param2\r
344                                 big_pos.x, big_pos.y, big_pos.z = pos_x, pos_y, pos_z\r
345                                 place_schematic(big_pos, schematic)\r
346 \r
347                                 -- Fill in large node meta\r
348                                 if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then\r
349                                         -- Node has meta fields\r
350                                         for x = 0, size_x do\r
351                                         for y = 0, size_y do\r
352                                         for z = 0, size_z do\r
353                                                 big_pos.x = pos_x + x\r
354                                                 big_pos.y = pos_y + y\r
355                                                 big_pos.z = pos_z + z\r
356                                                 -- Set metadata of new node\r
357                                                 get_meta(big_pos):from_table(meta)\r
358                                         end\r
359                                         end\r
360                                         end\r
361                                 end\r
362                                 pos.z = pos.z - 1\r
363                         end\r
364                         pos.y = pos.y - 1\r
365                 end\r
366                 pos.x = pos.x - 1\r
367         end\r
368         return worldedit.volume(pos1, pos2) * stretch_x * stretch_y * stretch_z, pos1, new_pos2\r
369 end\r
370 \r
371 \r
372 --- Transposes a region between two axes.\r
373 -- @return The number of nodes transposed.\r
374 -- @return The new transposed position 1.\r
375 -- @return The new transposed position 2.\r
376 function worldedit.transpose(pos1, pos2, axis1, axis2)\r
377         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
378 \r
379         local compare\r
380         local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]\r
381 \r
382         if extent1 > extent2 then\r
383                 compare = function(extent1, extent2)\r
384                         return extent1 > extent2\r
385                 end\r
386         else\r
387                 compare = function(extent1, extent2)\r
388                         return extent1 < extent2\r
389                 end\r
390         end\r
391 \r
392         -- Calculate the new position 2 after transposition\r
393         local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
394         new_pos2[axis1] = pos1[axis1] + extent2\r
395         new_pos2[axis2] = pos1[axis2] + extent1\r
396 \r
397         local upper_bound = {x=pos2.x, y=pos2.y, z=pos2.z}\r
398         if upper_bound[axis1] < new_pos2[axis1] then upper_bound[axis1] = new_pos2[axis1] end\r
399         if upper_bound[axis2] < new_pos2[axis2] then upper_bound[axis2] = new_pos2[axis2] end\r
400         worldedit.keep_loaded(pos1, upper_bound)\r
401 \r
402         local pos = {x=pos1.x, y=0, z=0}\r
403         local get_node, get_meta, set_node = minetest.get_node,\r
404                         minetest.get_meta, minetest.set_node\r
405         while pos.x <= pos2.x do\r
406                 pos.y = pos1.y\r
407                 while pos.y <= pos2.y do\r
408                         pos.z = pos1.z\r
409                         while pos.z <= pos2.z do\r
410                                 local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]\r
411                                 if compare(extent1, extent2) then -- Transpose only if below the diagonal\r
412                                         local node1 = get_node(pos)\r
413                                         local meta1 = get_meta(pos):to_table()\r
414                                         local value1, value2 = pos[axis1], pos[axis2] -- Save position values\r
415                                         pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 -- Swap axis extents\r
416                                         local node2 = get_node(pos)\r
417                                         local meta2 = get_meta(pos):to_table()\r
418                                         set_node(pos, node1)\r
419                                         get_meta(pos):from_table(meta1)\r
420                                         pos[axis1], pos[axis2] = value1, value2 -- Restore position values\r
421                                         set_node(pos, node2)\r
422                                         get_meta(pos):from_table(meta2)\r
423                                 end\r
424                                 pos.z = pos.z + 1\r
425                         end\r
426                         pos.y = pos.y + 1\r
427                 end\r
428                 pos.x = pos.x + 1\r
429         end\r
430         return worldedit.volume(pos1, pos2), pos1, new_pos2\r
431 end\r
432 \r
433 \r
434 --- Flips a region along `axis`.\r
435 -- @return The number of nodes flipped.\r
436 function worldedit.flip(pos1, pos2, axis)\r
437         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
438 \r
439         worldedit.keep_loaded(pos1, pos2)\r
440 \r
441         --- TODO: Flip the region slice by slice along the flip axis using schematic method.\r
442         local pos = {x=pos1.x, y=0, z=0}\r
443         local start = pos1[axis] + pos2[axis]\r
444         pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)\r
445         local get_node, get_meta, set_node = minetest.get_node,\r
446                         minetest.get_meta, minetest.set_node\r
447         while pos.x <= pos2.x do\r
448                 pos.y = pos1.y\r
449                 while pos.y <= pos2.y do\r
450                         pos.z = pos1.z\r
451                         while pos.z <= pos2.z do\r
452                                 local node1 = get_node(pos)\r
453                                 local meta1 = get_meta(pos):to_table()\r
454                                 local value = pos[axis] -- Save position\r
455                                 pos[axis] = start - value -- Shift position\r
456                                 local node2 = get_node(pos)\r
457                                 local meta2 = get_meta(pos):to_table()\r
458                                 set_node(pos, node1)\r
459                                 get_meta(pos):from_table(meta1)\r
460                                 pos[axis] = value -- Restore position\r
461                                 set_node(pos, node2)\r
462                                 get_meta(pos):from_table(meta2)\r
463                                 pos.z = pos.z + 1\r
464                         end\r
465                         pos.y = pos.y + 1\r
466                 end\r
467                 pos.x = pos.x + 1\r
468         end\r
469         return worldedit.volume(pos1, pos2)\r
470 end\r
471 \r
472 \r
473 --- Rotates a region clockwise around an axis.\r
474 -- @param pos1\r
475 -- @param pos2\r
476 -- @param axis Axis ("x", "y", or "z").\r
477 -- @param angle Angle in degrees (90 degree increments only).\r
478 -- @return The number of nodes rotated.\r
479 -- @return The new first position.\r
480 -- @return The new second position.\r
481 function worldedit.rotate(pos1, pos2, axis, angle)\r
482         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
483 \r
484         local other1, other2 = worldedit.get_axis_others(axis)\r
485         angle = angle % 360\r
486 \r
487         local count\r
488         if angle == 90 then\r
489                 worldedit.flip(pos1, pos2, other1)\r
490                 count, pos1, pos2 = worldedit.transpose(pos1, pos2, other1, other2)\r
491         elseif angle == 180 then\r
492                 worldedit.flip(pos1, pos2, other1)\r
493                 count = worldedit.flip(pos1, pos2, other2)\r
494         elseif angle == 270 then\r
495                 worldedit.flip(pos1, pos2, other2)\r
496                 count, pos1, pos2 = worldedit.transpose(pos1, pos2, other1, other2)\r
497         else\r
498                 error("Only 90 degree increments are supported!")\r
499         end\r
500         return count, pos1, pos2\r
501 end\r
502 \r
503 \r
504 --- Rotates all oriented nodes in a region clockwise around the Y axis.\r
505 -- @param pos1\r
506 -- @param pos2\r
507 -- @param angle Angle in degrees (90 degree increments only).\r
508 -- @return The number of nodes oriented.\r
509 -- TODO: Support 6D facedir rotation along arbitrary axis.\r
510 function worldedit.orient(pos1, pos2, angle)\r
511         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
512         local registered_nodes = minetest.registered_nodes\r
513 \r
514         local wallmounted = {\r
515                 [90]  = {[0]=0, 1, 5, 4, 2, 3},\r
516                 [180] = {[0]=0, 1, 3, 2, 5, 4},\r
517                 [270] = {[0]=0, 1, 4, 5, 3, 2}\r
518         }\r
519         local facedir = {\r
520                 [90]  = {[0]=1, 2, 3, 0},\r
521                 [180] = {[0]=2, 3, 0, 1},\r
522                 [270] = {[0]=3, 0, 1, 2}\r
523         }\r
524 \r
525         angle = angle % 360\r
526         if angle == 0 then\r
527                 return 0\r
528         end\r
529         if angle % 90 ~= 0 then\r
530                 error("Only 90 degree increments are supported!")\r
531         end\r
532         local wallmounted_substitution = wallmounted[angle]\r
533         local facedir_substitution = facedir[angle]\r
534 \r
535         worldedit.keep_loaded(pos1, pos2)\r
536 \r
537         local count = 0\r
538         local set_node, get_node, get_meta, swap_node = minetest.set_node,\r
539                         minetest.get_node, minetest.get_meta, minetest.swap_node\r
540         local pos = {x=pos1.x, y=0, z=0}\r
541         while pos.x <= pos2.x do\r
542                 pos.y = pos1.y\r
543                 while pos.y <= pos2.y do\r
544                         pos.z = pos1.z\r
545                         while pos.z <= pos2.z do\r
546                                 local node = get_node(pos)\r
547                                 local def = registered_nodes[node.name]\r
548                                 if def then\r
549                                         if def.paramtype2 == "wallmounted" then\r
550                                                 node.param2 = wallmounted_substitution[node.param2]\r
551                                                 local meta = get_meta(pos):to_table()\r
552                                                 set_node(pos, node)\r
553                                                 get_meta(pos):from_table(meta)\r
554                                                 count = count + 1\r
555                                         elseif def.paramtype2 == "facedir" then\r
556                                                 node.param2 = facedir_substitution[node.param2]\r
557                                                 local meta = get_meta(pos):to_table()\r
558                                                 set_node(pos, node)\r
559                                                 get_meta(pos):from_table(meta)\r
560                                                 count = count + 1\r
561                                         end\r
562                                 end\r
563                                 pos.z = pos.z + 1\r
564                         end\r
565                         pos.y = pos.y + 1\r
566                 end\r
567                 pos.x = pos.x + 1\r
568         end\r
569         return count\r
570 end\r
571 \r
572 \r
573 --- Attempts to fix the lighting in a region.\r
574 -- @return The number of nodes updated.\r
575 function worldedit.fixlight(pos1, pos2)\r
576         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
577 \r
578         local vmanip = minetest.get_voxel_manip(pos1, pos2)\r
579         vmanip:write_to_map()\r
580         vmanip:update_map() -- this updates the lighting\r
581 \r
582         return worldedit.volume(pos1, pos2)\r
583 end\r
584 \r
585 \r
586 --- Clears all objects in a region.\r
587 -- @return The number of objects cleared.\r
588 function worldedit.clear_objects(pos1, pos2)\r
589         pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
590 \r
591         worldedit.keep_loaded(pos1, pos2)\r
592 \r
593         -- Offset positions to include full nodes (positions are in the center of nodes)\r
594         local pos1x, pos1y, pos1z = pos1.x - 0.5, pos1.y - 0.5, pos1.z - 0.5\r
595         local pos2x, pos2y, pos2z = pos2.x + 0.5, pos2.y + 0.5, pos2.z + 0.5\r
596 \r
597         -- Center of region\r
598         local center = {\r
599                 x = pos1x + ((pos2x - pos1x) / 2),\r
600                 y = pos1y + ((pos2y - pos1y) / 2),\r
601                 z = pos1z + ((pos2z - pos1z) / 2)\r
602         }\r
603         -- Bounding sphere radius\r
604         local radius = math.sqrt(\r
605                         (center.x - pos1x) ^ 2 +\r
606                         (center.y - pos1y) ^ 2 +\r
607                         (center.z - pos1z) ^ 2)\r
608         local count = 0\r
609         for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do\r
610                 local entity = obj:get_luaentity()\r
611                 -- Avoid players and WorldEdit entities\r
612                 if not obj:is_player() and (not entity or\r
613                                 not entity.name:find("^worldedit:")) then\r
614                         local pos = obj:getpos()\r
615                         if pos.x >= pos1x and pos.x <= pos2x and\r
616                                         pos.y >= pos1y and pos.y <= pos2y and\r
617                                         pos.z >= pos1z and pos.z <= pos2z then\r
618                                 -- Inside region\r
619                                 obj:remove()\r
620                                 count = count + 1\r
621                         end\r
622                 end\r
623         end\r
624         return count\r
625 end\r
626 \r