1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
|
/**
Library for shapes
See docs/sdk/script/Shape.xml for documentation.
@author Sven2
*/
/* Base shape */
local BaseShape;
// Returns the area in squared pixels covered by the shape. Works for all specific shapes by using their functionality.
public func GetArea()
{
var check_rect = this->GetBoundingRectangle();
var area = 0;
for (var x = check_rect.x; x < check_rect.x + check_rect.wdt; x++)
for (var y = check_rect.y; y < check_rect.y + check_rect.hgt; y++)
if (this->IsPointContained(x, y))
area++;
return area;
}
/* Rectangle shape */
local BaseRectangle; // properties x,y,w,h
// Point contained in rectangle?
private func BaseRectangle_IsPointContained(int x, int y)
{
//return ((x-this.x+this.wdt)/this.wdt) * ((y-this.y+this.hgt)/this.hgt) == 1 && x>=this.x;
return x>=this.x && y>=this.y && x<this.x+this.wdt && y<this.y+this.hgt;
}
// bounding rectangle is just self
private func BaseRectangle_GetBoundingRectangle() { return this; }
private func BaseRectangle_Find_In(context)
{
if (!context) context = Global;
return context->Find_InRect(this.x, this.y, this.wdt, this.hgt);
}
private func BaseRectangle_Find_At(context)
{
if (!context) context = Global;
return context->Find_AtRect(this.x, this.y, this.wdt, this.hgt);
}
private func BaseRectangle_GetRandomPoint(proplist result)
{
if (this.wdt<=0 || this.hgt <=0) return false;
result.x = this.x + Random(this.wdt);
result.y = this.y + Random(this.hgt);
return true;
}
private func BaseRectangle_GetArea()
{
return this.wdt * this.hgt;
}
private func BaseRectangle_ToString()
{
return Format("Shape->Rectangle(%d, %d, %d, %d)", this.x, this.y, this.wdt, this.hgt);
}
private func BaseRectangle_IsFullMap()
{
return !this.x && !this.y && this.wdt == LandscapeWidth() && this.hgt == LandscapeHeight();
}
/** Constructor of rectangle area. (x,y) is included; (x+w,y+h) is excluded.
@par x Global left side of rectangle
@par y Global top side of rectangle
@par w Rectangle width
@par h Rectangle height
@return return a shape proplist representing a rectangle
*/
public func Rectangle(int x, int y, int w, int h)
{
return new BaseRectangle { x=x, y=y, wdt=w, hgt=h };
}
/* Circle shape */
local BaseCircle; // properties cx,cy,r
// point contained in circle?
private func BaseCircle_IsPointContained(int x, int y)
{
x-=this.cx; y-=this.cy;
var r=this.r;
return x*x + y*y <= r*r;
}
// bounding rectangle
private func BaseCircle_GetBoundingRectangle()
{
var r=this.r;
return new Shape.BaseRectangle { x=this.cx-r, y=this.cy-r, wdt=r*2+1, hgt=r*2+1 };
}
private func BaseCircle_GetRandomPoint(proplist result)
{
// Make sure radius circles are weighed equally
var r2 = this.r * this.r + 1;
if (r2>0x7fff) // for large numbers, the random function doesn't work
r2 = Random(0x8000) + Random(r2/0x8000+1) * 0x8000;
else
r2 = Random(r2);
var r = Sqrt(r2), a = Random(360);
result.x = this.cx + Sin(a, r);
result.y = this.cy + Cos(a, r);
return true;
}
public func BaseCircle_GetArea()
{
// Is aleady covered by general method, but this direct calculation for a simple circle is faster.
var area = 0;
for (var x = 0; x <= this.r; x++)
for (var y = 1; y <= this.r; y++)
if (x*x + y*y <= this.r*this.r)
area++;
return 4 * area + 1;
}
/** Constructor of circular area.
@par cx Global center x of circle
@par cy Global center y of circle
@par r Circle radius
@return return a shape proplist representing a filled circle around a point
*/
public func Circle(int cx, int cy, int r)
{
return new BaseCircle { cx=cx, cy=cy, r=r };
}
/* Intersection */
local BaseIntersection;
private func BaseIntersection_IsPointContained(int x, int y)
{
// Intersection: If any of the sub-areas exclude the point, then it's excluded
for (var area in this.areas)
if (!area->IsPointContained(x, y))
return false;
return true;
}
private func BaseIntersection_GetBoundingRectangle()
{
// Bounding rectangle of intersection
var result, rt;
for (var area in this.areas)
if (rt = area->GetBoundingRectangle())
{
// first bounds determine area
if (!result)
{
result = new Shape.BaseRectangle {x = rt.x, y = rt.y, wdt = rt.wdt, hgt = rt.hgt};
}
else
{
// following bounds reduce area
if (rt.x + rt.wdt < result.x + result.wdt) result.wdt = Max(rt.x + rt.wdt - result.x);
if (rt.y + rt.hgt < result.y + result.hgt) result.hgt = Max(rt.y + rt.hgt - result.y);
if (rt.x > result.x)
{
result.wdt = Max(result.wdt - rt.x + result.x);
result.x = rt.x;
}
if (rt.y > result.y)
{
result.hgt = Max(result.hgt - rt.y + result.y);
result.y = rt.y;
}
}
}
return result;
}
private func BaseIntersection_GetRandomPoint(proplist result, int num_tries)
{
// The precise shape of the intersection is unknown. So try 100 times to get a location from any of the subsections
if (!GetLength(this.areas)) return false;
if (!num_tries) num_tries = 200;
while (num_tries>0)
{
for (var area in this.areas)
{
// get point from subsection
if (!area->GetRandomPoint(result, num_tries)) return false; // sub-area empty? (note num-tries goes down)
var pt_ok = true;
// ensure it's contained in all other subsections
for (var area2 in this.areas) if (area != area2)
if (!area2->IsPointContained(result.x, result.y))
{
pt_ok = false;
break;
}
if (pt_ok) return true;
--num_tries;
}
}
return false;
}
/** Constructor of intersection area.
@par c1, c2, ... Up to ten parameters for intersected areas.
@return return a shape proplist representing a shape that contains only the points included in all the passed sub-shapes.
*/
public func Intersect(proplist c1, proplist c2, ...)
{
// Intersection of one area?
if (!c2) return c1;
// Otherwise, built array
var areas = [c1, c2], i=1, area;
while (area = Par(++i)) areas[i] = area;
return new BaseIntersection { areas = areas };
}
/* Combination */
local BaseCombination;
private func BaseCombination_IsPointContained(int x, int y)
{
// Combination: If any of the sub-areas include the point, then it's included
for (var area in this.areas)
if (area->IsPointContained(x, y))
return true;
return false;
}
private func BaseCombination_GetBoundingRectangle()
{
// Bounding rectangle of combination
var result, rt;
for (var area in this.areas)
if (rt = area->GetBoundingRectangle())
{
// first bounds determine area
if (!result)
{
result = new Shape.BaseRectangle {x = rt.x, y = rt.y, wdt = rt.wdt, hgt = rt.hgt};
}
else
{
// following bounds enlarge area
if (rt.x + rt.wdt > result.x + result.wdt) result.wdt = rt.x + rt.wdt - result.x;
if (rt.y + rt.hgt > result.y + result.hgt) result.hgt = rt.y + rt.hgt - result.y;
if (rt.x < result.x)
{
result.wdt += result.x - rt.x;
result.x = rt.x;
}
if (rt.y < result.y)
{
result.hgt += result.y - rt.y;
result.y = rt.y;
}
}
}
return result;
}
private func BaseCombination_GetRandomPoint(proplist result, int num_tries)
{
// Just get a random point from a random subsection
// Note that this doesn't weigh areas equally.
var len = GetLength(this.areas);
if (!len) return false;
return this.areas[Random(len)]->GetRandomPoint(result, num_tries);
}
/** Constructor of combined area.
@par c1, c2, ... Up to ten parameters for combined areas.
@return return a shape proplist representing a shape that contains all the points included in any of the passed sub-shapes.
*/
public func Combine(proplist c1, proplist c2, ...)
{
// Combination of one area?
if (!c2) return c1;
// Otherwise, built array
var areas = [c1, c2], i=1, area;
while (area = Par(++i)) areas[i] = area;
return new BaseCombination { areas = areas };
}
/* Subtraction */
local BaseSubtraction;
private func BaseSubtraction_IsPointContained(int x, int y)
{
// Subtraction contains everything in "in" area that is not contained in "ex" area
return this.in->IsPointContained(x, y) && !this.ex->IsPointContained(x, y);
}
private func BaseSubtraction_GetBoundingRectangle()
{
// Simply use "in" area since it bounds everything
return this.in->GetBoundingRectangle();
}
private func BaseSubtraction_GetRandomPoint(proplist result, int num_tries)
{
// Get random point in in-area until it lies outside ex-area
if (!num_tries) num_tries = 200;
while (num_tries>0)
{
if (!this.in->GetRandomPoint(result, num_tries)) return false; // sub-area empty? (note num-tries goes down)
if (!this.ex->IsPointContained(result.x, result.y)) return true;
--num_tries;
}
// Failed to find a point
return false;
}
/** Constructor of sutraction area.
@par in Shape that is included.
@par ex Shape that is excluded.
@return return a shape proplist representing a shape that includes the "in" shape and excludes the "ex" shape.
*/
public func Subtract(proplist in, proplist ex)
{
// Create a new subtraction area
return new BaseSubtraction { in = in, ex = ex };
}
/* Library initialization */
public func Definition(def)
{
// Initialize function pointers in shape classes
BaseShape =
{
GetArea = Shape.GetArea
};
BaseRectangle = new BaseShape
{
Type = "rect",
IsPointContained = Shape.BaseRectangle_IsPointContained,
GetBoundingRectangle = Shape.BaseRectangle_GetBoundingRectangle,
GetRandomPoint = Shape.BaseRectangle_GetRandomPoint,
GetArea = Shape.BaseRectangle_GetArea,
Find_In = Shape.BaseRectangle_Find_In,
Find_At = Shape.BaseRectangle_Find_At,
ToString = Shape.BaseRectangle_ToString,
IsFullMap = Shape.BaseRectangle_IsFullMap
};
BaseCircle = new BaseShape
{
IsPointContained = Shape.BaseCircle_IsPointContained,
GetBoundingRectangle = Shape.BaseCircle_GetBoundingRectangle,
GetRandomPoint = Shape.BaseCircle_GetRandomPoint,
GetArea = Shape.BaseCircle_GetArea
};
BaseIntersection = new BaseShape
{
IsPointContained = Shape.BaseIntersection_IsPointContained,
GetBoundingRectangle = Shape.BaseIntersection_GetBoundingRectangle,
GetRandomPoint = Shape.BaseIntersection_GetRandomPoint,
};
BaseCombination = new BaseShape
{
IsPointContained = Shape.BaseCombination_IsPointContained,
GetBoundingRectangle = Shape.BaseCombination_GetBoundingRectangle,
GetRandomPoint = Shape.BaseCombination_GetRandomPoint,
};
BaseSubtraction = new BaseShape
{
IsPointContained = Shape.BaseSubtraction_IsPointContained,
GetBoundingRectangle = Shape.BaseSubtraction_GetBoundingRectangle,
GetRandomPoint = Shape.BaseSubtraction_GetRandomPoint,
};
// shape class identity
BaseRectangle.shape = BaseRectangle;
BaseCircle.shape = BaseCircle;
BaseIntersection.shape = BaseIntersection;
BaseCombination.shape = BaseCombination;
BaseSubtraction.shape = BaseSubtraction;
return true;
}
/** Full landscape rectangle
@return Return a shape proplist representing the rectangle covering the whole current landscape.
*/
public func LandscapeRectangle()
{
return Rectangle(0, 0, LandscapeWidth(), LandscapeHeight());
}
/** Constructor of rectangle area. (x,y) is included; (x+w,y+h) is excluded. Automatically flips rectangles of negative size in any dimension.
@par x Global left side of rectangle
@par y Global top side of rectangle
@par w Rectangle width
@par h Rectangle height
@return return a shape proplist representing a rectangle
*/
global func Rectangle(int x2, int y2, int w2, int h2)
{
// normalize
if(w2 < 0)
{
x2 += w2;
w2 = -w2;
}
if(h2 < 0)
{
y2 += h2;
h2 = - h2;
}
return new Shape.BaseRectangle {x = x2, y = y2, wdt = w2, hgt = h2};
}
|