OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
TerrainModifier.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 using System;
28 using System.Reflection;
29 using log4net;
30 
31 using OpenSim.Region.Framework.Interfaces;
32 
33 namespace OpenSim.Region.CoreModules.World.Terrain
34 {
35  public abstract class TerrainModifier : ITerrainModifier
36  {
38  protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
39 
40  protected TerrainModifier(ITerrainModule module)
41  {
42  m_module = module;
43  }
44 
45  public abstract string ModifyTerrain(ITerrainChannel map, string[] args);
46 
47  public abstract string GetUsage();
48 
49  public abstract double operate(double[,] map, TerrainModifierData data, int x, int y);
50 
51  protected String parseParameters(string[] args, out TerrainModifierData data)
52  {
53  string val;
54  string arg;
55  string result;
56  data = new TerrainModifierData();
57  data.shape = String.Empty;
58  data.bevel = String.Empty;
59  data.dx = 0;
60  data.dy = 0;
61  if (args.Length < 4)
62  {
63  result = "Usage: " + GetUsage();
64  }
65  else
66  {
67  result = this.parseFloat(args[3], out data.elevation);
68  }
69  if (result == String.Empty)
70  {
71  int index = 3;
72  while(++index < args.Length && result == String.Empty)
73  {
74  arg = args[index];
75  // check for shape
76  if (arg.StartsWith("-rec=") || arg.StartsWith("-ell="))
77  {
78  if (data.shape != String.Empty)
79  {
80  result = "Only 1 '-rec' or '-ell' parameter is permitted.";
81  }
82  else
83  {
84  data.shape = arg.StartsWith("-ell=") ? "ellipse" : "rectangle";
85  val = arg.Substring(arg.IndexOf("=") + 1);
86  string[] coords = val.Split(new char[] {','});
87  if ((coords.Length < 3) || (coords.Length > 4))
88  {
89  result = String.Format("Bad format for shape parameter {0}", arg);
90  }
91  else
92  {
93  result = this.parseInt(coords[0], out data.x0);
94  if (result == String.Empty)
95  {
96  result = this.parseInt(coords[1], out data.y0);
97  }
98  if (result == String.Empty)
99  {
100  result = this.parseInt(coords[2], out data.dx);
101  }
102  if (result == String.Empty)
103  {
104  if (coords.Length == 4)
105  {
106  result = this.parseInt(coords[3], out data.dy);
107  }
108  else
109  {
110  data.dy = data.dx;
111  }
112  }
113  if (result == String.Empty)
114  {
115  if ((data.dx <= 0) || (data.dy <= 0))
116  {
117  result = "Shape sizes must be positive integers";
118  }
119  }
120  else
121  {
122  result = String.Format("Bad value in shape parameters {0}", arg);
123  }
124  }
125  }
126  }
127  else if (arg.StartsWith("-taper="))
128  {
129  if (data.bevel != String.Empty)
130  {
131  result = "Only 1 '-taper' parameter is permitted.";
132  }
133  else
134  {
135  data.bevel = "taper";
136  val = arg.Substring(arg.IndexOf("=") + 1);
137  result = this.parseFloat(val, out data.bevelevation);
138  if (result != String.Empty)
139  {
140  result = String.Format("Bad format for taper parameter {0}", arg);
141  }
142  }
143  }
144  else
145  {
146  result = String.Format("Unrecognized parameter {0}", arg);
147  }
148  }
149  }
150  return result;
151  }
152 
153  protected string parseFloat(String s, out float f)
154  {
155  string result;
156  double d;
157  if (Double.TryParse(s, out d))
158  {
159  try
160  {
161  f = (float)d;
162  result = String.Empty;
163  }
164  catch(InvalidCastException)
165  {
166  result = String.Format("{0} is invalid", s);
167  f = -1.0f;
168  }
169  }
170  else
171  {
172  f = -1.0f;
173  result = String.Format("{0} is invalid", s);
174  }
175  return result;
176  }
177 
178  protected string parseInt(String s, out int i)
179  {
180  string result;
181  if (Int32.TryParse(s, out i))
182  {
183  result = String.Empty;
184  }
185  else
186  {
187  result = String.Format("{0} is invalid", s);
188  }
189  return result;
190  }
191 
193  {
194  bool[,] mask;
195  int xMax;
196  int yMax;
197  int xMid;
198  int yMid;
199  if (data.shape == "ellipse")
200  {
201  mask = this.ellipticalMask(data.dx, data.dy);
202  xMax = mask.GetLength(0);
203  yMax = mask.GetLength(1);
204  xMid = xMax / 2 + xMax % 2;
205  yMid = yMax / 2 + yMax % 2;
206  }
207  else
208  {
209  mask = this.rectangularMask(data.dx, data.dy);
210  xMax = mask.GetLength(0);
211  yMax = mask.GetLength(1);
212  xMid = 0;
213  yMid = 0;
214  }
215 // m_log.DebugFormat("Apply {0} mask {1}x{2} @ {3},{4}", data.shape, xMax, yMax, xMid, yMid);
216  double[,] buffer = map.GetDoubles();
217  int yDim = yMax;
218  while(--yDim >= 0)
219  {
220  int yPos = data.y0 + yDim - yMid;
221  if ((yPos >= 0) && (yPos < map.Height))
222  {
223  int xDim = xMax;
224  while(--xDim >= 0)
225  {
226  int xPos = data.x0 + xDim - xMid;
227  if ((xPos >= 0) && (xPos < map.Width) && (mask[xDim, yDim]))
228  {
229  double endElevation = this.operate(buffer, data, xPos, yPos);
230  map[xPos, yPos] = endElevation;
231  }
232  }
233  }
234  }
235  }
236 
237  protected double computeBevel(TerrainModifierData data, int x, int y)
238  {
239  int deltaX;
240  int deltaY;
241  int xMax;
242  int yMax;
243  double factor;
244  if (data.bevel == "taper")
245  {
246  if (data.shape == "ellipse")
247  {
248  deltaX = x - data.x0;
249  deltaY = y - data.y0;
250  xMax = data.dx;
251  yMax = data.dy;
252  factor = (double)((deltaX * deltaX) + (deltaY * deltaY));
253  factor /= ((xMax * xMax) + (yMax * yMax));
254  }
255  else
256  {
257  // pyramid
258  xMax = data.dx / 2 + data.dx % 2;
259  yMax = data.dy / 2 + data.dy % 2;
260  deltaX = Math.Abs(data.x0 + xMax - x);
261  deltaY = Math.Abs(data.y0 + yMax - y);
262  factor = Math.Max(((double)(deltaY) / yMax), ((double)(deltaX) / xMax));
263  }
264  }
265  else
266  {
267  factor = 0.0;
268  }
269  return factor;
270  }
271 
272  private bool[,] rectangularMask(int xSize, int ySize)
273  {
274  bool[,] mask = new bool[xSize, ySize];
275  int yPos = ySize;
276  while(--yPos >= 0)
277  {
278  int xPos = xSize;
279  while(--xPos >= 0)
280  {
281  mask[xPos, yPos] = true;
282  }
283  }
284  return mask;
285  }
286 
287  /*
288  * Fast ellipse-based derivative of Bresenham algorithm.
289  * https://web.archive.org/web/20120225095359/http://homepage.smc.edu/kennedy_john/belipse.pdf
290  */
291  private bool[,] ellipticalMask(int xRadius, int yRadius)
292  {
293  long twoASquared = 2L * xRadius * xRadius;
294  long twoBSquared = 2L * yRadius * yRadius;
295 
296  bool[,] mask = new bool[2 * xRadius + 1, 2 * yRadius + 1];
297 
298  long ellipseError = 0L;
299  long stoppingX = twoBSquared * xRadius;
300  long stoppingY = 0L;
301  long xChange = yRadius * yRadius * (1L - 2L * xRadius);
302  long yChange = xRadius * xRadius;
303 
304  int xPos = xRadius;
305  int yPos = 0;
306 
307  // first set of points
308  while(stoppingX >= stoppingY)
309  {
310  int yUpper = yRadius + yPos;
311  int yLower = yRadius - yPos;
312  // fill in the mask
313  int xNow = xPos;
314  while(xNow >= 0)
315  {
316  mask[xRadius + xNow, yUpper] = true;
317  mask[xRadius - xNow, yUpper] = true;
318  mask[xRadius + xNow, yLower] = true;
319  mask[xRadius - xNow, yLower] = true;
320  --xNow;
321  }
322  yPos++;
323  stoppingY += twoASquared;
324  ellipseError += yChange;
325  yChange += twoASquared;
326  if ((2L * ellipseError + xChange) > 0L)
327  {
328  xPos--;
329  stoppingX -= twoBSquared;
330  ellipseError += xChange;
331  xChange += twoBSquared;
332  }
333  }
334 
335  // second set of points
336  xPos = 0;
337  yPos = yRadius;
338  xChange = yRadius * yRadius;
339  yChange = xRadius * xRadius * (1L - 2L * yRadius);
340 
341  ellipseError = 0L;
342  stoppingX = 0L;
343  stoppingY = twoASquared * yRadius;
344 
345  while(stoppingX <= stoppingY)
346  {
347  int xUpper = xRadius + xPos;
348  int xLower = xRadius - xPos;
349  // fill in the mask
350  int yNow = yPos;
351  while(yNow >= 0)
352  {
353  mask[xUpper, yRadius + yNow] = true;
354  mask[xUpper, yRadius - yNow] = true;
355  mask[xLower, yRadius + yNow] = true;
356  mask[xLower, yRadius - yNow] = true;
357  --yNow;
358  }
359  xPos++;
360  stoppingX += twoBSquared;
361  ellipseError += xChange;
362  xChange += twoBSquared;
363  if ((2L * ellipseError + yChange) > 0L)
364  {
365  yPos--;
366  stoppingY -= twoASquared;
367  ellipseError += yChange;
368  yChange += twoASquared;
369  }
370  }
371  return mask;
372  }
373  }
374 }
375 
String parseParameters(string[] args, out TerrainModifierData data)
void applyModification(ITerrainChannel map, TerrainModifierData data)
double computeBevel(TerrainModifierData data, int x, int y)
delegate void ModifyTerrain(UUID user, float height, float seconds, byte size, byte action, float north, float west, float south, float east, UUID agentId)