]> git.lizzy.rs Git - worldedit.git/blob - worldedit/primitives.lua
Separate components into separate mods, add visualization API with hide(), suppress...
[worldedit.git] / worldedit / primitives.lua
1 worldedit = worldedit or {}\r
2 \r
3 --adds a hollow sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
4 worldedit.hollow_sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
5         local node = {name=nodename}\r
6         local pos1 = {x=0, y=0, z=0}\r
7         local full_radius = radius * radius + radius\r
8         local env = minetest.env\r
9         for x = -radius, radius do\r
10                 pos1.x = pos.x + x\r
11                 for y = -radius, radius do\r
12                         pos1.y = pos.y + y\r
13                         for z = -radius, radius do\r
14                                 if x*x+y*y+z*z >= (radius-1) * (radius-1) + (radius-1) and x*x+y*y+z*z <= full_radius then\r
15                                         pos1.z = pos.z + z\r
16                                         env:add_node({x=pos.x+x,y=pos.y+y,z=pos.z+z}, node)\r
17                                 end\r
18                         end\r
19                 end\r
20         end\r
21 end\r
22 \r
23 --adds a sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
24 worldedit.sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
25         local node = {name=nodename}\r
26         local pos1 = {x=0, y=0, z=0}\r
27         local full_radius = radius * radius + radius\r
28         local count = 0\r
29         local env = minetest.env\r
30         for x = -radius, radius do\r
31                 pos1.x = pos.x + x\r
32                 for y = -radius, radius do\r
33                         pos1.y = pos.y + y\r
34                         for z = -radius, radius do\r
35                                 if x*x+y*y+z*z <= full_radius then\r
36                                         pos1.z = pos.z + z\r
37                                         env:add_node(pos1, node)\r
38                                         count = count + 1\r
39                                 end\r
40                         end\r
41                 end\r
42         end\r
43         return count\r
44 end\r
45 \r
46 --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
47 worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)\r
48         local other1, other2\r
49         if axis == "x" then\r
50                 other1, other2 = "y", "z"\r
51         elseif axis == "y" then\r
52                 other1, other2 = "x", "z"\r
53         else --axis == "z"\r
54                 other1, other2 = "x", "y"\r
55         end\r
56 \r
57         local env = minetest.env\r
58         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
59         local node = {name=nodename}\r
60         local count = 0\r
61         local step = 1\r
62         if length < 0 then\r
63                 length = -length\r
64                 step = -1\r
65         end\r
66         for i = 1, length do\r
67                 local offset1, offset2 = 0, radius\r
68                 local delta = -radius\r
69                 while offset1 <= offset2 do\r
70                         --add node at each octant\r
71                         local first1, first2 = pos[other1] + offset1, pos[other1] - offset1\r
72                         local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
73                         currentpos[other1], currentpos[other2] = first1, second1\r
74                         env:add_node(currentpos, node) --octant 1\r
75                         currentpos[other1] = first2\r
76                         env:add_node(currentpos, node) --octant 4\r
77                         currentpos[other2] = second2\r
78                         env:add_node(currentpos, node) --octant 5\r
79                         currentpos[other1] = first1\r
80                         env:add_node(currentpos, node) --octant 8\r
81                         local first1, first2 = pos[other1] + offset2, pos[other1] - offset2\r
82                         local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
83                         currentpos[other1], currentpos[other2] = first1, second1\r
84                         env:add_node(currentpos, node) --octant 2\r
85                         currentpos[other1] = first2\r
86                         env:add_node(currentpos, node) --octant 3\r
87                         currentpos[other2] = second2\r
88                         env:add_node(currentpos, node) --octant 6\r
89                         currentpos[other1] = first1\r
90                         env:add_node(currentpos, node) --octant 7\r
91 \r
92                         count = count + 8 --wip: broken\r
93 \r
94                         --move to next location\r
95                         delta = delta + (offset1 * 2) + 1\r
96                         if delta >= 0 then\r
97                                 offset2 = offset2 - 1\r
98                                 delta = delta - (offset2 * 2)\r
99                         end\r
100                         offset1 = offset1 + 1\r
101                 end\r
102                 currentpos[axis] = currentpos[axis] + step\r
103         end\r
104         return count\r
105 end\r
106 \r
107 --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
108 worldedit.cylinder = function(pos, axis, length, radius, nodename)\r
109         local other1, other2\r
110         if axis == "x" then\r
111                 other1, other2 = "y", "z"\r
112         elseif axis == "y" then\r
113                 other1, other2 = "x", "z"\r
114         else --axis == "z"\r
115                 other1, other2 = "x", "y"\r
116         end\r
117 \r
118         local env = minetest.env\r
119         local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
120         local node = {name=nodename}\r
121         local count = 0\r
122         local step = 1\r
123         if length < 0 then\r
124                 length = -length\r
125                 step = -1\r
126         end\r
127         for i = 1, length do\r
128                 local offset1, offset2 = 0, radius\r
129                 local delta = -radius\r
130                 while offset1 <= offset2 do\r
131                         --connect each pair of octants\r
132                         currentpos[other1] = pos[other1] - offset1\r
133                         local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
134                         for i = 0, offset1 * 2 do\r
135                                 currentpos[other2] = second1\r
136                                 env:add_node(currentpos, node) --octant 1 to 4\r
137                                 currentpos[other2] = second2\r
138                                 env:add_node(currentpos, node) --octant 5 to 8\r
139                                 currentpos[other1] = currentpos[other1] + 1\r
140                         end\r
141                         currentpos[other1] = pos[other1] - offset2\r
142                         local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
143                         for i = 0, offset2 * 2 do\r
144                                 currentpos[other2] = second1\r
145                                 env:add_node(currentpos, node) --octant 2 to 3\r
146                                 currentpos[other2] = second2\r
147                                 env:add_node(currentpos, node) --octant 6 to 7\r
148                                 currentpos[other1] = currentpos[other1] + 1\r
149                         end\r
150 \r
151                         count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken\r
152 \r
153                         --move to next location\r
154                         delta = delta + (offset1 * 2) + 1\r
155                         offset1 = offset1 + 1\r
156                         if delta >= 0 then\r
157                                 offset2 = offset2 - 1\r
158                                 delta = delta - (offset2 * 2)\r
159                         end\r
160                 end\r
161                 currentpos[axis] = currentpos[axis] + step\r
162         end\r
163         return count\r
164 end\r
165 \r
166 --adds a pyramid at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
167 worldedit.pyramid = function(pos, height, nodename)\r
168         local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height\r
169         local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height\r
170         local pos = {x=0, y=pos1y, z=0}\r
171 \r
172         local count = 0\r
173         local node = {name=nodename}\r
174         local env = minetest.env\r
175         while pos.y <= pos2y do --each vertical level of the pyramid\r
176                 pos.x = pos1x\r
177                 while pos.x <= pos2x do\r
178                         pos.z = pos1z\r
179                         while pos.z <= pos2z do\r
180                                 env:add_node(pos, node)\r
181                                 pos.z = pos.z + 1\r
182                         end\r
183                         pos.x = pos.x + 1\r
184                 end\r
185                 count = count + ((pos2y - pos.y) * 2 + 1) ^ 2\r
186                 pos.y = pos.y + 1\r
187 \r
188                 pos1x, pos2x = pos1x + 1, pos2x - 1\r
189                 pos1z, pos2z = pos1z + 1, pos2z - 1\r
190 \r
191         end\r
192         return count\r
193 end\r
194 \r
195 --adds a spiral at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added\r
196 worldedit.spiral = function(pos, width, height, spacer, nodename) --wip: clean this up\r
197         -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua\r
198         av, sn = math.abs, function(s) return s~=0 and s/av(s) or 0 end\r
199         local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards\r
200                 if z == -x and z >= x then return (2*z+1)^2 end\r
201                 local l = math.max(av(z), av(x))\r
202                 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
203         end\r
204         local function spiralt(side)\r
205                 local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)\r
206                 for i = 1, side do\r
207                         for j = 1, side do\r
208                                 local id = side^2 - sindex(stop - i + 1,start + j - 1)\r
209                                 ret[id] = {x=i,z=j}\r
210                         end\r
211                 end\r
212                 return ret\r
213         end\r
214         -- connect the joined parts\r
215         local spiral = spiralt(width)\r
216         height = tonumber(height)\r
217         if height < 1 then height = 1 end\r
218         spacer = tonumber(spacer)-1\r
219         if spacer < 1 then spacer = 1 end\r
220         local count = 0\r
221         local node = {name=nodename}\r
222         local np,lp\r
223         for y=0,height do\r
224                 lp = nil\r
225                 for _,v in ipairs(spiral) do\r
226                         np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}\r
227                         if lp~=nil then\r
228                                 if lp.x~=np.x then \r
229                                         if lp.x<np.x then \r
230                                                 for i=lp.x+1,np.x do\r
231                                                         minetest.env:add_node({x=i, y=np.y, z=np.z}, node)\r
232                                                         count = count + 1\r
233                                                 end\r
234                                         else\r
235                                                 for i=np.x,lp.x-1 do\r
236                                                         minetest.env:add_node({x=i, y=np.y, z=np.z}, node)\r
237                                                         count = count + 1\r
238                                                 end\r
239                                         end\r
240                                 end\r
241                                 if lp.z~=np.z then \r
242                                         if lp.z<np.z then \r
243                                                 for i=lp.z+1,np.z do\r
244                                                         minetest.env:add_node({x=np.x, y=np.y, z=i}, node)\r
245                                                         count = count + 1\r
246                                                 end\r
247                                         else\r
248                                                 for i=np.z,lp.z-1 do\r
249                                                         minetest.env:add_node({x=np.x, y=np.y, z=i}, node)\r
250                                                         count = count + 1\r
251                                                 end\r
252                                         end\r
253                                 end\r
254                         end\r
255                         lp = np\r
256                 end\r
257         end\r
258         return count\r
259 end