001package squidpony.squidmath; 002 003import squidpony.ArrayTools; 004import squidpony.LZSEncoding; 005import squidpony.StringKit; 006import squidpony.squidgrid.zone.MutableZone; 007import squidpony.squidgrid.zone.Zone; 008 009import java.io.Serializable; 010import java.util.*; 011 012import static squidpony.squidmath.CrossHash.Water.*; 013 014/** 015 * Region encoding of on/off information about areas using bitsets; uncompressed (fatty), but fast (greased lightning). 016 * This can handle any size of 2D data, and is not strictly limited to 256x256 as CoordPacker is. It stores several long 017 * arrays and uses each bit in one of those numbers to represent a single point, though sometimes this does waste bits 018 * if the height of the area this encodes is not a multiple of 64 (if you store a 80x64 map, this uses 80 longs; if you 019 * store an 80x65 map, this uses 160 longs, 80 for the first 64 rows and 80 more to store the next row). It's much 020 * faster than CoordPacker at certain operations (anything that expands or retracts an area, including 021 * {@link #expand()}), {@link #retract()}), {@link #fringe()}), {@link #surface()}, and {@link #flood(GreasedRegion)}, 022 * and slightly faster on others, like {@link #and(GreasedRegion)} (called intersectPacked() in CoordPacker) and 023 * {@link #or(GreasedRegion)} (called unionPacked() in CoordPacker). 024 * <br> 025 * Each GreasedRegion is mutable, and instance methods typically modify that instance and return it for chaining. There 026 * are exceptions, usually where multiple GreasedRegion values are returned and the instance is not modified. 027 * <br> 028 * Typical usage involves constructing a GreasedRegion from some input data, like a char[][] for a map or a double[][] 029 * from DijkstraMap, and modifying it spatially with expand(), retract(), flood(), etc. It's common to mix in data from 030 * other GreasedRegions with and() (which gets the intersection of two GreasedRegions and stores it in one), or() (which 031 * is like and() but for the union), xor() (like and() but for exclusive or, finding only cells that are on in exactly 032 * one of the two GreasedRegions), and andNot() (which can be considered the "subtract another region from me" method). 033 * There are 8-way (Chebyshev distance) variants on all of the spatial methods, and methods without "8way" in the name 034 * are either 4-way (Manhattan distance) or not affected by distance measurement. Once you have a GreasedRegion, you may 035 * want to: 036 * <ul> 037 * <li>get a single random point from it (use {@link #singleRandom(IRNG)}),</li> 038 * <li>get several random points from it with random sampling (use {@link #randomPortion(IRNG, int)}),</li> 039 * <li>mutate the current GreasedRegion to keep random points from it with random sampling (use {@link #randomRegion(IRNG, int)}),</li> 040 * <li>get random points that are likely to be separated (use {@link #mixedRandomSeparated(double, int, long)} with 041 * a random long for the last parameter, or {@link #quasiRandomSeparated(double)} if you don't want a random seed),</li> 042 * <li>do what any of the above "separated" methods can do, but mutate the current GreasedRegion (use 043 * {@link #mixedRandomRegion(double, int, long)} or {@link #quasiRandomRegion(double, int)}),</li> 044 * <li>get all points from it (use {@link #asCoords()} to get a Coord array, or produce a 2D array of the contents 045 * with {@link #decode()} or {@link #toChars(char, char)}),</li> 046 * <li>use it to modify other 2D data, such as with {@link #mask(char[][], char)}, 047 * {@link #inverseMask(char[][], char)}, {@link #writeDoubles(double[][], double)}, or 048 * {@link #writeIntsInto(int[][], int)}, along with variations on those.</li> 049 * </ul> 050 * <br> 051 * You may also want to produce some 2D data from one or more GreasedRegions, as with {@link #sum(GreasedRegion...)} or 052 * {@link #toChars()}. The most effective techniques regarding GreasedRegion involve multiple methods, like getting a 053 * few random points from an existing GreasedRegion representing floor tiles in a dungeon with 054 * {@link #randomRegion(IRNG, int)}, then finding a random expansion of those initial points with 055 * {@link #spill(GreasedRegion, int, IRNG)}, giving the original GreasedRegion of floor tiles as the first argument. 056 * This could be used to position puddles of water or toxic waste in a dungeon level, while still keeping the starting 057 * points and finished points within the boundaries of valid (floor) cells. If you wanted to place something like mold 058 * that can be on floors or on cells immediately adjacent to floors (like walls), you could call {@link #expand()} on 059 * the floor tiles before calling spill, allowing the spill to spread onto non-floor cells that are next to floors. 060 * <br> 061 * For efficiency, you can place one GreasedRegion into another (typically a temporary value that is no longer needed 062 * and can be recycled) using {@link #remake(GreasedRegion)}, or give the information that would normally be used to 063 * construct a fresh GreasedRegion to an existing one of the same dimensions with {@link #refill(boolean[][])} or any 064 * of the overloads of refill(). These re-methods don't do as much work as a constructor does if the width and height 065 * of their argument are identical to their current width and height, and don't create more garbage for the GC. 066 * <br> 067 * Created by Tommy Ettinger on 6/24/2016. 068 */ 069public class GreasedRegion extends Zone.Skeleton implements Collection<Coord>, Serializable, MutableZone { 070 private static final long serialVersionUID = 0; 071 private static final SobolQRNG sobol = new SobolQRNG(2); 072 073 public long[] data; 074 public int height; 075 public int width; 076 private int ySections; 077 private long yEndMask; 078 private boolean tallied; 079 private int ct; 080 private int[] counts; 081 082 private void tally() 083 { 084 ct = 0; 085 for (int i = 0, tmp; i < counts.length; i++) { 086 tmp = Long.bitCount(data[i]); 087 counts[i] = tmp == 0 ? 0 : (ct += tmp); 088 } 089 tallied = true; 090 } 091 092 /** 093 * Constructs an empty 64x64 GreasedRegion. 094 * GreasedRegions are mutable, so you can add to this with insert() or insertSeveral(), among others. 095 */ 096 public GreasedRegion() 097 { 098 width = 64; 099 height = 64; 100 ySections = 1; 101 yEndMask = -1L; 102 data = new long[64]; 103 counts = new int[64]; 104 ct = 0; 105 tallied = true; 106 } 107 108 /** 109 * Constructs a GreasedRegion with the given rectangular boolean array, with width of bits.length and height of 110 * bits[0].length, any value of true considered "on", and any value of false considered "off." 111 * @param bits a rectangular 2D boolean array where true is on and false is off 112 */ 113 public GreasedRegion(final boolean[][] bits) 114 { 115 width = bits.length; 116 height = bits[0].length; 117 ySections = (height + 63) >> 6; 118 yEndMask = -1L >>> (64 - (height & 63)); 119 data = new long[width * ySections]; 120 for (int x = 0, xs = 0; x < width; x++, xs += ySections) { 121 for (int y = 0; y < height; y++) { 122 if(bits[x][y]) data[xs + (y >> 6)] |= 1L << (y & 63); 123 } 124 } 125 counts = new int[width * ySections]; 126 tallied = false; 127 } 128 /** 129 * Reassigns this GreasedRegion with the given rectangular boolean array, reusing the current data storage (without 130 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 131 * this are always cleared, then any value of true in map is considered "on", and any value of false in map is 132 * considered "off." 133 * @param map a rectangular 2D boolean array where true is on and false is off 134 * @return this for chaining 135 */ 136 public GreasedRegion refill(final boolean[][] map) { 137 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 138 Arrays.fill(data, 0L); 139 for (int x = 0; x < width; x++) { 140 for (int y = 0; y < height; y++) { 141 data[x * ySections + (y >> 6)] |= (map[x][y] ? 1L : 0L) << (y & 63); 142 } 143 } 144 tallied = false; 145 return this; 146 } else { 147 width = (map == null) ? 0 : map.length; 148 height = (map == null || map.length <= 0) ? 0 : map[0].length; 149 ySections = (height + 63) >> 6; 150 yEndMask = -1L >>> (64 - (height & 63)); 151 data = new long[width * ySections]; 152 for (int x = 0; x < width; x++) { 153 for (int y = 0; y < height; y++) { 154 if(map[x][y]) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 155 } 156 } 157 counts = new int[width * ySections]; 158 tallied = false; 159 return this; 160 } 161 } 162 163 /** 164 * Constructs a GreasedRegion with the given rectangular char array, with width of map.length and height of 165 * map[0].length, any value that equals yes is considered "on", and any other value considered "off." 166 * @param map a rectangular 2D char array where yes is on and everything else is off 167 * @param yes which char to encode as "on" 168 */ 169 public GreasedRegion(final char[][] map, final char yes) 170 { 171 width = map.length; 172 height = map[0].length; 173 ySections = (height + 63) >> 6; 174 yEndMask = -1L >>> (64 - (height & 63)); 175 data = new long[width * ySections]; 176 for (int x = 0; x < width; x++) { 177 for (int y = 0; y < height; y++) { 178 if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 179 } 180 } 181 counts = new int[width * ySections]; 182 tallied = false; 183 } 184 /** 185 * Reassigns this GreasedRegion with the given rectangular char array, reusing the current data storage (without 186 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 187 * this are always cleared, then any value that equals yes is considered "on", and any other value considered "off." 188 * @param map a rectangular 2D char array where yes is on and everything else is off 189 * @param yes which char to encode as "on" 190 * @return this for chaining 191 */ 192 public GreasedRegion refill(final char[][] map, final char yes) { 193 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 194 Arrays.fill(data, 0L); 195 for (int x = 0; x < width; x++) { 196 for (int y = 0; y < height; y++) { 197 data[x * ySections + (y >> 6)] |= ((map[x][y] == yes) ? 1L : 0L) << (y & 63); 198 } 199 } 200 tallied = false; 201 return this; 202 } else { 203 width = (map == null) ? 0 : map.length; 204 height = (map == null || map.length <= 0) ? 0 : map[0].length; 205 ySections = (height + 63) >> 6; 206 yEndMask = -1L >>> (64 - (height & 63)); 207 data = new long[width * ySections]; 208 for (int x = 0; x < width; x++) { 209 for (int y = 0; y < height; y++) { 210 if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 211 } 212 } 213 counts = new int[width * ySections]; 214 tallied = false; 215 return this; 216 } 217 } 218 219 /** 220 * Constructs a GreasedRegion with the given rectangular char array, with width of map.length and height of 221 * map[0].length, any value that equals yes is considered "on", and any other value considered "off." 222 * @param map a rectangular 2D char array where yes is on and everything else is off 223 * @param yes which char to encode as "on" 224 */ 225 public GreasedRegion(final char[][] map, final char[] yes) 226 { 227 width = map.length; 228 height = map[0].length; 229 ySections = (height + 63) >> 6; 230 yEndMask = -1L >>> (64 - (height & 63)); 231 data = new long[width * ySections]; 232 for (int x = 0; x < width; x++) { 233 for (int y = 0; y < height; y++) { 234 for(char e : yes) 235 { 236 if(map[x][y] == e) { 237 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 238 break; 239 } 240 } 241 } 242 } 243 counts = new int[width * ySections]; 244 tallied = false; 245 } 246 /** 247 * Reassigns this GreasedRegion with the given rectangular char array, reusing the current data storage (without 248 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 249 * this are always cleared, then any value that equals yes is considered "on", and any other value considered "off." 250 * @param map a rectangular 2D char array where yes is on and everything else is off 251 * @param yes which char to encode as "on" 252 * @return this for chaining 253 */ 254 public GreasedRegion refill(final char[][] map, final char[] yes) { 255 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 256 Arrays.fill(data, 0L); 257 for (int x = 0; x < width; x++) { 258 for (int y = 0; y < height; y++) { 259 for(char e : yes) 260 { 261 if(map[x][y] == e) { 262 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 263 break; 264 } 265 } 266 } 267 } 268 tallied = false; 269 return this; 270 } else { 271 width = (map == null) ? 0 : map.length; 272 height = (map == null || map.length <= 0) ? 0 : map[0].length; 273 ySections = (height + 63) >> 6; 274 yEndMask = -1L >>> (64 - (height & 63)); 275 data = new long[width * ySections]; 276 for (int x = 0; x < width; x++) { 277 for (int y = 0; y < height; y++) { 278 for(char e : yes) 279 { 280 if(map[x][y] == e) { 281 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 282 break; 283 } 284 } 285 } 286 } 287 counts = new int[width * ySections]; 288 tallied = false; 289 return this; 290 } 291 } 292 293 /** 294 * Weird constructor that takes a String array, _as it would be printed_, so each String is a row and indexing would 295 * be done with y, x instead of the normal x, y. 296 * @param map String array (as printed, not the normal storage) where each String is a row 297 * @param yes the char to consider "on" in the GreasedRegion 298 */ 299 public GreasedRegion(final String[] map, final char yes) 300 { 301 height = map.length; 302 width = map[0].length(); 303 ySections = (height + 63) >> 6; 304 yEndMask = -1L >>> (64 - (height & 63)); 305 data = new long[width * ySections]; 306 for (int x = 0; x < width; x++) { 307 for (int y = 0; y < height; y++) { 308 if(map[y].charAt(x) == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 309 } 310 } 311 counts = new int[width * ySections]; 312 tallied = false; 313 } 314 315 /** 316 * Weird refill method that takes a String array, _as it would be printed_, so each String is a row and indexing 317 * would be done with y, x instead of the normal x, y. 318 * @param map String array (as printed, not the normal storage) where each String is a row 319 * @param yes the char to consider "on" in the GreasedRegion 320 * @return this for chaining 321 */ 322 public GreasedRegion refill(final String[] map, final char yes) { 323 if (map != null && map.length > 0 && height == map.length && width == map[0].length()) { 324 Arrays.fill(data, 0L); 325 for (int x = 0; x < width; x++) { 326 for (int y = 0; y < height; y++) { 327 data[x * ySections + (y >> 6)] |= ((map[y].charAt(x) == yes) ? 1L : 0L) << (y & 63); 328 } 329 } 330 tallied = false; 331 return this; 332 } else { 333 height = (map == null) ? 0 : map.length; 334 width = (map == null || map.length <= 0) ? 0 : map[0].length(); 335 ySections = (height + 63) >> 6; 336 yEndMask = -1L >>> (64 - (height & 63)); 337 data = new long[width * ySections]; 338 for (int x = 0; x < width; x++) { 339 for (int y = 0; y < height; y++) { 340 if(map[y].charAt(y) == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 341 } 342 } 343 counts = new int[width * ySections]; 344 tallied = false; 345 return this; 346 } 347 } 348 /** 349 * Constructs a GreasedRegion with the given rectangular int array, with width of map.length and height of 350 * map[0].length, any value that equals yes is considered "on", and any other value considered "off." 351 * @param map a rectangular 2D int array where an int == yes is on and everything else is off 352 * @param yes which int to encode as "on" 353 */ 354 public GreasedRegion(final int[][] map, final int yes) 355 { 356 width = map.length; 357 height = map[0].length; 358 ySections = (height + 63) >> 6; 359 yEndMask = -1L >>> (64 - (height & 63)); 360 data = new long[width * ySections]; 361 for (int x = 0; x < width; x++) { 362 for (int y = 0; y < height; y++) { 363 if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 364 } 365 } 366 counts = new int[width * ySections]; 367 tallied = false; 368 } 369 /** 370 * Reassigns this GreasedRegion with the given rectangular int array, reusing the current data storage (without 371 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 372 * this are always cleared, then any value that equals yes is considered "on", and any other value considered "off." 373 * @param map a rectangular 2D int array where an int == yes is on and everything else is off 374 * @param yes which int to encode as "on" 375 * @return this for chaining 376 */ 377 public GreasedRegion refill(final int[][] map, final int yes) { 378 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 379 Arrays.fill(data, 0L); 380 for (int x = 0; x < width; x++) { 381 for (int y = 0; y < height; y++) { 382 data[x * ySections + (y >> 6)] |= ((map[x][y] == yes) ? 1L : 0L) << (y & 63); 383 } 384 } 385 tallied = false; 386 return this; 387 } else { 388 width = (map == null) ? 0 : map.length; 389 height = (map == null || map.length <= 0) ? 0 : map[0].length; 390 ySections = (height + 63) >> 6; 391 yEndMask = -1L >>> (64 - (height & 63)); 392 data = new long[width * ySections]; 393 for (int x = 0; x < width; x++) { 394 for (int y = 0; y < height; y++) { 395 if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 396 } 397 } 398 counts = new int[width * ySections]; 399 tallied = false; 400 return this; 401 } 402 } 403 404 /** 405 * Constructs this GreasedRegion using an int[][], treating cells as on if they are greater than or equal to lower 406 * and less than upper, or off otherwise. 407 * @param map an int[][] that should have some ints between lower and upper 408 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 409 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 410 */ 411 public GreasedRegion(final int[][] map, final int lower, final int upper) 412 { 413 width = map.length; 414 height = map[0].length; 415 ySections = (height + 63) >> 6; 416 yEndMask = -1L >>> (64 - (height & 63)); 417 data = new long[width * ySections]; 418 int[] column; 419 for (int x = 0; x < width; x++) { 420 column = map[x]; 421 for (int y = 0; y < height; y++) { 422 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 423 } 424 } 425 counts = new int[width * ySections]; 426 tallied = false; 427 } 428 429 /** 430 * Reassigns this GreasedRegion with the given rectangular int array, reusing the current data storage (without 431 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 432 * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than 433 * upper, or off otherwise. 434 * @param map a rectangular 2D int array that should have some values between lower and upper 435 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 436 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 437 * @return this for chaining 438 */ 439 public GreasedRegion refill(final int[][] map, final int lower, final int upper) { 440 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 441 Arrays.fill(data, 0L); 442 int[] column; 443 for (int x = 0; x < width; x++) { 444 column = map[x]; 445 for (int y = 0; y < height; y++) { 446 data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63); 447 } 448 } 449 tallied = false; 450 return this; 451 } else { 452 width = (map == null) ? 0 : map.length; 453 height = (map == null || map.length <= 0) ? 0 : map[0].length; 454 ySections = (height + 63) >> 6; 455 yEndMask = -1L >>> (64 - (height & 63)); 456 data = new long[width * ySections]; 457 int[] column; 458 for (int x = 0; x < width; x++) { 459 column = map[x]; 460 for (int y = 0; y < height; y++) { 461 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 462 } 463 } 464 counts = new int[width * ySections]; 465 tallied = false; 466 return this; 467 } 468 } 469 470 /** 471 * Constructs this GreasedRegion using a byte[][], treating cells as on if they are greater than or equal to lower 472 * and less than upper, or off otherwise. 473 * @param map a byte[][] that should have some bytes between lower and upper 474 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 475 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 476 */ 477 public GreasedRegion(final byte[][] map, final int lower, final int upper) 478 { 479 width = map.length; 480 height = map[0].length; 481 ySections = (height + 63) >> 6; 482 yEndMask = -1L >>> (64 - (height & 63)); 483 data = new long[width * ySections]; 484 byte[] column; 485 for (int x = 0; x < width; x++) { 486 column = map[x]; 487 for (int y = 0; y < height; y++) { 488 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 489 } 490 } 491 counts = new int[width * ySections]; 492 tallied = false; 493 } 494 495 /** 496 * Reassigns this GreasedRegion with the given rectangular byte array, reusing the current data storage (without 497 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 498 * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than 499 * upper, or off otherwise. 500 * @param map a rectangular 2D byte array that should have some values between lower and upper 501 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 502 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 503 * @return this for chaining 504 */ 505 public GreasedRegion refill(final byte[][] map, final int lower, final int upper) { 506 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 507 Arrays.fill(data, 0L); 508 byte[] column; 509 for (int x = 0; x < width; x++) { 510 column = map[x]; 511 for (int y = 0; y < height; y++) { 512 data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63); 513 } 514 } 515 tallied = false; 516 return this; 517 } else { 518 width = (map == null) ? 0 : map.length; 519 height = (map == null || map.length <= 0) ? 0 : map[0].length; 520 ySections = (height + 63) >> 6; 521 yEndMask = -1L >>> (64 - (height & 63)); 522 data = new long[width * ySections]; 523 byte[] column; 524 for (int x = 0; x < width; x++) { 525 column = map[x]; 526 for (int y = 0; y < height; y++) { 527 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 528 } 529 } 530 counts = new int[width * ySections]; 531 tallied = false; 532 return this; 533 } 534 } 535 536 537 /** 538 * Constructs this GreasedRegion using a short[][], treating cells as on if they are greater than or equal to lower 539 * and less than upper, or off otherwise. 540 * @param map a short[][] that should have some shorts between lower and upper 541 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 542 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 543 */ 544 public GreasedRegion(final short[][] map, final int lower, final int upper) 545 { 546 width = map.length; 547 height = map[0].length; 548 ySections = (height + 63) >> 6; 549 yEndMask = -1L >>> (64 - (height & 63)); 550 data = new long[width * ySections]; 551 short[] column; 552 for (int x = 0; x < width; x++) { 553 column = map[x]; 554 for (int y = 0; y < height; y++) { 555 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 556 } 557 } 558 counts = new int[width * ySections]; 559 tallied = false; 560 } 561 562 /** 563 * Reassigns this GreasedRegion with the given rectangular short array, reusing the current data storage (without 564 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 565 * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than 566 * upper, or off otherwise. 567 * @param map a rectangular 2D short array that should have some values between lower and upper 568 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 569 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 570 * @return this for chaining 571 */ 572 public GreasedRegion refill(final short[][] map, final int lower, final int upper) { 573 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 574 Arrays.fill(data, 0L); 575 short[] column; 576 for (int x = 0; x < width; x++) { 577 column = map[x]; 578 for (int y = 0; y < height; y++) { 579 data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63); 580 } 581 } 582 tallied = false; 583 return this; 584 } else { 585 width = (map == null) ? 0 : map.length; 586 height = (map == null || map.length <= 0) ? 0 : map[0].length; 587 ySections = (height + 63) >> 6; 588 yEndMask = -1L >>> (64 - (height & 63)); 589 data = new long[width * ySections]; 590 short[] column; 591 for (int x = 0; x < width; x++) { 592 column = map[x]; 593 for (int y = 0; y < height; y++) { 594 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 595 } 596 } 597 counts = new int[width * ySections]; 598 tallied = false; 599 return this; 600 } 601 } 602 /** 603 * Constructs this GreasedRegion using a double[][] (typically one generated by 604 * {@link squidpony.squidai.DijkstraMap}) that only stores two relevant states: an "on" state for values less than 605 * or equal to upperBound (inclusive), and an "off" state for anything else. 606 * @param map a double[][] that probably relates in some way to DijkstraMap. 607 * @param upperBound upper inclusive; any double greater than this will be off, any others will be on 608 */ 609 public GreasedRegion(final double[][] map, final double upperBound) 610 { 611 width = map.length; 612 height = map[0].length; 613 ySections = (height + 63) >> 6; 614 yEndMask = -1L >>> (64 - (height & 63)); 615 data = new long[width * ySections]; 616 for (int x = 0; x < width; x++) { 617 for (int y = 0; y < height; y++) { 618 if(map[x][y] <= upperBound) 619 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 620 } 621 } 622 counts = new int[width * ySections]; 623 tallied = false; 624 } 625 /** 626 * Reassigns this GreasedRegion with the given rectangular double array, reusing the current data storage (without 627 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 628 * this are always cleared, then cells are treated as on if they are less than or equal to upperBound, or off 629 * otherwise. 630 * @param map a rectangular 2D double array that should usually have some values less than or equal to upperBound 631 * @param upperBound upper bound, inclusive; all on cells will have values in map that are less than or equal to this 632 * @return this for chaining 633 */ 634 public GreasedRegion refill(final double[][] map, final double upperBound) { 635 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 636 Arrays.fill(data, 0L); 637 for (int x = 0; x < width; x++) { 638 for (int y = 0; y < height; y++) { 639 if(map[x][y] <= upperBound) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 640 } 641 } 642 tallied = false; 643 return this; 644 } else { 645 width = (map == null) ? 0 : map.length; 646 height = (map == null || map.length <= 0) ? 0 : map[0].length; 647 ySections = (height + 63) >> 6; 648 yEndMask = -1L >>> (64 - (height & 63)); 649 data = new long[width * ySections]; 650 for (int x = 0; x < width; x++) { 651 for (int y = 0; y < height; y++) { 652 if(map[x][y] <= upperBound) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 653 } 654 } 655 counts = new int[width * ySections]; 656 tallied = false; 657 return this; 658 } 659 } 660 661 /** 662 * Constructs this GreasedRegion using a double[][] (typically one generated by 663 * {@link squidpony.squidai.DijkstraMap}) that only stores two relevant states: an "on" state for values between 664 * lowerBound (inclusive) and upperBound (exclusive), and an "off" state for anything else. 665 * @param map a double[][] that probably relates in some way to DijkstraMap. 666 * @param lowerBound lower inclusive; any double lower than this will be off, any equal to or greater than this, 667 * but less than upper, will be on 668 * @param upperBound upper exclusive; any double greater than or equal to this this will be off, any doubles both 669 * less than this and equal to or greater than lower will be on 670 */ 671 public GreasedRegion(final double[][] map, final double lowerBound, final double upperBound) 672 { 673 width = map.length; 674 height = map[0].length; 675 ySections = (height + 63) >> 6; 676 yEndMask = -1L >>> (64 - (height & 63)); 677 data = new long[width * ySections]; 678 for (int x = 0; x < width; x++) { 679 for (int y = 0; y < height; y++) { 680 if(map[x][y] >= lowerBound && map[x][y] < upperBound) 681 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 682 } 683 } 684 counts = new int[width * ySections]; 685 tallied = false; 686 } 687 /** 688 * Reassigns this GreasedRegion with the given rectangular double array, reusing the current data storage (without 689 * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in 690 * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than 691 * upper, or off otherwise. 692 * @param map a rectangular 2D double array that should have some values between lower and upper 693 * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower 694 * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper 695 * @return this for chaining 696 */ 697 public GreasedRegion refill(final double[][] map, final double lower, final double upper) { 698 if (map != null && map.length > 0 && width == map.length && height == map[0].length) { 699 Arrays.fill(data, 0L); 700 double[] column; 701 for (int x = 0; x < width; x++) { 702 column = map[x]; 703 for (int y = 0; y < height; y++) { 704 data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63); 705 } 706 } 707 tallied = false; 708 return this; 709 } else { 710 width = (map == null) ? 0 : map.length; 711 height = (map == null || map.length <= 0) ? 0 : map[0].length; 712 ySections = (height + 63) >> 6; 713 yEndMask = -1L >>> (64 - (height & 63)); 714 data = new long[width * ySections]; 715 double[] column; 716 for (int x = 0; x < width; x++) { 717 column = map[x]; 718 for (int y = 0; y < height; y++) { 719 if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 720 } 721 } 722 counts = new int[width * ySections]; 723 tallied = false; 724 return this; 725 } 726 } 727 728 /** 729 * Constructs this GreasedRegion using a double[][] that only stores two relevant states: an "on" state for values 730 * between lowerBound (inclusive) and upperBound (exclusive), and an "off" state for anything else. This variant 731 * scales the input so each "on" position in map produces a 2x2 on area if scale is 2, a 3x3 area if scale is 3, and 732 * so on. 733 * @param map a double[][]; depending on scale, the GreasedRegion may have different width and height 734 * @param lowerBound lower inclusive; any double lower than this will be off, any equal to or greater than this, 735 * but less than upper, will be on 736 * @param upperBound upper exclusive; any double greater than or equal to this this will be off, any doubles both 737 * less than this and equal to or greater than lower will be on 738 * @param scale the size of the square of cells in this that each "on" value in map will correspond to 739 */ 740 public GreasedRegion(final double[][] map, final double lowerBound, final double upperBound, int scale) 741 { 742 scale = Math.min(63, Math.max(1, scale)); 743 int baseWidth = map.length, baseHeight = map[0].length; 744 width = baseWidth * scale; 745 height = baseHeight * scale; 746 ySections = (height + 63) >> 6; 747 yEndMask = -1L >>> (64 - (height & 63)); 748 data = new long[width * ySections]; 749 long shape = (1L << scale) - 1L, leftover; 750 for (int bx = 0, x = 0; bx < baseWidth; bx++, x += scale) { 751 for (int by = 0, y = 0; by < baseHeight; by++, y += scale) { 752 if(map[bx][by] >= lowerBound && map[bx][by] < upperBound) { 753 for (int i = 0; i < scale; i++) { 754 data[(x + i) * ySections + (y >> 6)] |= shape << (y & 63); 755 if((leftover = (y + scale - 1 & 63) + 1) < (y & 63) + 1 && (y + leftover >> 6) < ySections) 756 { 757 data[(x + i) * ySections + (y >> 6) + 1] |= (1L << leftover) - 1L; 758 } 759 } 760 } 761 } 762 } 763 counts = new int[width * ySections]; 764 tallied = false; 765 } 766 /** 767 * Reassigns this GreasedRegion with the given rectangular double array, reusing the current data storage (without 768 * extra allocations) if {@code this.width == map.length * scale && this.height == map[0].length * scale}. The 769 * current values stored in this are always cleared, then cells are treated as on if they are greater than or equal 770 * to lower and less than upper, or off otherwise, before considering scaling. This variant scales the input so each 771 * "on" position in map produces a 2x2 on area if scale is 2, a 3x3 area if scale is 3, and so on. 772 * @param map a double[][]; depending on scale, the GreasedRegion may have different width and height 773 * @param lowerBound lower inclusive; any double lower than this will be off, any equal to or greater than this, 774 * but less than upper, will be on 775 * @param upperBound upper exclusive; any double greater than or equal to this this will be off, any doubles both 776 * less than this and equal to or greater than lower will be on 777 * @param scale the size of the square of cells in this that each "on" value in map will correspond to 778 * @return this for chaining 779 */ 780 public GreasedRegion refill(final double[][] map, final double lowerBound, final double upperBound, int scale) { 781 scale = Math.min(63, Math.max(1, scale)); 782 if (map != null && map.length > 0 && width == map.length * scale && height == map[0].length * scale) { 783 Arrays.fill(data, 0L); 784 double[] column; 785 int baseWidth = map.length, baseHeight = map[0].length; 786 long shape = (1L << scale) - 1L, leftover; 787 for (int bx = 0, x = 0; bx < baseWidth; bx++, x += scale) { 788 column = map[bx]; 789 for (int by = 0, y = 0; by < baseHeight; by++, y += scale) { 790 if(column[by] >= lowerBound && column[by] < upperBound) { 791 for (int i = 0; i < scale; i++) { 792 data[(x + i) * ySections + (y >> 6)] |= shape << (y & 63); 793 if((leftover = (y + scale - 1 & 63) + 1) < (y & 63) + 1 && (y + leftover >> 6) < ySections) 794 { 795 data[(x + i) * ySections + (y >> 6) + 1] |= (1L << leftover) - 1L; 796 } 797 } 798 } 799 } 800 } 801 tallied = false; 802 return this; 803 } else { 804 int baseWidth = (map == null) ? 0 : map.length, 805 baseHeight = (map == null || map.length <= 0) ? 0 : map[0].length; 806 width = baseWidth * scale; 807 height = baseHeight * scale; 808 ySections = (height + 63) >> 6; 809 yEndMask = -1L >>> (64 - (height & 63)); 810 data = new long[width * ySections]; 811 long shape = (1L << scale) - 1L, leftover; 812 for (int bx = 0, x = 0; bx < baseWidth; bx++, x += scale) { 813 for (int by = 0, y = 0; by < baseHeight; by++, y += scale) { 814 if(map[bx][by] >= lowerBound && map[bx][by] < upperBound) { 815 for (int i = 0; i < scale; i++) { 816 data[(x + i) * ySections + (y >> 6)] |= shape << (y & 63); 817 if((leftover = (y + scale - 1 & 63)) < y && y < height - leftover) 818 { 819 data[(x + i) * ySections + (y >> 6) + 1] |= (1L << leftover) - 1L; 820 } 821 } 822 } 823 } 824 } 825 counts = new int[width * ySections]; 826 tallied = false; 827 return this; 828 } 829 } 830 831 /** 832 * Constructs a GreasedRegion with the given 1D boolean array, with the given width and height, where an [x][y] 833 * position is obtained from bits given an index n with x = n / height, y = n % height, any value of true 834 * considered "on", and any value of false considered "off." 835 * @param bits a 1D boolean array where true is on and false is off 836 * @param width the width of the desired GreasedRegion; width * height should equal bits.length 837 * @param height the height of the desired GreasedRegion; width * height should equal bits.length 838 */ 839 public GreasedRegion(final boolean[] bits, final int width, final int height) 840 { 841 this.width = width; 842 this.height = height; 843 ySections = (height + 63) >> 6; 844 yEndMask = -1L >>> (64 - (height & 63)); 845 data = new long[width * ySections]; 846 for (int a = 0, x = 0, y = 0; a < bits.length; a++, x = a / height, y = a % height) { 847 if(bits[a]) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 848 } 849 counts = new int[width * ySections]; 850 tallied = false; 851 } 852 /** 853 * Reassigns this GreasedRegion with the given 1D boolean array, reusing the current data storage (without 854 * extra allocations) if this.width == width and this.height == height, where an [x][y] 855 * position is obtained from bits given an index n with x = n / height, y = n % height, any value of true 856 * considered "on", and any value of false considered "off." 857 * @param bits a 1D boolean array where true is on and false is off 858 * @param width the width of the desired GreasedRegion; width * height should equal bits.length 859 * @param height the height of the desired GreasedRegion; width * height should equal bits.length 860 * @return this for chaining 861 */ 862 public GreasedRegion refill(final boolean[] bits, final int width, final int height) { 863 if (bits != null && this.width == width && this.height == height) { 864 Arrays.fill(data, 0L); 865 for (int a = 0, x = 0, y = 0; a < bits.length; a++, x = a / height, y = a % height) { 866 data[x * ySections + (y >> 6)] |= (bits[a] ? 1L : 0L) << (y & 63); 867 } 868 tallied = false; 869 return this; 870 } else { 871 this.width = (bits == null || width < 0) ? 0 : width; 872 this.height = (bits == null || bits.length <= 0 || height < 0) ? 0 : height; 873 ySections = (this.height + 63) >> 6; 874 yEndMask = -1L >>> (64 - (this.height & 63)); 875 data = new long[this.width * ySections]; 876 if(bits != null) { 877 for (int a = 0, x = 0, y = 0; a < bits.length; a++, x = a / this.height, y = a % this.height) { 878 if (bits[a]) data[x * ySections + (y >> 6)] |= 1L << (y & 63); 879 } 880 } 881 counts = new int[width * ySections]; 882 tallied = false; 883 return this; 884 } 885 } 886 887 /** 888 * Constructor for an empty GreasedRegion of the given width and height. 889 * GreasedRegions are mutable, so you can add to this with insert() or insertSeveral(), among others. 890 * @param width the maximum width for the GreasedRegion 891 * @param height the maximum height for the GreasedRegion 892 */ 893 public GreasedRegion(final int width, final int height) 894 { 895 this.width = width; 896 this.height = height; 897 ySections = (height + 63) >> 6; 898 yEndMask = -1L >>> (64 - (height & 63)); 899 data = new long[width * ySections]; 900 counts = new int[width * ySections]; 901 ct = 0; 902 tallied = true; 903 } 904 905 /** 906 * If this GreasedRegion has the same width and height passed as parameters, this acts the same as {@link #empty()}, 907 * makes no allocations, and returns this GreasedRegion with its contents all "off"; otherwise, this does allocate 908 * a differently-sized amount of internal data to match the new width and height, sets the fields to all match the 909 * new width and height, and returns this GreasedRegion with its new width and height, with all contents "off". This 910 * is meant for cases where a GreasedRegion may be reused effectively, but its size may not always be the same. 911 * @param width the width to potentially resize this GreasedRegion to 912 * @param height the height to potentially resize this GreasedRegion to 913 * @return this GreasedRegion, always with all contents "off", and with the height and width set. 914 */ 915 public GreasedRegion resizeAndEmpty(final int width, final int height) { 916 if (width == this.width && height == this.height) { 917 Arrays.fill(data, 0L); 918 Arrays.fill(counts, 0); 919 ct = 0; 920 tallied = true; 921 } else { 922 this.width = Math.max(width, 0); 923 this.height = Math.max(height, 0); 924 ySections = (this.height + 63) >> 6; 925 yEndMask = -1L >>> (64 - (this.height & 63)); 926 data = new long[this.width * ySections]; 927 counts = new int[this.width * ySections]; 928 ct = 0; 929 tallied = true; 930 } 931 return this; 932 933 } 934 935 /** 936 * Constructor for a GreasedRegion that contains a single "on" cell, and has the given width and height. 937 * Note that to avoid confusion with the constructor that takes multiple Coord values, this takes the single "on" 938 * Coord first, while the multiple-Coord constructor takes its vararg or array of Coords last. 939 * @param single the one (x,y) point to store as "on" in this GreasedRegion 940 * @param width the maximum width for the GreasedRegion 941 * @param height the maximum height for the GreasedRegion 942 */ 943 public GreasedRegion(final Coord single, final int width, final int height) 944 { 945 this.width = width; 946 this.height = height; 947 ySections = (height + 63) >> 6; 948 yEndMask = -1L >>> (64 - (height & 63)); 949 data = new long[width * ySections]; 950 counts = new int[width * ySections]; 951 if(single.x < width && single.y < height && single.x >= 0 && single.y >= 0) 952 { 953 data[ct = single.x * ySections + (single.y >> 6)] |= 1L << (single.y & 63); 954 counts[ct] = 1; 955 ct = 1; 956 tallied = true; 957 } 958 else 959 { 960 ct = 0; 961 tallied = true; 962 } 963 } 964 965 /** 966 * Constructor for a GreasedRegion that can have several "on" cells specified, and has the given width and height. 967 * Note that to avoid confusion with the constructor that takes one Coord value, this takes the vararg or array of 968 * Coords last, while the single-Coord constructor takes its one Coord first. 969 * @param width the maximum width for the GreasedRegion 970 * @param height the maximum height for the GreasedRegion 971 * @param points an array or vararg of Coord to store as "on" in this GreasedRegion 972 */ 973 public GreasedRegion(final int width, final int height, final Coord... points) 974 { 975 this.width = width; 976 this.height = height; 977 ySections = (height + 63) >> 6; 978 yEndMask = -1L >>> (64 - (height & 63)); 979 data = new long[width * ySections]; 980 if(points != null) 981 { 982 for (int i = 0, x, y; i < points.length; i++) { 983 x = points[i].x; 984 y = points[i].y; 985 if(x < width && y < height && x >= 0 && y >= 0) 986 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 987 } 988 } 989 counts = new int[width * ySections]; 990 tallied = false; 991 } 992 993 /** 994 * Constructor for a GreasedRegion that can have several "on" cells specified, and has the given width and height. 995 * Note that to avoid confusion with the constructor that takes one Coord value, this takes the Iterable of 996 * Coords last, while the single-Coord constructor takes its one Coord first. 997 * @param width the maximum width for the GreasedRegion 998 * @param height the maximum height for the GreasedRegion 999 * @param points an array or vararg of Coord to store as "on" in this GreasedRegion 1000 */ 1001 public GreasedRegion(final int width, final int height, final Iterable<Coord> points) 1002 { 1003 this.width = width; 1004 this.height = height; 1005 ySections = (height + 63) >> 6; 1006 yEndMask = -1L >>> (64 - (height & 63)); 1007 data = new long[width * ySections]; 1008 if(points != null) { 1009 int x, y; 1010 for (Coord c : points) { 1011 x = c.x; 1012 y = c.y; 1013 if (x < width && y < height && x >= 0 && y >= 0) 1014 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 1015 } 1016 } 1017 counts = new int[width * ySections]; 1018 tallied = false; 1019 1020 } 1021 1022 /** 1023 * Constructor for a random GreasedRegion of the given width and height, typically assigning approximately half of 1024 * the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more efficient than an RNG when 1025 * you're making a lot of calls on it. 1026 * @param random a RandomnessSource that should have a good nextLong() method; DiverRNG is excellent but Lathe32RNG is faster on GWT (only there) 1027 * @param width the maximum width for the GreasedRegion 1028 * @param height the maximum height for the GreasedRegion 1029 */ 1030 public GreasedRegion(final RandomnessSource random, final int width, final int height) 1031 { 1032 this.width = width; 1033 this.height = height; 1034 ySections = (height + 63) >> 6; 1035 yEndMask = -1L >>> (64 - (height & 63)); 1036 data = new long[width * ySections]; 1037 for (int i = 0; i < width * ySections; i++) { 1038 data[i] = random.nextLong(); 1039 } 1040 if(ySections > 0 && yEndMask != -1) { 1041 for (int a = ySections - 1; a < data.length; a += ySections) { 1042 data[a] &= yEndMask; 1043 } 1044 } 1045 counts = new int[width * ySections]; 1046 tallied = false; 1047 } 1048 /** 1049 * Reassigns this GreasedRegion by filling it with random values from random, reusing the current data storage 1050 * (without extra allocations) if this.width == width and this.height == height, and typically assigning 1051 * approximately half of the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more 1052 * efficient than an RNG when you're making a lot of calls on it. 1053 * @param random a RandomnessSource that should have a good nextLong() method; DiverRNG is excellent but Lathe32RNG is faster on GWT (only there) 1054 * @param width the width of the desired GreasedRegion 1055 * @param height the height of the desired GreasedRegion 1056 * @return this for chaining 1057 */ 1058 public GreasedRegion refill(final RandomnessSource random, final int width, final int height) { 1059 if (random != null){ 1060 if(this.width == width && this.height == height) { 1061 for (int i = 0; i < width * ySections; i++) { 1062 data[i] = random.nextLong(); 1063 } 1064 } else { 1065 this.width = Math.max(width, 0); 1066 this.height = Math.max(height, 0); 1067 ySections = (this.height + 63) >> 6; 1068 yEndMask = -1L >>> (64 - (this.height & 63)); 1069 data = new long[this.width * ySections]; 1070 for (int i = 0; i < this.width * ySections; i++) { 1071 data[i] = random.nextLong(); 1072 } 1073 counts = new int[this.width * ySections]; 1074 } 1075 if(ySections > 0 && yEndMask != -1) { 1076 for (int a = ySections - 1; a < data.length; a += ySections) { 1077 data[a] &= yEndMask; 1078 } 1079 } 1080 tallied = false; 1081 } 1082 return this; 1083 } 1084 1085 /** 1086 * Constructor for a random GreasedRegion of the given width and height, typically assigning approximately half of 1087 * the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more efficient than an RNG when 1088 * you're making a lot of calls on it, so you may prefer {@link #GreasedRegion(RandomnessSource, int, int)}. 1089 * @param random an IRNG, such as an RNG, that this will use to generate its contents 1090 * @param width the maximum width for the GreasedRegion 1091 * @param height the maximum height for the GreasedRegion 1092 */ 1093 public GreasedRegion(final IRNG random, final int width, final int height) 1094 { 1095 this.width = width; 1096 this.height = height; 1097 ySections = (height + 63) >> 6; 1098 yEndMask = -1L >>> (64 - (height & 63)); 1099 data = new long[width * ySections]; 1100 for (int i = 0; i < width * ySections; i++) { 1101 data[i] = random.nextLong(); 1102 } 1103 if(ySections > 0 && yEndMask != -1) { 1104 for (int a = ySections - 1; a < data.length; a += ySections) { 1105 data[a] &= yEndMask; 1106 } 1107 } 1108 counts = new int[width * ySections]; 1109 tallied = false; 1110 } 1111 /** 1112 * Reassigns this GreasedRegion by filling it with random values from random, reusing the current data storage 1113 * (without extra allocations) if this.width == width and this.height == height, and typically assigning 1114 * approximately half of the cells in this to "on" and the rest to off. 1115 * @param random an IRNG that should have a good nextLong() method; an RNG constructed with the default RandomnessSource will be fine 1116 * @param width the width of the desired GreasedRegion 1117 * @param height the height of the desired GreasedRegion 1118 * @return this for chaining 1119 */ 1120 public GreasedRegion refill(final IRNG random, final int width, final int height) { 1121 if (random != null){ 1122 if(this.width == width && this.height == height) { 1123 for (int i = 0; i < width * ySections; i++) { 1124 data[i] = random.nextLong(); 1125 } 1126 } else { 1127 this.width = Math.max(width, 0); 1128 this.height = Math.max(height, 0); 1129 ySections = (this.height + 63) >> 6; 1130 yEndMask = -1L >>> (64 - (this.height & 63)); 1131 data = new long[this.width * ySections]; 1132 for (int i = 0; i < this.width * ySections; i++) { 1133 data[i] = random.nextLong(); 1134 } 1135 counts = new int[width * ySections]; 1136 } 1137 if(ySections > 0 && yEndMask != -1) { 1138 for (int a = ySections - 1; a < data.length; a += ySections) { 1139 data[a] &= yEndMask; 1140 } 1141 } 1142 tallied = false; 1143 } 1144 return this; 1145 } 1146 1147 /** 1148 * Constructor for a random GreasedRegion of the given width and height, trying to set the given fraction of cells 1149 * to on. Depending on the value of fraction, this makes between 0 and 6 calls to the nextLong() method of random's 1150 * internal RandomnessSource, per 64 cells of this GreasedRegion (if height is not a multiple of 64, round up to get 1151 * the number of calls this makes). As such, this sacrifices the precision of the fraction to obtain significantly 1152 * better speed than generating one random number per cell, although the precision is probably good enough (fraction 1153 * is effectively rounded down to the nearest multiple of 0.015625, and clamped between 0.0 and 1.0). The parameter 1154 * {@code random} can be an object like a {@link DiverRNG}, an {@link RNG} backed by a well-distributed 1155 * RandomnessSource like its default, DiverRNG, a {@link GWTRNG} (especially if you target GWT, where it will 1156 * perform much better than most alternatives), or any of various other RandomnessSource implementations that 1157 * distribute bits well for {@link RandomnessSource#nextLong()}, but should not be intentionally-biased RNGs like 1158 * {@link DharmaRNG} or {@link EditRNG}, nor double-based QRNGs like {@link VanDerCorputQRNG} or {@link SobolQRNG}. 1159 * @param random a RandomnessSource that should produce high-quality long values, like the defaults for {@link RNG} 1160 * @param fraction between 0.0 and 1.0 (clamped), only considering a precision of 1/64.0 (0.015625) between steps 1161 * @param width the maximum width for the GreasedRegion 1162 * @param height the maximum height for the GreasedRegion 1163 */ 1164 public GreasedRegion(final RandomnessSource random, final double fraction, final int width, final int height) 1165 { 1166 this.width = width; 1167 this.height = height; 1168 int bitCount = (int) (fraction * 64); 1169 ySections = (height + 63) >> 6; 1170 yEndMask = -1L >>> (64 - (height & 63)); 1171 data = new long[width * ySections]; 1172 for (int i = 0; i < width * ySections; i++) { 1173 data[i] = approximateBits(random, bitCount); 1174 } 1175 if(ySections > 0 && yEndMask != -1) { 1176 for (int a = ySections - 1; a < data.length; a += ySections) { 1177 data[a] &= yEndMask; 1178 } 1179 } 1180 counts = new int[width * ySections]; 1181 tallied = false; 1182 } 1183 /** 1184 * Reassigns this GreasedRegion randomly, reusing the current data storage (without extra allocations) if this.width 1185 * == width and this.height == height, while trying to set the given fraction of cells to on. Depending on the value 1186 * of fraction, this makes between 0 and 6 calls to the nextLong() method of random's internal RandomnessSource, per 1187 * 64 cells of this GreasedRegion (if height is not a multiple of 64, round up to get the number of calls this 1188 * makes). As such, this sacrifices the precision of the fraction to obtain significantly better speed than 1189 * generating one random number per cell, although the precision is probably good enough (fraction is effectively 1190 * rounded down to the nearest multiple of 0.015625, and clamped between 0.0 and 1.0). The parameter {@code random} 1191 * can be an object like a {@link DiverRNG}, an {@link RNG} backed by a well-distributed RandomnessSource like its 1192 * default, DiverRNG, a {@link GWTRNG} (especially if you target GWT, where it will perform much better than most 1193 * alternatives), or any of various other RandomnessSource implementations that distribute bits well for 1194 * {@link RandomnessSource#nextLong()}, but should not be intentionally-biased RNGs like {@link DharmaRNG} or 1195 * {@link EditRNG}, nor double-based QRNGs like {@link VanDerCorputQRNG} or {@link SobolQRNG}. 1196 * @param random a RandomnessSource that should produce high-quality long values, like the defaults for {@link RNG} 1197 * @param fraction between 0.0 and 1.0 (clamped), only considering a precision of 1/64.0 (0.015625) between steps 1198 * @param width the maximum width for the GreasedRegion 1199 * @param height the maximum height for the GreasedRegion 1200 * @return this for chaining 1201 */ 1202 public GreasedRegion refill(final RandomnessSource random, final double fraction, final int width, final int height) { 1203 if (random != null){ 1204 int bitCount = (int) (fraction * 64); 1205 if(this.width == width && this.height == height) { 1206 for (int i = 0; i < width * ySections; i++) { 1207 data[i] = approximateBits(random, bitCount); 1208 } 1209 } else { 1210 this.width = Math.max(width, 0); 1211 this.height = Math.max(height, 0); 1212 ySections = (this.height + 63) >> 6; 1213 yEndMask = -1L >>> (64 - (this.height & 63)); 1214 data = new long[this.width * ySections]; 1215 for (int i = 0; i < this.width * ySections; i++) { 1216 data[i] = approximateBits(random, bitCount); 1217 } 1218 counts = new int[width * ySections]; 1219 } 1220 if(ySections > 0 && yEndMask != -1) { 1221 for (int a = ySections - 1; a < data.length; a += ySections) { 1222 data[a] &= yEndMask; 1223 } 1224 } 1225 } 1226 tallied = false; 1227 return this; 1228 } 1229 1230 /** 1231 * Copy constructor that takes another GreasedRegion and copies all of its data into this new one. 1232 * If you find yourself frequently using this constructor and assigning it to the same variable, consider using the 1233 * {@link #remake(GreasedRegion)} method on the variable instead, which will, if it has the same width and height 1234 * as the other GreasedRegion, avoid creating garbage and quickly fill the variable with the other's contents. 1235 * @see #copy() for a convenience method that just uses this constructor 1236 * @param other another GreasedRegion that will be copied into this new GreasedRegion 1237 */ 1238 public GreasedRegion(final GreasedRegion other) 1239 { 1240 width = other.width; 1241 height = other.height; 1242 ySections = other.ySections; 1243 yEndMask = other.yEndMask; 1244 data = new long[width * ySections]; 1245 counts = new int[width * ySections]; 1246 System.arraycopy(other.data, 0, data, 0, width * ySections); 1247 System.arraycopy(other.counts, 0, counts, 0, width * ySections); 1248 ct = other.ct; 1249 tallied = other.tallied; 1250 } 1251 1252 /** 1253 * Primarily for internal use, this constructor copies data2 exactly into the internal long array the new 1254 * GreasedRegion will use, and does not perform any validation steps to ensure that cells that would be "on" but are 1255 * outside the actual height of the GreasedRegion are actually removed (this only matters if height is not a 1256 * multiple of 64). 1257 * @param data2 a long array that is typically from another GreasedRegion, and would be hard to make otherwise 1258 * @param width the width of the GreasedRegion to construct 1259 * @param height the height of the GreasedRegion to construct 1260 */ 1261 public GreasedRegion(final long[] data2, final int width, final int height) 1262 { 1263 this.width = width; 1264 this.height = height; 1265 ySections = (height + 63) >> 6; 1266 yEndMask = -1L >>> (64 - (height & 63)); 1267 data = new long[width * ySections]; 1268 System.arraycopy(data2, 0, data, 0, width * ySections); 1269 if(ySections > 0 && yEndMask != -1) { 1270 for (int a = ySections - 1; a < data.length; a += ySections) { 1271 data[a] &= yEndMask; 1272 } 1273 } 1274 counts = new int[width * ySections]; 1275 tallied = false; 1276 } 1277 1278 /** 1279 * Primarily for internal use, this constructor copies data2 into the internal long array the new GreasedRegion will 1280 * use, but treats data2 as having the dimensions [dataWidth][dataHeight], and uses the potentially-different 1281 * dimensions [width][height] for the constructed GreasedRegion. This will truncate data2 on width, height, or both 1282 * if width or height is smaller than dataWidth or dataHeight. It will fill extra space with all "off" if width or 1283 * height is larger than dataWidth or dataHeight. It will interpret data2 as the same 2D shape regardless of the 1284 * width or height it is being assigned to, and data2 will not be reshaped by truncation. 1285 * @param data2 a long array that is typically from another GreasedRegion, and would be hard to make otherwise 1286 * @param dataWidth the width to interpret data2 as having 1287 * @param dataHeight the height to interpret data2 as having 1288 * @param width the width of the GreasedRegion to construct 1289 * @param height the height of the GreasedRegion to construct 1290 */ 1291 public GreasedRegion(final long[] data2, final int dataWidth, final int dataHeight, final int width, final int height) 1292 { 1293 this.width = width; 1294 this.height = height; 1295 ySections = (height + 63) >> 6; 1296 yEndMask = -1L >>> (64 - (height & 63)); 1297 data = new long[width * ySections]; 1298 1299 final int ySections2 = (dataHeight + 63) >> 6; 1300 if(ySections2 == 0) 1301 { 1302 counts = new int[0]; 1303 tallied = false; 1304 return; 1305 } 1306 if(ySections == 1) { 1307 System.arraycopy(data2, 0, data, 0, Math.min(dataWidth, width)); 1308 } 1309 else 1310 { 1311 if(dataHeight >= height) { 1312 for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) { 1313 System.arraycopy(data2, i, data, j, ySections); 1314 } 1315 } 1316 else 1317 { 1318 for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) { 1319 System.arraycopy(data2, i, data, j, ySections2); 1320 } 1321 } 1322 } 1323 if(ySections > 0 && yEndMask != -1) { 1324 for (int a = ySections - 1; a < data.length; a += ySections) { 1325 data[a] &= yEndMask; 1326 } 1327 } 1328 counts = new int[width * ySections]; 1329 tallied = false; 1330 } 1331 /** 1332 * Primarily for internal use, this method copies data2 into the internal long array the new GreasedRegion will 1333 * use, but treats data2 as having the dimensions [dataWidth][dataHeight], and uses the potentially-different 1334 * dimensions [width][height] for this GreasedRegion, potentially re-allocating the internal data this uses if width 1335 * and/or height are different from what they were. This will truncate data2 on width, height, or both if width or 1336 * height is smaller than dataWidth or dataHeight. It will fill extra space with all "off" if width or height is 1337 * larger than dataWidth or dataHeight. It will interpret data2 as the same 2D shape regardless of the width or 1338 * height it is being assigned to, and data2 will not be reshaped by truncation. 1339 * @param data2 a long array that is typically from another GreasedRegion, and would be hard to make otherwise 1340 * @param dataWidth the width to interpret data2 as having 1341 * @param dataHeight the height to interpret data2 as having 1342 * @param width the width to set this GreasedRegion to have 1343 * @param height the height to set this GreasedRegion to have 1344 */ 1345 public GreasedRegion refill(final long[] data2, final int dataWidth, final int dataHeight, final int width, final int height) 1346 { 1347 if(width != this.width || height != this.height) { 1348 this.width = width; 1349 this.height = height; 1350 ySections = (height + 63) >> 6; 1351 yEndMask = -1L >>> (64 - (height & 63)); 1352 data = new long[width * ySections]; 1353 counts = new int[width * ySections]; 1354 } 1355 else { 1356 Arrays.fill(data, 0L); 1357 } 1358 final int ySections2 = (dataHeight + 63) >> 6; 1359 if(ySections2 == 0) 1360 return this; 1361 if(ySections == 1) { 1362 System.arraycopy(data2, 0, data, 0, Math.min(dataWidth, width)); 1363 } 1364 else 1365 { 1366 if(dataHeight >= height) { 1367 for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) { 1368 System.arraycopy(data2, i, data, j, ySections); 1369 } 1370 } 1371 else 1372 { 1373 for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) { 1374 System.arraycopy(data2, i, data, j, ySections2); 1375 } 1376 } 1377 } 1378 if(ySections > 0 && yEndMask != -1) { 1379 for (int a = ySections - 1; a < data.length; a += ySections) { 1380 data[a] &= yEndMask; 1381 } 1382 } 1383 tallied = false; 1384 return this; 1385 } 1386 1387 /** 1388 * A useful method for efficiency, remake() reassigns this GreasedRegion to have its contents replaced by other. If 1389 * other and this GreasedRegion have identical width and height, this is very efficient and performs no additional 1390 * allocations, simply replacing the cell data in this with the cell data from other. If width and height are not 1391 * both equal between this and other, this does allocate a new data array, but still reassigns this GreasedRegion 1392 * in-place and acts similarly to when width and height are both equal (it just uses some more memory). 1393 * <br> 1394 * Using remake() or the similar refill() methods in chains of operations on multiple GreasedRegions can be key to 1395 * maintaining good performance and memory usage. You often can recycle a no-longer-used GreasedRegion by assigning 1396 * a GreasedRegion you want to keep to it with remake(), then mutating either the remade value or the one that was 1397 * just filled into this but keeping one version around for later usage. 1398 * @param other another GreasedRegion to replace the data in this GreasedRegion with 1399 * @return this for chaining 1400 */ 1401 public GreasedRegion remake(GreasedRegion other) { 1402 if (width == other.width && height == other.height) { 1403 System.arraycopy(other.data, 0, data, 0, width * ySections); 1404 System.arraycopy(other.counts, 0, counts, 0, width * ySections); 1405 ct = other.ct; 1406 tallied = other.tallied; 1407 return this; 1408 } else { 1409 width = other.width; 1410 height = other.height; 1411 ySections = other.ySections; 1412 yEndMask = other.yEndMask; 1413 data = new long[width * ySections]; 1414 counts = new int[width * ySections]; 1415 System.arraycopy(other.data, 0, data, 0, width * ySections); 1416 System.arraycopy(other.counts, 0, counts, 0, width * ySections); 1417 ct = other.ct; 1418 tallied = other.tallied; 1419 return this; 1420 } 1421 } 1422 1423 /** 1424 * Changes the width and/or height of this GreasedRegion, enlarging or shrinking starting at the edges where 1425 * {@code x == width - 1} and {@code y == height - 1}. There isn't an especially efficient way to expand from the 1426 * other edges, but this method is able to copy data in bulk, so at least this method should be very fast. You can 1427 * use {@code insert(int, int, GreasedRegion)} if you want to place one GreasedRegion inside another one, 1428 * potentially with a different size. The space created by any enlargement starts all off; shrinking doesn't change 1429 * the existing data where it isn't removed by the shrink. 1430 * @param widthChange the amount to change width by; can be positive, negative, or zero 1431 * @param heightChange the amount to change height by; can be positive, negative, or zero 1432 * @return this for chaining 1433 */ 1434 public GreasedRegion alterBounds(int widthChange, int heightChange) 1435 { 1436 int newWidth = width + widthChange; 1437 int newHeight = height + heightChange; 1438 if(newWidth <= 0 || newHeight <= 0) 1439 { 1440 width = 0; 1441 height = 0; 1442 ySections= 0; 1443 yEndMask = -1; 1444 data = new long[0]; 1445 counts = new int[0]; 1446 ct = 0; 1447 tallied = true; 1448 return this; 1449 } 1450 int newYSections = (newHeight + 63) >> 6; 1451 yEndMask = -1L >>> (64 - (newHeight & 63)); 1452 long[] newData = new long[newWidth * newYSections]; 1453 counts = new int[newWidth * newYSections]; 1454 1455 for (int x = 0; x < width && x < newWidth; x++) { 1456 for (int ys = 0; ys < ySections && ys < newYSections; ys++) { 1457 newData[x * newYSections + ys] = data[x * ySections + ys]; 1458 } 1459 } 1460 ySections = newYSections; 1461 width = newWidth; 1462 height = newHeight; 1463 data = newData; 1464 if(ySections > 0 && yEndMask != -1) { 1465 for (int a = ySections - 1; a < data.length; a += ySections) { 1466 data[a] &= yEndMask; 1467 } 1468 } 1469 tallied = false; 1470 return this; 1471 } 1472 1473 /** 1474 * Makes a copy of this GreasedRegion that has been rotated 90 degrees {@code turns} times. If using y-down 1475 * coordinates, then these rotations are clockwise; otherwise, they are counter-clockwise. This uses a copy because 1476 * in many caseswhere the GreasedRegion has non-equal width and height, the rotated version has different 1477 * dimensions, and that requires allocating most of a new GreasedRegion anyway. This GreasedRegion is never modifed 1478 * as a result of this method. 1479 * @param turns how many times to rotate the copy (clockwise if using y-down, counterclockwise otherwise) 1480 * @return a potentially-rotated copy of this GreasedRegion 1481 */ 1482 public GreasedRegion copyRotated(int turns) 1483 { 1484 switch (turns & 3) { 1485 case 0: { 1486 return copy(); 1487 } 1488 case 1: { 1489 GreasedRegion next = new GreasedRegion(height, width); 1490 for (int x = 0; x < width; x++) { 1491 for (int y = 0, iy = height - 1; y < height; y++, iy--) { 1492 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0L) 1493 next.data[iy * ySections + (x >> 6)] |= 1L << (x & 63); 1494 } 1495 } 1496 return next; 1497 } 1498 case 2: { 1499 GreasedRegion next = new GreasedRegion(width, height); 1500 for (int x = 0, ix = width - 1; x < width; x++, ix--) { 1501 for (int y = 0, iy = height - 1; y < height; y++, iy--) { 1502 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0L) 1503 next.data[ix * ySections + (iy >> 6)] |= 1L << (iy & 63); 1504 } 1505 } 1506 return next; 1507 } 1508 default: { 1509 GreasedRegion next = new GreasedRegion(height, width); 1510 for (int x = 0, ix = width - 1; x < width; x++, ix--) { 1511 for (int y = 0; y < height; y++) { 1512 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0L) 1513 next.data[y * ySections + (ix >> 6)] |= 1L << (ix & 63); 1514 } 1515 } 1516 return next; 1517 } 1518 } 1519 } 1520 1521 public GreasedRegion flip(boolean leftRight, boolean upDown) { 1522 if(ySections <= 0) return this; 1523 if(leftRight) { 1524 long t; 1525 for (int x = 0, o = width - 1; x < (width >>> 1); x++, o--) { 1526 for (int y = 0; y < ySections; y++) { 1527 t = data[x * ySections + y]; 1528 data[x * ySections + y] = data[o * ySections + y]; 1529 data[o * ySections + y] = t; 1530 } 1531 } 1532 } 1533 if(upDown) { 1534 if(yEndMask == -1L) { 1535 long t; 1536 for (int x = 0; x < width; x++) { 1537 for (int y = 0, o = ySections - 1; y < (ySections >>> 1); y++, o--) { 1538 t = Long.reverse(data[x * ySections + y]); 1539 data[x * ySections + y] = Long.reverse(data[x * ySections + o]); 1540 data[x * ySections + o] = t; 1541 } 1542 if((ySections & 1) == 1){ 1543 data[x * ySections + (ySections >>> 1)] = Long.reverse(data[x * ySections + (ySections >>> 1)]); 1544 } 1545 } 1546 } 1547 else { 1548 int shift = Long.numberOfLeadingZeros(yEndMask); 1549 if (ySections == 1) { 1550 for (int x = 0; x < width; x++) { 1551 data[x] = Long.reverse(data[x]) >>> shift; 1552 } 1553 } else { 1554 for (int x = 0; x < width; x++) { 1555 int ie = x * ySections + ySections - 1; 1556 int ib = x * ySections + ySections - 2; 1557 int il = x * ySections + 1; 1558 int is = x * ySections; 1559 long end = Long.reverse(data[ie]); 1560 long big = Long.reverse(data[ib]); 1561 long little = Long.reverse(data[il]); 1562 long start = Long.reverse(data[is]); 1563 data[ie] = start >>> shift; 1564 data[is] = end >>> shift; 1565 data[is] |= big << 64 - shift; 1566 data[ib] = start << 64 - shift; 1567 data[ib] |= little >>> shift; 1568 1569 for (int y = 1; y < (ySections >>> 1); y++) { 1570 end = big; 1571 start = little; 1572 big = Long.reverse(data[--ib]); 1573 little = Long.reverse(data[++il]); 1574 ++is; 1575 data[is] = end >>> shift; 1576 data[is] |= big << 64 - shift; 1577 data[ib] = start << 64 - shift; 1578 data[ib] |= little >>> shift; 1579 } 1580 } 1581 } 1582 } 1583 } 1584 return this; 1585 } 1586 1587 /** 1588 * Sets the cell at x,y to on if value is true or off if value is false. Does nothing if x,y is out of bounds. 1589 * @param value the value to set in the cell 1590 * @param x the x-position of the cell 1591 * @param y the y-position of the cell 1592 * @return this for chaining 1593 */ 1594 public GreasedRegion set(boolean value, int x, int y) 1595 { 1596 if(x < width && y < height && x >= 0 && y >= 0) { 1597 if(value) 1598 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 1599 else 1600 data[x * ySections + (y >> 6)] &= ~(1L << (y & 63)); 1601 tallied = false; 1602 } 1603 return this; 1604 } 1605 1606 /** 1607 * Sets the cell at point to on if value is true or off if value is false. Does nothing if point is out of bounds, 1608 * or if point is null. 1609 * @param value the value to set in the cell 1610 * @param point the x,y Coord of the cell to set 1611 * @return this for chaining 1612 */ 1613 public GreasedRegion set(boolean value, Coord point) 1614 { 1615 if(point == null) return this; 1616 return set(value, point.x, point.y); 1617 } 1618 1619 /** 1620 * Sets the cell at x,y to "on". Does nothing if x,y is out of bounds. 1621 * More efficient, slightly, than {@link #set(boolean, int, int)} if you just need to set a cell to "on". 1622 * @param x the x-position of the cell 1623 * @param y the y-position of the cell 1624 * @return this for chaining 1625 */ 1626 public GreasedRegion insert(int x, int y) 1627 { 1628 if(x < width && y < height && x >= 0 && y >= 0) 1629 { 1630 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 1631 tallied = false; 1632 } 1633 return this; 1634 } 1635 1636 /** 1637 * Sets the given cell, "tightly" encoded for a specific width/height as by {@link #asTightEncoded()}, to "on". 1638 * Does nothing if the cell is out of bounds. 1639 * @param tight a cell tightly encoded for this GreasedRegion's width and height 1640 * @return this for chaining 1641 */ 1642 public GreasedRegion insert(int tight) 1643 { 1644 if(tight < width * height && tight >= 0) 1645 { 1646 data[(tight % width) * ySections + ((tight / width) >>> 6)] |= 1L << ((tight / width) & 63); 1647 tallied = false; 1648 } 1649 return this; 1650 } 1651 /** 1652 * Sets the cell at point to "on". Does nothing if point is out of bounds, or if point is null. 1653 * More efficient, slightly, than {@link #set(boolean, Coord)} if you just need to set a cell to "on". 1654 * @param point the x,y Coord of the cell 1655 * @return this for chaining 1656 */ 1657 public GreasedRegion insert(Coord point) 1658 { 1659 1660 if(point == null) return this; 1661 return insert(point.x, point.y); 1662 } 1663 1664 /** 1665 * Takes another GreasedRegion, called other, with potentially different size and inserts its "on" cells into thi 1666 * GreasedRegion at the given x,y offset, allowing negative x and/or y to put only part of other in this. 1667 * <br> 1668 * This is a rather complex method internally, but should be about as efficient as a general insert-region method 1669 * can be. 1670 * @param x the x offset to start inserting other at; may be negative 1671 * @param y the y offset to start inserting other at; may be negative 1672 * @param other the other GreasedRegion to insert 1673 * @return this for chaining 1674 */ 1675 public GreasedRegion insert(int x, int y, GreasedRegion other) 1676 { 1677 if(other == null || other.ySections <= 0 || other.width <= 0) 1678 return this; 1679 1680 int start = Math.max(0, x), len = Math.min(width, Math.min(other.width, other.width + x) - start), 1681 oys = other.ySections, jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63), 1682 originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump); 1683 long[] data2 = new long[other.width * ySections]; 1684 1685 long prev, tmp; 1686 if(oys == ySections) { 1687 if (x < 0) { 1688 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1689 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 1690 data2[jj * ySections + i] = other.data[j * oys + oi]; 1691 } 1692 } 1693 } else if (x > 0) { 1694 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1695 for (int j = 0, jj = start; j < len; j++, jj++) { 1696 data2[jj * ySections + i] = other.data[j * ySections + oi]; 1697 } 1698 } 1699 } else { 1700 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1701 for (int j = 0; j < len; j++) { 1702 data2[j * ySections + i] = other.data[j * ySections + oi]; 1703 } 1704 } 1705 } 1706 } 1707 else if(oys < ySections) 1708 { 1709 if (x < 0) { 1710 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1711 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 1712 data2[jj * ySections + i] = other.data[j * oys + oi]; 1713 } 1714 } 1715 } else if (x > 0) { 1716 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {// oi < oys - Math.max(0, jump) 1717 for (int j = 0, jj = start; j < len; j++, jj++) { 1718 data2[jj * ySections + i] = other.data[j * oys + oi]; 1719 } 1720 } 1721 } else { 1722 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1723 for (int j = 0; j < len; j++) { 1724 data2[j * ySections + i] = other.data[j * oys + oi]; 1725 } 1726 } 1727 } 1728 } 1729 else 1730 { 1731 if (x < 0) { 1732 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1733 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 1734 data2[jj * ySections + i] = other.data[j * oys + oi]; 1735 } 1736 } 1737 } else if (x > 0) { 1738 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1739 for (int j = 0, jj = start; j < len; j++, jj++) { 1740 data2[jj * ySections + i] = other.data[j * oys + oi]; 1741 } 1742 } 1743 } else { 1744 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1745 for (int j = 0; j < len; j++) { 1746 data2[j * ySections + i] = other.data[j * oys + oi]; 1747 } 1748 } 1749 } 1750 } 1751 1752 if(lily < 0) { 1753 for (int i = start; i < len; i++) { 1754 prev = 0L; 1755 for (int j = 0; j < ySections; j++) { 1756 tmp = prev; 1757 prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily); 1758 data2[i * ySections + j] >>>= -lily; 1759 data2[i * ySections + j] |= tmp; 1760 } 1761 } 1762 } 1763 else if(lily > 0) { 1764 for (int i = start; i < start + len; i++) { 1765 prev = 0L; 1766 for (int j = 0; j < ySections; j++) { 1767 tmp = prev; 1768 prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily); 1769 data2[i * ySections + j] <<= lily; 1770 data2[i * ySections + j] |= tmp; 1771 } 1772 } 1773 } 1774 len = Math.min(width, start + len); 1775 for (int i = start; i < len; i++) { 1776 for (int j = 0; j < ySections; j++) { 1777 data[i * ySections + j] |= data2[i * ySections + j]; 1778 } 1779 } 1780 1781 if(ySections > 0 && yEndMask != -1) { 1782 for (int a = ySections - 1; a < data.length; a += ySections) { 1783 data[a] &= yEndMask; 1784 } 1785 } 1786 tallied = false; 1787 return this; 1788 } 1789 1790 public GreasedRegion insertSeveral(Coord... points) 1791 { 1792 for (int i = 0, x, y; i < points.length; i++) { 1793 x = points[i].x; 1794 y = points[i].y; 1795 if(x < width && y < height && x >= 0 && y >= 0) 1796 { 1797 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 1798 tallied = false; 1799 } 1800 } 1801 return this; 1802 } 1803 1804 public GreasedRegion insertSeveral(final int[] points) 1805 { 1806 for (int i = 0, tight; i < points.length; i++) { 1807 tight = points[i]; 1808 if(tight < width * height && tight >= 0) 1809 { 1810 data[(tight % width) * ySections + ((tight / width) >>> 6)] |= 1L << ((tight / width) & 63); 1811 tallied = false; 1812 } 1813 } 1814 return this; 1815 } 1816 1817 public GreasedRegion insertSeveral(Iterable<Coord> points) 1818 { 1819 int x, y; 1820 for (Coord pt : points) { 1821 x = pt.x; 1822 y = pt.y; 1823 if(x < width && y < height && x >= 0 && y >= 0) 1824 { 1825 data[x * ySections + (y >> 6)] |= 1L << (y & 63); 1826 tallied = false; 1827 } 1828 } 1829 return this; 1830 } 1831 1832 public GreasedRegion insertRectangle(int startX, int startY, int rectangleWidth, int rectangleHeight) 1833 { 1834 if(rectangleWidth < 1 || rectangleHeight < 1 || ySections <= 0) 1835 return this; 1836 if(startX < 0) 1837 startX = 0; 1838 else if(startX >= width) 1839 startX = width - 1; 1840 if(startY < 0) 1841 startY = 0; 1842 else if(startY >= height) 1843 startY = height - 1; 1844 int endX = Math.min(width, startX + rectangleWidth) - 1, 1845 endY = Math.min(height, startY + rectangleHeight) - 1, 1846 startSection = startY >> 6, endSection = endY >> 6; 1847 if(startSection < endSection) 1848 { 1849 long startMask = -1L << (startY & 63), 1850 endMask = -1L >>> (~endY & 63); 1851 for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) { 1852 data[a] |= startMask; 1853 } 1854 if(endSection - startSection > 1) 1855 { 1856 for (int b = 1; b < endSection - startSection; b++) { 1857 for (int a = startX * ySections + startSection + b; a < endX * ySections + ySections; a += ySections) { 1858 data[a] = -1; 1859 } 1860 } 1861 } 1862 for (int a = startX * ySections + endSection; a <= endX * ySections + endSection; a += ySections) { 1863 data[a] |= endMask; 1864 } 1865 } 1866 else 1867 { 1868 long mask = (-1L << (startY & 63)) & (-1L >>> (~endY & 63)); 1869 for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) { 1870 data[a] |= mask; 1871 } 1872 } 1873 1874 if(yEndMask != -1L) { 1875 for (int a = ySections - 1; a < data.length; a += ySections) { 1876 data[a] &= yEndMask; 1877 } 1878 } 1879 tallied = false; 1880 return this; 1881 } 1882 1883 public GreasedRegion insertCircle(Coord center, int radius) 1884 { 1885 float high, changedX; 1886 int rndX, rndY; 1887 for (int dx = -radius; dx <= radius; ++dx) { 1888 changedX = dx - 0.25f * (dx >> 31 | -dx >>> 31); // project nayuki signum 1889 rndX = Math.round(changedX); 1890 high = (float) Math.sqrt(radius * radius - changedX * changedX); 1891 insert(center.x + rndX, center.y); 1892 for (float dy = high; dy >= 0.75f; --dy) { 1893 rndY = Math.round(dy - 0.25f); 1894 insert(center.x + rndX, center.y + rndY); 1895 insert(center.x + rndX, center.y - rndY); 1896 } 1897 } 1898 return this; 1899 } 1900 1901 public GreasedRegion remove(int x, int y) 1902 { 1903 if(x < width && y < height && x >= 0 && y >= 0) 1904 { 1905 data[x * ySections + (y >> 6)] &= ~(1L << (y & 63)); 1906 tallied = false; 1907 } 1908 return this; 1909 } 1910 public GreasedRegion remove(Coord point) 1911 { 1912 return remove(point.x, point.y); 1913 } 1914 /** 1915 * Takes another GreasedRegion, called other, with potentially different size and removes its "on" cells from this 1916 * GreasedRegion at the given x,y offset, allowing negative x and/or y to remove only part of other in this. 1917 * <br> 1918 * This is a rather complex method internally, but should be about as efficient as a general remove-region method 1919 * can be. The code is identical to {@link #insert(int, int, GreasedRegion)} except that where insert only adds 1920 * cells, this only removes cells. Essentially, insert() is to {@link #or(GreasedRegion)} as remove() is to 1921 * {@link #andNot(GreasedRegion)}. 1922 * @param x the x offset to start removing other from; may be negative 1923 * @param y the y offset to start removing other from; may be negative 1924 * @param other the other GreasedRegion to remove 1925 * @return this for chaining 1926 */ 1927 public GreasedRegion remove(int x, int y, GreasedRegion other) 1928 { 1929 if(other == null || other.ySections <= 0 || other.width <= 0) 1930 return this; 1931 1932 int start = Math.max(0, x), len = Math.min(width, Math.min(other.width, other.width + x) - start), 1933 oys = other.ySections, jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y-1 >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63), 1934 originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump); 1935 long[] data2 = new long[other.width * ySections]; 1936 1937 long prev, tmp; 1938 if(oys == ySections) { 1939 if (x < 0) { 1940 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1941 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 1942 data2[jj * ySections + i] = other.data[j * oys + oi]; 1943 } 1944 } 1945 } else if (x > 0) { 1946 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1947 for (int j = 0, jj = start; j < len; j++, jj++) { 1948 data2[jj * ySections + i] = other.data[j * ySections + oi]; 1949 } 1950 } 1951 } else { 1952 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1953 for (int j = 0; j < len; j++) { 1954 data2[j * ySections + i] = other.data[j * ySections + oi]; 1955 } 1956 } 1957 } 1958 } 1959 else if(oys < ySections) 1960 { 1961 if (x < 0) { 1962 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1963 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 1964 data2[jj * ySections + i] = other.data[j * oys + oi]; 1965 } 1966 } 1967 } else if (x > 0) { 1968 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {// oi < oys - Math.max(0, jump) 1969 for (int j = 0, jj = start; j < len; j++, jj++) { 1970 data2[jj * ySections + i] = other.data[j * oys + oi]; 1971 } 1972 } 1973 } else { 1974 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1975 for (int j = 0; j < len; j++) { 1976 data2[j * ySections + i] = other.data[j * oys + oi]; 1977 } 1978 } 1979 } 1980 } 1981 else 1982 { 1983 if (x < 0) { 1984 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1985 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 1986 data2[jj * ySections + i] = other.data[j * oys + oi]; 1987 } 1988 } 1989 } else if (x > 0) { 1990 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1991 for (int j = 0, jj = start; j < len; j++, jj++) { 1992 data2[jj * ySections + i] = other.data[j * oys + oi]; 1993 } 1994 } 1995 } else { 1996 for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) { 1997 for (int j = 0; j < len; j++) { 1998 data2[j * ySections + i] = other.data[j * oys + oi]; 1999 } 2000 } 2001 } 2002 } 2003 2004 if(lily < 0) { 2005 for (int i = start; i < len; i++) { 2006 prev = 0L; 2007 for (int j = 0; j < ySections; j++) { 2008 tmp = prev; 2009 prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily); 2010 data2[i * ySections + j] >>>= -lily; 2011 data2[i * ySections + j] |= tmp; 2012 } 2013 } 2014 } 2015 else if(lily > 0) { 2016 for (int i = start; i < start + len; i++) { 2017 prev = 0L; 2018 for (int j = 0; j < ySections; j++) { 2019 tmp = prev; 2020 prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily); 2021 data2[i * ySections + j] <<= lily; 2022 data2[i * ySections + j] |= tmp; 2023 } 2024 } 2025 } 2026 len = Math.min(width, start + len); 2027 for (int i = start; i < len; i++) { 2028 for (int j = 0; j < ySections; j++) { 2029 data[i * ySections + j] &= ~data2[i * ySections + j]; 2030 } 2031 } 2032 2033 if(ySections > 0 && yEndMask != -1) { 2034 for (int a = ySections - 1; a < data.length; a += ySections) { 2035 data[a] &= yEndMask; 2036 } 2037 } 2038 tallied = false; 2039 2040 return this; 2041 } 2042 public GreasedRegion removeSeveral(Coord... points) 2043 { 2044 for (int i = 0, x, y; i < points.length; i++) { 2045 x = points[i].x; 2046 y = points[i].y; 2047 if(x < width && y < height && x >= 0 && y >= 0) 2048 { 2049 data[x * ySections + (y >> 6)] &= ~(1L << (y & 63)); 2050 tallied = false; 2051 } 2052 } 2053 return this; 2054 } 2055 2056 public GreasedRegion removeSeveral(Iterable<Coord> points) 2057 { 2058 int x, y; 2059 for (Coord pt : points) { 2060 x = pt.x; 2061 y = pt.y; 2062 if(x < width && y < height && x >= 0 && y >= 0) 2063 { 2064 data[x * ySections + (y >> 6)] &= ~(1L << (y & 63)); 2065 tallied = false; 2066 } 2067 } 2068 return this; 2069 } 2070 2071 /** 2072 * Removes all "on" cells from (startX, startY) inclusive 2073 * to (startX+rectangleWidth, startY+rectangleHeight) exclusive, removing a total width of rectangleWidth and a 2074 * total height of rectangleHeight in cells. 2075 * @param startX left side x-coordinate 2076 * @param startY top side (or bottom if positive y is up) y-coordinate 2077 * @param rectangleWidth how many cells wide the area to remove is 2078 * @param rectangleHeight how many cells tal the area to remove is 2079 * @return this, after modification, for chaining 2080 */ 2081 public GreasedRegion removeRectangle(int startX, int startY, int rectangleWidth, int rectangleHeight) 2082 { 2083 if(startX < 0) 2084 { 2085 rectangleWidth += startX; 2086 startX = 0; 2087 } 2088 else if(startX >= width) 2089 { 2090 rectangleWidth = 1; 2091 startX = width - 1; 2092 } 2093 if(startY < 0) 2094 { 2095 rectangleHeight += startY; 2096 startY = 0; 2097 } 2098 else if(startY >= height) 2099 { 2100 rectangleHeight = 1; 2101 startY = height - 1; 2102 } 2103 if(rectangleWidth < 1 || rectangleHeight < 1 || ySections <= 0) 2104 return this; 2105 int endX = Math.min(width, startX + rectangleWidth) - 1, 2106 endY = Math.min(height, startY + rectangleHeight) - 1, 2107 startSection = startY >> 6, endSection = endY >> 6; 2108 if(startSection < endSection) 2109 { 2110 long startMask = ~(-1L << (startY & 63)), 2111 endMask = ~(-1L >>> (~endY & 63)); 2112 for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) { 2113 data[a] &= startMask; 2114 } 2115 if(endSection - startSection > 1) 2116 { 2117 for (int b = 1; b < endSection - startSection; b++) { 2118 for (int a = startX * ySections + startSection + b; a < endX * ySections + ySections; a += ySections) { 2119 data[a] = 0; 2120 } 2121 } 2122 } 2123 for (int a = startX * ySections + endSection; a <= endX * ySections + endSection; a += ySections) { 2124 data[a] &= endMask; 2125 } 2126 } 2127 else 2128 { 2129 long mask = ~((-1L << (startY & 63)) & (-1L >>> (~endY & 63))); 2130 for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) { 2131 data[a] &= mask; 2132 } 2133 } 2134 tallied = false; 2135 return this; 2136 } 2137 2138 public GreasedRegion removeCircle(Coord center, int radius) 2139 { 2140 float high, changedX; 2141 int rndX, rndY; 2142 for (int dx = -radius; dx <= radius; ++dx) { 2143 changedX = dx - 0.25f * (dx >> 31 | -dx >>> 31); // project nayuki signum 2144 rndX = Math.round(changedX); 2145 high = (float) Math.sqrt(radius * radius - changedX * changedX); 2146 remove(center.x + rndX, center.y); 2147 for (float dy = high; dy >= 0.75f; --dy) { 2148 rndY = Math.round(dy - 0.25f); 2149 remove(center.x + rndX, center.y + rndY); 2150 remove(center.x + rndX, center.y - rndY); 2151 } 2152 } 2153 return this; 2154 } 2155 2156 /** 2157 * Equivalent to {@link #clear()}, setting all cells to "off," but also returns this for chaining. 2158 * @return this for chaining 2159 */ 2160 public GreasedRegion empty() 2161 { 2162 Arrays.fill(data, 0L); 2163 Arrays.fill(counts, 0); 2164 ct = 0; 2165 tallied = true; 2166 return this; 2167 } 2168 2169 /** 2170 * Sets all cells in this to "on." 2171 * @return this for chaining 2172 */ 2173 public GreasedRegion allOn() 2174 { 2175 if(ySections > 0) 2176 { 2177 if(yEndMask == -1) { 2178 Arrays.fill(data, -1); 2179 Arrays.fill(counts, 64); 2180 ct = ySections * width << 6; 2181 tallied = true; 2182 } 2183 else 2184 { 2185 ct = Long.bitCount(yEndMask); 2186 for (int a = ySections - 1; a < data.length; a += ySections) { 2187 data[a] = yEndMask; 2188 counts[a] = ct; 2189 for (int i = 0; i < ySections - 1; i++) { 2190 data[a-i-1] = -1; 2191 counts[a-i-1] = 64; 2192 } 2193 } 2194 ct *= width; 2195 ct += (ySections - 1) * width << 6; 2196 tallied = true; 2197 } 2198 } 2199 return this; 2200 } 2201 2202 /** 2203 * Sets all cells in this to "on" if contents is true, or "off" if contents is false. 2204 * @param contents true to set all cells to on, false to set all cells to off 2205 * @return this for chaining 2206 */ 2207 public GreasedRegion fill(boolean contents) 2208 { 2209 if(contents) 2210 { 2211 if(ySections > 0) 2212 { 2213 if(yEndMask == -1) { 2214 Arrays.fill(data, -1); 2215 Arrays.fill(counts, 64); 2216 ct = ySections * width << 6; 2217 tallied = true; 2218 } 2219 else 2220 { 2221 ct = Long.bitCount(yEndMask); 2222 for (int a = ySections - 1; a < data.length; a += ySections) { 2223 data[a] = yEndMask; 2224 counts[a] = ct; 2225 for (int i = 0; i < ySections - 1; i++) { 2226 data[a-i-1] = -1; 2227 counts[a-i-1] = 64; 2228 } 2229 } 2230 ct *= width; 2231 ct += (ySections - 1) * width << 6; 2232 tallied = true; 2233 } 2234 } 2235 //else... what, if ySections is 0 there's nothing to do 2236 } 2237 else 2238 { 2239 Arrays.fill(data, 0L); 2240 Arrays.fill(counts, 0); 2241 ct = 0; 2242 tallied = true; 2243 } 2244 return this; 2245 } 2246 /** 2247 * Turns all cells that are adjacent to the boundaries of the GreasedRegion to "off". 2248 * @return this for chaining 2249 */ 2250 public GreasedRegion removeEdges() 2251 { 2252 if(ySections > 0) { 2253 for (int i = 0; i < ySections; i++) { 2254 data[i] = 0L; 2255 data[width * ySections - 1 - i] = 0L; 2256 } 2257 if (ySections == 1) { 2258 for (int i = 0; i < width; i++) { 2259 data[i] &= yEndMask >>> 1 & -2L; 2260 } 2261 } else { 2262 for (int i = ySections; i < data.length - ySections; i += ySections) { 2263 data[i] &= -2L; 2264 } 2265 for (int a = ySections * 2 - 1; a < data.length - ySections; a += ySections) { 2266 data[a] &= yEndMask >>> 1; 2267 } 2268 } 2269 tallied = false; 2270 } 2271 return this; 2272 } 2273 2274 /** 2275 * Simple method that returns a newly-allocated copy of this GreasedRegion; modifications to one won't change the 2276 * other, and this method returns the copy while leaving the original unchanged. 2277 * @return a copy of this GreasedRegion; the copy can be changed without altering the original 2278 */ 2279 public GreasedRegion copy() 2280 { 2281 return new GreasedRegion(this); 2282 } 2283 2284 /** 2285 * Returns this GreasedRegion's data as a 2D boolean array, [width][height] in size, with on treated as true and off 2286 * treated as false. 2287 * @return a 2D boolean array that represents this GreasedRegion's data 2288 */ 2289 public boolean[][] decode() 2290 { 2291 boolean[][] bools = new boolean[width][height]; 2292 for (int x = 0; x < width; x++) { 2293 for (int y = 0; y < height; y++) { 2294 bools[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0; 2295 } 2296 } 2297 return bools; 2298 } 2299 2300 /** 2301 * Fills this GreasedRegion's data into the given 2D char array, modifying it and returning it, with "on" cells 2302 * filled with the char parameter {@code on} and "off" cells with the parameter {@code off}. 2303 * @param chars a 2D char array that will be modified; must not be null, nor can it contain null elements 2304 * @param on the char to use for "on" cells 2305 * @param off the char to use for "off" cells 2306 * @return a 2D char array that represents this GreasedRegion's data 2307 */ 2308 public char[][] intoChars(char[][] chars, char on, char off) 2309 { 2310 for (int x = 0; x < width && x < chars.length; x++) { 2311 for (int y = 0; y < height && y < chars[x].length; y++) { 2312 chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? on : off; 2313 } 2314 } 2315 return chars; 2316 } 2317 2318 /** 2319 * Fills this GreasedRegion's data into the given 2D char array, modifying it and returning it, with "on" cells 2320 * filled with the char parameter {@code on} and "off" cells left as-is. 2321 * @param chars a 2D char array that will be modified; must not be null, nor can it contain null elements 2322 * @param on the char to use for "on" cells 2323 * @return a 2D char array that represents the "on" cells in this GreasedRegion's data written over chars 2324 */ 2325 public char[][] intoChars(char[][] chars, char on) 2326 { 2327 for (int x = 0; x < width && x < chars.length; x++) { 2328 for (int y = 0; y < height && y < chars[x].length; y++) { 2329 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0) 2330 chars[x][y] = on; 2331 } 2332 } 2333 return chars; 2334 } 2335 2336 /** 2337 * Returns this GreasedRegion's data as a 2D char array, [width][height] in size, with "on" cells filled with the 2338 * char parameter on and "off" cells with the parameter off. 2339 * @param on the char to use for "on" cells 2340 * @param off the char to use for "off" cells 2341 * @return a 2D char array that represents this GreasedRegion's data 2342 */ 2343 public char[][] toChars(char on, char off) 2344 { 2345 char[][] chars = new char[width][height]; 2346 for (int x = 0; x < width; x++) { 2347 for (int y = 0; y < height; y++) { 2348 chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? on : off; 2349 } 2350 } 2351 return chars; 2352 } 2353 /** 2354 * Returns this GreasedRegion's data as a 2D char array, [width][height] in size, with "on" cells filled with '.' 2355 * and "off" cells with '#'. 2356 * @return a 2D char array that represents this GreasedRegion's data 2357 */ 2358 2359 public char[][] toChars() 2360 { 2361 return toChars('.', '#'); 2362 } 2363 2364 /** 2365 * Returns this GreasedRegion's data as a StringBuilder, with each row made of the parameter on for "on" cells and 2366 * the parameter off for "off" cells, separated by newlines, with no trailing newline at the end. 2367 * @param on the char to use for "on" cells 2368 * @param off the char to use for "off" cells 2369 * @return a StringBuilder that stores each row of this GreasedRegion as chars, with rows separated by newlines. 2370 */ 2371 public StringBuilder show(char on, char off) 2372 { 2373 StringBuilder sb = new StringBuilder((width+1) * height); 2374 for (int y = 0; y < height;) { 2375 for (int x = 0; x < width; x++) { 2376 sb.append((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? on : off); 2377 } 2378 if(++y < height) 2379 sb.append('\n'); 2380 } 2381 return sb; 2382 } 2383 2384 /** 2385 * Returns a legible String representation of this that can be printed over multiple lines, with all "on" cells 2386 * represented by '.' and all "off" cells by '#', in roguelike floors-on walls-off convention, separating each row 2387 * by newlines (without a final trailing newline, so you could append text right after this). 2388 * @return a String representation of this GreasedRegion using '.' for on, '#' for off, and newlines between rows 2389 */ 2390 @Override 2391 public String toString() 2392 { 2393 return show('.', '#').toString(); 2394 } 2395 2396 /** 2397 * Returns a copy of map where if a cell is "on" in this GreasedRegion, this keeps the value in map intact, 2398 * and where a cell is "off", it instead writes the char filler. 2399 * @param map a 2D char array that will not be modified 2400 * @param filler the char to use where this GreasedRegion stores an "off" cell 2401 * @return a masked copy of map 2402 */ 2403 public char[][] mask(char[][] map, char filler) 2404 { 2405 if(map == null || map.length == 0) 2406 return new char[0][0]; 2407 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2408 char[][] chars = new char[width2][height2]; 2409 for (int x = 0; x < width2; x++) { 2410 for (int y = 0; y < height2; y++) { 2411 chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? map[x][y] : filler; 2412 } 2413 } 2414 return chars; 2415 } 2416 2417 /** 2418 * Returns a copy of map where if a cell is "on" in this GreasedRegion, this keeps the value in map intact, 2419 * and where a cell is "off", it instead writes the short filler. Meant for use with MultiSpill, but may be 2420 * used anywhere you have a 2D short array. {@link #mask(char[][], char)} is more likely to be useful. 2421 * @param map a 2D short array that will not be modified 2422 * @param filler the short to use where this GreasedRegion stores an "off" cell 2423 * @return a masked copy of map 2424 */ 2425 public short[][] mask(short[][] map, short filler) 2426 { 2427 if(map == null || map.length == 0) 2428 return new short[0][0]; 2429 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2430 short[][] shorts = new short[width2][height2]; 2431 for (int x = 0; x < width2; x++) { 2432 for (int y = 0; y < height2; y++) { 2433 shorts[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? map[x][y] : filler; 2434 } 2435 } 2436 return shorts; 2437 } 2438 2439 /** 2440 * Returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps the value in map intact, 2441 * and where a cell is "on", it instead writes the char toWrite. 2442 * @param map a 2D char array that will not be modified 2443 * @param toWrite the char to use where this GreasedRegion stores an "on" cell 2444 * @return a masked copy of map 2445 */ 2446 public char[][] inverseMask(char[][] map, char toWrite) 2447 { 2448 if(map == null || map.length == 0) 2449 return new char[0][0]; 2450 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2451 char[][] chars = new char[width2][height2]; 2452 for (int x = 0; x < width2; x++) { 2453 for (int y = 0; y < height2; y++) { 2454 chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? toWrite : map[x][y]; 2455 } 2456 } 2457 return chars; 2458 } 2459 2460 /** 2461 * "Inverse mask for ints;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps 2462 * the value in map intact, and where a cell is "on", it instead writes the int toWrite. 2463 * @param map a 2D int array that will not be modified 2464 * @param toWrite the int to use where this GreasedRegion stores an "on" cell 2465 * @return an altered copy of map 2466 */ 2467 public int[][] writeInts(int[][] map, int toWrite) 2468 { 2469 if(map == null || map.length == 0) 2470 return new int[0][0]; 2471 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2472 int[][] ints = new int[width2][height2]; 2473 for (int x = 0; x < width2; x++) { 2474 for (int y = 0; y < height2; y++) { 2475 ints[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? toWrite : map[x][y]; 2476 } 2477 } 2478 return ints; 2479 } 2480 2481 /** 2482 * "Inverse mask for ints;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps 2483 * the value in map intact, and where a cell is "on", it instead writes the int toWrite. Modifies map in-place, 2484 * unlike {@link #writeInts(int[][], int)}. 2485 * @param map a 2D int array that <b>will</b> be modified 2486 * @param toWrite the int to use where this GreasedRegion stores an "on" cell 2487 * @return map, with the changes applied; not a copy 2488 */ 2489 public int[][] writeIntsInto(int[][] map, int toWrite) 2490 { 2491 if(map == null || map.length == 0) 2492 return map; 2493 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2494 for (int x = 0; x < width2; x++) { 2495 for (int y = 0; y < height2; y++) { 2496 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0) 2497 map[x][y] = toWrite; 2498 } 2499 } 2500 return map; 2501 } 2502 /** 2503 * "Inverse mask for doubles;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps 2504 * the value in map intact, and where a cell is "on", it instead writes the double toWrite. 2505 * @param map a 2D double array that will not be modified 2506 * @param toWrite the double to use where this GreasedRegion stores an "on" cell 2507 * @return an altered copy of map 2508 */ 2509 public double[][] writeDoubles(double[][] map, double toWrite) 2510 { 2511 if(map == null || map.length == 0) 2512 return new double[0][0]; 2513 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2514 double[][] doubles = new double[width2][height2]; 2515 for (int x = 0; x < width2; x++) { 2516 for (int y = 0; y < height2; y++) { 2517 doubles[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? toWrite : map[x][y]; 2518 } 2519 } 2520 return doubles; 2521 } 2522 2523 /** 2524 * "Inverse mask for doubles;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps 2525 * the value in map intact, and where a cell is "on", it instead writes the double toWrite. Modifies map in-place, 2526 * unlike {@link #writeDoubles(double[][], double)}. 2527 * @param map a 2D double array that <b>will</b> be modified 2528 * @param toWrite the double to use where this GreasedRegion stores an "on" cell 2529 * @return map, with the changes applied; not a copy 2530 */ 2531 public double[][] writeDoublesInto(double[][] map, double toWrite) 2532 { 2533 if(map == null || map.length == 0) 2534 return map; 2535 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2536 for (int x = 0; x < width2; x++) { 2537 for (int y = 0; y < height2; y++) { 2538 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0) 2539 map[x][y] = toWrite; 2540 } 2541 } 2542 return map; 2543 } 2544 /** 2545 * Like {@link #inverseMask(char[][], char)}, but modifies {@code map} in-place and returns it. If a cell is "off" 2546 * in this GreasedRegion, this keeps the value in map intact, and where a cell is "on", it instead writes the char 2547 * toWrite. Modifies map in-place, unlike {@link #inverseMask(char[][], char)}. 2548 * @param map a 2D char array that <b>will</b> be modified 2549 * @param toWrite the char to use where this GreasedRegion stores an "on" cell 2550 * @return map, with the changes applied; not a copy 2551 */ 2552 public char[][] writeCharsInto(char[][] map, char toWrite) 2553 { 2554 if(map == null || map.length == 0) 2555 return map; 2556 int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length); 2557 for (int x = 0; x < width2; x++) { 2558 for (int y = 0; y < height2; y++) { 2559 if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0) 2560 map[x][y] = toWrite; 2561 } 2562 } 2563 return map; 2564 } 2565 2566 /** 2567 * Union of two GreasedRegions, assigning the result into this GreasedRegion. Any cell that is "on" in either 2568 * GreasedRegion will be made "on" in this GreasedRegion. 2569 * @param other another GreasedRegion that will not be modified 2570 * @return this, after modification, for chaining 2571 */ 2572 public GreasedRegion or(GreasedRegion other) 2573 { 2574 for (int x = 0; x < width && x < other.width; x++) { 2575 for (int y = 0; y < ySections && y < other.ySections; y++) { 2576 data[x * ySections + y] |= other.data[x * other.ySections + y]; 2577 } 2578 } 2579 2580 if(ySections > 0 && yEndMask != -1) { 2581 for (int a = ySections - 1; a < data.length; a += ySections) { 2582 data[a] &= yEndMask; 2583 } 2584 } 2585 tallied = false; 2586 2587 return this; 2588 } 2589 2590 /** 2591 * Intersection of two GreasedRegions, assigning the result into this GreasedRegion. Any cell that is "on" in both 2592 * GreasedRegions will be kept "on" in this GreasedRegion, but all other cells will be made "off." 2593 * @param other another GreasedRegion that will not be modified 2594 * @return this, after modification, for chaining 2595 */ 2596 public GreasedRegion and(GreasedRegion other) 2597 { 2598 for (int x = 0; x < width && x < other.width; x++) { 2599 for (int y = 0; y < ySections && y < other.ySections; y++) { 2600 data[x * ySections + y] &= other.data[x * other.ySections + y]; 2601 } 2602 } 2603 tallied = false; 2604 return this; 2605 } 2606 2607 /** 2608 * Intersection of two GreasedRegions, assigning the result into this GreasedRegion, with the special requirement 2609 * that other must be a 64x64 area, and the special property that other will be considered tiled to cover all of the 2610 * area of this GreasedRegion. Any cell that is "on" in both GreasedRegions (treating other as tiling) will be kept 2611 * "on" in this GreasedRegion, but all other cells will be made "off." 2612 * @param other another GreasedRegion that will not be modified but must be 64x64 in size; will act as if it tiles 2613 * @return this, after modification, for chaining 2614 */ 2615 public GreasedRegion andWrapping64(GreasedRegion other) 2616 { 2617 for (int x = 0; x < width; x++) { 2618 for (int y = 0; y < ySections; y++) { 2619 data[x * ySections + y] &= other.data[x & 63]; 2620 } 2621 } 2622 tallied = false; 2623 return this; 2624 } 2625 /** 2626 * Difference of two GreasedRegions, assigning the result into this GreasedRegion. Any cell that is "on" in this 2627 * GreasedRegion and "off" in other will be kept "on" in this GreasedRegion, but all other cells will be made "off." 2628 * @param other another GreasedRegion that will not be modified 2629 * @return this, after modification, for chaining 2630 * @see #notAnd(GreasedRegion) notAnd is a very similar method that acts sort-of in reverse of this method 2631 */ 2632 public GreasedRegion andNot(GreasedRegion other) 2633 { 2634 for (int x = 0; x < width && x < other.width; x++) { 2635 for (int y = 0; y < ySections && y < other.ySections; y++) { 2636 data[x * ySections + y] &= ~other.data[x * other.ySections + y]; 2637 } 2638 } 2639 tallied = false; 2640 return this; 2641 } 2642 2643 /** 2644 * Like andNot, but subtracts this GreasedRegion from other and stores the result in this GreasedRegion, without 2645 * mutating other. 2646 * @param other another GreasedRegion that will not be modified 2647 * @return this, after modification, for chaining 2648 * @see #andNot(GreasedRegion) andNot is a very similar method that acts sort-of in reverse of this method 2649 */ 2650 public GreasedRegion notAnd(GreasedRegion other) 2651 { 2652 for (int x = 0; x < width && x < other.width; x++) { 2653 for (int y = 0; y < ySections && y < other.ySections; y++) { 2654 data[x * ySections + y] = other.data[x * other.ySections + y] & ~data[x * ySections + y]; 2655 } 2656 } 2657 tallied = false; 2658 return this; 2659 } 2660 2661 /** 2662 * Symmetric difference (more commonly known as exclusive or, hence the name) of two GreasedRegions, assigning the 2663 * result into this GreasedRegion. Any cell that is "on" in this and "off" in other, or "off" in this and "on" in 2664 * other, will be made "on" in this; all other cells will be made "off." Useful to find cells that are "on" in 2665 * exactly one of two GreasedRegions (not "on" in both, or "off" in both). 2666 * @param other another GreasedRegion that will not be modified 2667 * @return this, after modification, for chaining 2668 */ 2669 public GreasedRegion xor(GreasedRegion other) 2670 { 2671 for (int x = 0; x < width && x < other.width; x++) { 2672 for (int y = 0; y < ySections && y < other.ySections; y++) { 2673 data[x * ySections + y] ^= other.data[x * other.ySections + y]; 2674 } 2675 } 2676 2677 if(ySections > 0 && yEndMask != -1) { 2678 for (int a = ySections - 1; a < data.length; a += ySections) { 2679 data[a] &= yEndMask; 2680 } 2681 } 2682 tallied = false; 2683 return this; 2684 } 2685 2686 /** 2687 * Negates this GreasedRegion, turning "on" to "off" and "off" to "on." 2688 * @return this, after modification, for chaining 2689 */ 2690 public GreasedRegion not() 2691 { 2692 for (int a = 0; a < data.length; a++) 2693 { 2694 data[a] = ~data[a]; 2695 } 2696 2697 if(ySections > 0 && yEndMask != -1) { 2698 for (int a = ySections - 1; a < data.length; a += ySections) { 2699 data[a] &= yEndMask; 2700 } 2701 } 2702 tallied = false; 2703 return this; 2704 } 2705 2706 /** 2707 * Moves the "on" cells in this GreasedRegion to the given x and y offset, removing cells that move out of bounds. 2708 * @param x the x offset to translate by; can be negative 2709 * @param y the y offset to translate by; can be negative 2710 * @return this for chaining 2711 */ 2712 public GreasedRegion translate(int x, int y) { 2713 GreasedRegion result = this; 2714 if (width < 1 || ySections <= 0 || (x == 0 && y == 0)) { 2715 } else { 2716 int start = Math.max(0, x), len = Math.min(width, width + x) - start, 2717 jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63), 2718 originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump); 2719 long[] data2 = new long[width * ySections]; 2720 long prev, tmp; 2721 if (x < 0) { 2722 for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) { 2723 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 2724 data2[jj * ySections + i] = data[j * ySections + oi]; 2725 } 2726 } 2727 } else if (x > 0) { 2728 for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) { 2729 for (int j = 0, jj = start; j < len; j++, jj++) { 2730 data2[jj * ySections + i] = data[j * ySections + oi]; 2731 } 2732 } 2733 } else { 2734 for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) { 2735 for (int j = 0; j < len; j++) { 2736 data2[j * ySections + i] = data[j * ySections + oi]; 2737 } 2738 } 2739 } 2740 if (lily < 0) { 2741 for (int i = start; i < len; i++) { 2742 prev = 0L; 2743 for (int j = 0; j < ySections; j++) { 2744 tmp = prev; 2745 prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily); 2746 data2[i * ySections + j] >>>= -lily; 2747 data2[i * ySections + j] |= tmp; 2748 } 2749 } 2750 } else if (lily > 0) { 2751 for (int i = start; i < start + len; i++) { 2752 prev = 0L; 2753 for (int j = 0; j < ySections; j++) { 2754 tmp = prev; 2755 prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily); 2756 data2[i * ySections + j] <<= lily; 2757 data2[i * ySections + j] |= tmp; 2758 } 2759 } 2760 } 2761 if (yEndMask != -1) { 2762 for (int a = ySections - 1; a < data2.length; a += ySections) { 2763 data2[a] &= yEndMask; 2764 } 2765 } 2766 data = data2; 2767 tallied = false; 2768 } 2769 2770 2771 return result; 2772 } 2773 2774 /** 2775 * Adds to this GreasedRegion with a moved set of its own "on" cells, moved to the given x and y offset. 2776 * Ignores cells that would be added out of bounds. Keeps all cells that are currently "on" unchanged. 2777 * @param x the x offset to translate by; can be negative 2778 * @param y the y offset to translate by; can be negative 2779 * @return this for chaining 2780 */ 2781 public GreasedRegion insertTranslation(int x, int y) { 2782 GreasedRegion result = this; 2783 if (width < 1 || ySections <= 0 || (x == 0 && y == 0)) { 2784 } else { 2785 int start = Math.max(0, x), len = Math.min(width, width + x) - start, 2786 jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63), 2787 originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump); 2788 long[] data2 = new long[width * ySections]; 2789 long prev, tmp; 2790 if (x < 0) { 2791 for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) { 2792 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 2793 data2[jj * ySections + i] = data[j * ySections + oi]; 2794 } 2795 } 2796 } else if (x > 0) { 2797 for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) { 2798 for (int j = 0, jj = start; j < len; j++, jj++) { 2799 data2[jj * ySections + i] = data[j * ySections + oi]; 2800 } 2801 } 2802 } else { 2803 for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) { 2804 for (int j = 0; j < len; j++) { 2805 data2[j * ySections + i] = data[j * ySections + oi]; 2806 } 2807 } 2808 } 2809 if (lily < 0) { 2810 for (int i = start; i < len; i++) { 2811 prev = 0L; 2812 for (int j = 0; j < ySections; j++) { 2813 tmp = prev; 2814 prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily); 2815 data2[i * ySections + j] >>>= -lily; 2816 data2[i * ySections + j] |= tmp; 2817 } 2818 } 2819 } else if (lily > 0) { 2820 for (int i = start; i < start + len; i++) { 2821 prev = 0L; 2822 for (int j = 0; j < ySections; j++) { 2823 tmp = prev; 2824 prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily); 2825 data2[i * ySections + j] <<= lily; 2826 data2[i * ySections + j] |= tmp; 2827 } 2828 } 2829 } 2830 for (int i = 0; i < width * ySections; i++) { 2831 data2[i] |= data[i]; 2832 } 2833 if (yEndMask != -1) { 2834 for (int a = ySections - 1; a < data2.length; a += ySections) { 2835 data2[a] &= yEndMask; 2836 } 2837 } 2838 data = data2; 2839 tallied = false; 2840 } 2841 2842 2843 return result; 2844 } 2845 2846 /** 2847 * Effectively doubles the x and y values of each cell this contains (not scaling each cell to be larger, so each 2848 * "on" cell will be surrounded by "off" cells), and re-maps the positions so the given x and y in the doubled space 2849 * become 0,0 in the resulting GreasedRegion (which is this, assigning to itself). 2850 * @param x in the doubled coordinate space, the x position that should become 0 x in the result; can be negative 2851 * @param y in the doubled coordinate space, the y position that should become 0 y in the result; can be negative 2852 * @return this for chaining 2853 */ 2854 public GreasedRegion zoom(int x, int y) { 2855 GreasedRegion result = this; 2856 if (width < 1 || ySections <= 0) { 2857 } else { 2858 x = -x; 2859 y = -y; 2860 int 2861 width2 = width + 1 >>> 1, ySections2 = ySections + 1 >>> 1, 2862 start = Math.max(0, x), len = Math.min(width, width + x) - start, 2863 //tall = (Math.min(height, height + y) - Math.max(0, y)) + 63 >> 6, 2864 jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63), 2865 originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump), 2866 oddX = (x & 1), oddY = (y & 1); 2867 long[] data2 = new long[width * ySections]; 2868 long prev, tmp, yEndMask2 = -1L >>> (64 - ((height + 1 >>> 1) & 63)); 2869 if (x < 0) { 2870 for (int i = alterJump, oi = originalJump; i <= ySections2 && oi < ySections; i++, oi++) { 2871 for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) { 2872 data2[jj * ySections + i] = data[j * ySections + oi]; 2873 } 2874 } 2875 } else if (x > 0) { 2876 for (int i = alterJump, oi = originalJump; i <= ySections2 && oi < ySections; i++, oi++) { 2877 for (int j = 0, jj = start; j < len; j++, jj++) { 2878 data2[jj * ySections + i] = data[j * ySections + oi]; 2879 } 2880 } 2881 } else { 2882 for (int i = alterJump, oi = originalJump; i <= ySections2 && oi < ySections; i++, oi++) { 2883 for (int j = 0; j < len; j++) { 2884 data2[j * ySections + i] = data[j * ySections + oi]; 2885 } 2886 } 2887 } 2888 if (lily < 0) { 2889 for (int i = start; i < len; i++) { 2890 prev = 0L; 2891 for (int j = ySections2; j >= 0; j--) { 2892 tmp = prev; 2893 prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily); 2894 data2[i * ySections + j] >>>= -lily; 2895 data2[i * ySections + j] |= tmp; 2896 } 2897 } 2898 } else if (lily > 0) { 2899 for (int i = start; i < start + len; i++) { 2900 prev = 0L; 2901 for (int j = 0; j < ySections2; j++) { 2902 tmp = prev; 2903 prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily); 2904 data2[i * ySections + j] <<= lily; 2905 data2[i * ySections + j] |= tmp; 2906 } 2907 } 2908 } 2909 if (yEndMask2 != -1) { 2910 for (int a = ySections2 - 1; a < data2.length; a += ySections) { 2911 data2[a] &= yEndMask2; 2912 if (ySections2 < ySections) 2913 data2[a + 1] = 0L; 2914 } 2915 } 2916 for (int i = 0; i < width2; i++) { 2917 for (int j = 0; j < ySections2; j++) { 2918 prev = data2[i * ySections + j]; 2919 tmp = prev >>> 32; 2920 prev &= 0xFFFFFFFFL; 2921 prev = (prev | (prev << 16)) & 0x0000FFFF0000FFFFL; 2922 prev = (prev | (prev << 8)) & 0x00FF00FF00FF00FFL; 2923 prev = (prev | (prev << 4)) & 0x0F0F0F0F0F0F0F0FL; 2924 prev = (prev | (prev << 2)) & 0x3333333333333333L; 2925 prev = (prev | (prev << 1)) & 0x5555555555555555L; 2926 prev <<= oddY; 2927 if (oddX == 1) { 2928 if (i * 2 + 1 < width) 2929 data[(i * ySections + j) * 2 + ySections] = prev; 2930 if (i * 2 < width) 2931 data[(i * ySections + j) * 2] = 0L; 2932 } else { 2933 if (i * 2 < width) 2934 data[(i * ySections + j) * 2] = prev; 2935 if (i * 2 + 1 < width) 2936 data[(i * ySections + j) * 2 + ySections] = 0L; 2937 } 2938 if (j * 2 + 1 < ySections) { 2939 tmp = (tmp | (tmp << 16)) & 0x0000FFFF0000FFFFL; 2940 tmp = (tmp | (tmp << 8)) & 0x00FF00FF00FF00FFL; 2941 tmp = (tmp | (tmp << 4)) & 0x0F0F0F0F0F0F0F0FL; 2942 tmp = (tmp | (tmp << 2)) & 0x3333333333333333L; 2943 tmp = (tmp | (tmp << 1)) & 0x5555555555555555L; 2944 tmp <<= oddY; 2945 if (oddX == 1) { 2946 if (i * 2 + 1 < width) 2947 data[(i * ySections + j) * 2 + ySections + 1] = tmp; 2948 if (i * 2 < width) 2949 data[(i * ySections + j) * 2 + 1] = 0L; 2950 } else { 2951 if (i * 2 < width) 2952 data[(i * ySections + j) * 2 + 1] = tmp; 2953 if (i * 2 + 1 < width) 2954 data[(i * ySections + j) * 2 + ySections + 1] = 0L; 2955 } 2956 } 2957 } 2958 } 2959 if (yEndMask != -1) { 2960 for (int a = ySections - 1; a < data.length; a += ySections) { 2961 data[a] &= yEndMask; 2962 } 2963 } 2964 tallied = false; 2965 } 2966 2967 2968 return result; 2969 } 2970 2971 /** 2972 * Takes the pairs of "on" cells in this GreasedRegion that are separated by exactly one cell in an orthogonal line, 2973 * and changes the gap cells to "on" as well. 2974 * <br> 2975 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 2976 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 2977 * very well by operating in bulk on up to 64 cells at a time. 2978 * @return this for chaining 2979 */ 2980 public GreasedRegion connect() { 2981 GreasedRegion result = this; 2982 if (width < 2 || ySections == 0) { 2983 } else { 2984 final long[] next = new long[width * ySections]; 2985 System.arraycopy(data, 0, next, 0, width * ySections); 2986 for (int a = 0; a < ySections; a++) { 2987 next[a] |= ((data[a] << 1) & (data[a] >>> 1)) | data[a + ySections]; 2988 next[(width - 1) * ySections + a] |= ((data[(width - 1) * ySections + a] << 1) & (data[(width - 1) * ySections + a] >>> 1)) | data[(width - 2) * ySections + a]; 2989 2990 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 2991 next[i] |= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections]); 2992 } 2993 2994 if (a > 0) { 2995 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 2996 next[i] |= (data[i - 1] & 0x8000000000000000L) >>> 63 & (data[i] >>> 1); 2997 } 2998 } else { 2999 for (int i = ySections; i < (width - 1) * ySections; i += ySections) { 3000 next[i] |= (data[i] >>> 1 & 1L); 3001 } 3002 } 3003 3004 if (a < ySections - 1) { 3005 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3006 next[i] |= (data[i + 1] & 1L) << 63 & (data[i] << 1); 3007 } 3008 } else { 3009 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3010 next[i] |= (data[i] << 1 & 0x8000000000000000L); 3011 } 3012 3013 } 3014 } 3015 if (ySections > 0 && yEndMask != -1) { 3016 for (int a = ySections - 1; a < next.length; a += ySections) { 3017 next[a] &= yEndMask; 3018 } 3019 } 3020 data = next; 3021 tallied = false; 3022 } 3023 3024 return result; 3025 } 3026 3027 /** 3028 * Takes the pairs of "on" cells in this GreasedRegion that are separated by exactly one cell in an orthogonal or 3029 * diagonal line, and changes the gap cells to "on" as well. 3030 * <br> 3031 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3032 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3033 * very well by operating in bulk on up to 64 cells at a time. 3034 * @return this for chaining 3035 */ 3036 public GreasedRegion connect8way() { 3037 GreasedRegion result = this; 3038 if (width < 2 || ySections == 0) { 3039 } else { 3040 final long[] next = new long[width * ySections]; 3041 System.arraycopy(data, 0, next, 0, width * ySections); 3042 for (int a = 0; a < ySections; a++) { 3043 next[a] |= ((data[a] << 1) & (data[a] >>> 1)) | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1); 3044 next[(width - 1) * ySections + a] |= ((data[(width - 1) * ySections + a] << 1) & (data[(width - 1) * ySections + a] >>> 1)) 3045 | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1); 3046 3047 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3048 next[i] |= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections]) 3049 | ((data[i - ySections] << 1) & (data[i + ySections] >>> 1)) 3050 | ((data[i + ySections] << 1) & (data[i - ySections] >>> 1)); 3051 } 3052 3053 if (a > 0) { 3054 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3055 next[i] |= ((data[i - 1] & 0x8000000000000000L) >>> 63 & (data[i] >>> 1)) | 3056 ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i + ySections] >>> 1)) | 3057 ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i - ySections] >>> 1)); 3058 } 3059 } else { 3060 for (int i = ySections; i < (width - 1) * ySections; i += ySections) { 3061 next[i] |= (data[i] >>> 1 & 1L) | (data[i - ySections] >>> 1 & 1L) | (data[i + ySections] >>> 1 & 1L); 3062 } 3063 } 3064 3065 if (a < ySections - 1) { 3066 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3067 next[i] |= ((data[i + 1] & 1L) << 63 & (data[i] << 1)) | 3068 ((data[i - ySections + 1] & 1L) << 63 & (data[i + ySections] << 1)) | 3069 ((data[i + ySections + 1] & 1L) << 63 & (data[i - ySections] << 1)); 3070 } 3071 } else { 3072 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3073 next[i] |= (data[i] << 1 & 0x8000000000000000L) 3074 | (data[i - ySections] << 1 & 0x8000000000000000L) | (data[i + ySections] << 1 & 0x8000000000000000L); 3075 } 3076 3077 } 3078 } 3079 if (ySections > 0 && yEndMask != -1) { 3080 for (int a = ySections - 1; a < next.length; a += ySections) { 3081 next[a] &= yEndMask; 3082 } 3083 } 3084 data = next; 3085 tallied = false; 3086 } 3087 3088 return result; 3089 } 3090 /** 3091 * Takes the pairs of "on" cells in this GreasedRegion that are separated by exactly one cell in an orthogonal or 3092 * diagonal line, and changes the gap cells to "on" as well. As a special case, this requires diagonals to either 3093 * have no "on" cells adjacent along the perpendicular diagonal, or both cells on that perpendicular diagonal need 3094 * to be "on." This is useful to counteract some less-desirable behavior of {@link #connect8way()}, where a right 3095 * angle would always get the inner corners filled because it was considered a diagonal. 3096 * <br> 3097 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3098 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3099 * very well by operating in bulk on up to 64 cells at a time. 3100 * @return this for chaining 3101 */ 3102 public GreasedRegion connectLines() { 3103 GreasedRegion result = this; 3104 if (width < 2 || ySections == 0) { 3105 } else { 3106 final long[] next = new long[width * ySections]; 3107 System.arraycopy(data, 0, next, 0, width * ySections); 3108 for (int a = 0; a < ySections; a++) { 3109 next[a] |= ((data[a] << 1) & (data[a] >>> 1)) | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1); 3110 next[(width - 1) * ySections + a] |= ((data[(width - 1) * ySections + a] << 1) & (data[(width - 1) * ySections + a] >>> 1)) 3111 | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1); 3112 3113 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3114 next[i] |= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections]) 3115 | (((data[i - ySections] << 1) & (data[i + ySections] >>> 1)) 3116 ^ ((data[i + ySections] << 1) & (data[i - ySections] >>> 1))); 3117 } 3118 3119 if (a > 0) { 3120 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3121 next[i] |= ((data[i - 1] & 0x8000000000000000L) >>> 63 & (data[i] >>> 1)) 3122 | (((data[i - ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i + ySections] >>> 1)) 3123 ^ ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i - ySections] >>> 1))); 3124 } 3125 } else { 3126 for (int i = ySections; i < (width - 1) * ySections; i += ySections) { 3127 next[i] |= (data[i] >>> 1 & 1L) | (data[i - ySections] >>> 1 & 1L) | (data[i + ySections] >>> 1 & 1L); 3128 } 3129 } 3130 3131 if (a < ySections - 1) { 3132 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3133 next[i] |= ((data[i + 1] & 1L) << 63 & (data[i] << 1)) 3134 | (((data[i - ySections + 1] & 1L) << 63 & (data[i + ySections] << 1)) 3135 ^ ((data[i + ySections + 1] & 1L) << 63 & (data[i - ySections] << 1))); 3136 } 3137 } else { 3138 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3139 next[i] |= (data[i] << 1 & 0x8000000000000000L) 3140 | (data[i - ySections] << 1 & 0x8000000000000000L) | (data[i + ySections] << 1 & 0x8000000000000000L); 3141 } 3142 3143 } 3144 } 3145 if (ySections > 0 && yEndMask != -1) { 3146 for (int a = ySections - 1; a < next.length; a += ySections) { 3147 next[a] &= yEndMask; 3148 } 3149 } 3150 data = next; 3151 tallied = false; 3152 } 3153 3154 return result; 3155 } 3156 3157 /** 3158 * Like {@link #retract()}, this reduces the width of thick areas of this GreasedRegion, but thin() will not remove 3159 * areas that would be identical in a subsequent call to retract(), such as if the area would be eliminated. This 3160 * is useful primarily for adjusting areas so they do not exceed a width of 2 cells, though their length (the longer 3161 * of the two dimensions) will be unaffected by this. Especially wide, irregularly-shaped areas may have unintended 3162 * appearances if you call this repeatedly or use {@link #thinFully()}; consider using this sparingly, or primarily 3163 * when an area has just gotten thicker than desired. 3164 * <br> 3165 * This currently uses 4-way adjacency, but had previously used 8-way; if you want the behavior this previously had, 3166 * you can use {@link #thin8way()}, but it may be a good idea to try this method as well (some of the old behavior 3167 * had problems where it yielded significantly larger minimum widths in some areas). 3168 * @return this for chaining 3169 */ 3170 public GreasedRegion thin() { 3171 GreasedRegion result = this; 3172 if (width <= 2 || ySections <= 0) { 3173 } else { 3174 GreasedRegion c1 = new GreasedRegion(this).retract(), 3175 c2 = new GreasedRegion(c1).expand().xor(this).expand().and(this); 3176 remake(c1).or(c2);/* 3177 System.out.println("\n\nc1:\n" + c1.toString() + "\n"); 3178 System.out.println("\n\nc2:\n" + c2.toString() + "\n"); 3179 System.out.println("\n\nthis:\n" + toString() + "\n"); 3180 */ 3181 } 3182 return result; 3183 } 3184 3185 /** 3186 * Calls {@link #thin()} repeatedly, until the result is unchanged from the last call. Consider using the idiom 3187 * {@code expand8way().retract().thinFully()} to help change a possibly-strange appearance when the GreasedRegion 3188 * this is called on touches the edges of the grid. In general, this method is likely to go too far when it tries to 3189 * thin a round or irregular area, and this often results in many diagonal lines spanning the formerly-thick area. 3190 * <br> 3191 * This currently uses 4-way adjacency, but had previously used 8-way; if you want the behavior this previously had, 3192 * you can use {@link #thinFully8way()}, but it may be a good idea to try this method as well (some of the old 3193 * behavior had problems where it yielded significantly larger minimum widths in some areas). 3194 * @return this for chaining 3195 */ 3196 public GreasedRegion thinFully() 3197 { 3198 while (size() != thin().size()); 3199 return this; 3200 } 3201 3202 3203 /** 3204 * Like {@link #retract8way()}, this reduces the width of thick areas of this GreasedRegion, but thin8way() will not 3205 * remove areas that would be identical in a subsequent call to retract8way(), such as if the area would be 3206 * eliminated. This is useful primarily for adjusting areas so they do not exceed a width of 2 cells, though their 3207 * length (the longer of the two dimensions) will be unaffected by this. Especially wide, irregularly-shaped areas 3208 * may have unintended appearances if you call this repeatedly or use {@link #thinFully8way()}; consider using this 3209 * sparingly, or primarily when an area has just gotten thicker than desired. 3210 * <br> 3211 * This method was called {@link #thin()}, but now that name refers to a variant that uses 4-way adjacency. 3212 * @return this for chaining 3213 */ 3214 public GreasedRegion thin8way() { 3215 GreasedRegion result = this; 3216 if (width <= 2 || ySections <= 0) { 3217 } else { 3218 GreasedRegion c1 = new GreasedRegion(this).retract8way(), 3219 c2 = new GreasedRegion(c1).expand8way().xor(this).expand8way().and(this); 3220 remake(c1).or(c2); 3221 } 3222 return result; 3223 } 3224 3225 /** 3226 * Calls {@link #thin8way()} repeatedly, until the result is unchanged from the last call. Consider using the idiom 3227 * {@code expand8way().retract().thinFully8way()} to help change a strange appearance when the GreasedRegion this is 3228 * called on touches the edges of the grid. In general, this method is likely to go too far when it tries to thin a 3229 * round or irregular area, and this often results in many diagonal lines spanning the formerly-thick area. 3230 * <br> 3231 * This method was called {@link #thinFully()}, but now that name refers to a variant that uses 4-way adjacency. 3232 * @return this for chaining 3233 */ 3234 public GreasedRegion thinFully8way() 3235 { 3236 while (size() != thin8way().size()); 3237 return this; 3238 } 3239 3240 3241 /** 3242 * Removes "on" cells that are orthogonally adjacent to other "on" cells, keeping at least one cell in a group "on." 3243 * Uses a "checkerboard" pattern to determine which cells to turn off, with all cells that would be black on a 3244 * checkerboard turned off and all others kept as-is. 3245 * @return this for chaining 3246 */ 3247 public GreasedRegion disperse() { 3248 GreasedRegion result = this; 3249 if (width < 1 || ySections <= 0) { 3250 } else { 3251 long mask = 0x5555555555555555L; 3252 for (int i = 0; i < width; i++) { 3253 for (int j = 0; j < ySections; j++) { 3254 data[j] &= mask; 3255 } 3256 mask = ~mask; 3257 } 3258 tallied = false; 3259 } 3260 return result; 3261 } 3262 /** 3263 * Removes "on" cells that are 8-way adjacent to other "on" cells, keeping at least one cell in a group "on." 3264 * Uses a "grid-like" pattern to determine which cells to turn off, with all cells with even x and even y kept as-is 3265 * but all other cells (with either or both odd x or odd y) turned off. 3266 * @return this for chaining 3267 */ 3268 public GreasedRegion disperse8way() { 3269 GreasedRegion result = this; 3270 if (width < 1 || ySections <= 0) { 3271 } else { 3272 int len = data.length; 3273 long mask = 0x5555555555555555L; 3274 for (int j = 0; j < len - 1; j += 2) { 3275 data[j] &= mask; 3276 data[j + 1] = 0; 3277 } 3278 tallied = false; 3279 } 3280 return result; 3281 } 3282 /** 3283 * Removes "on" cells that are nearby other "on" cells, with a random factor to which bits are actually turned off 3284 * that still ensures exactly half of the bits are kept as-is (the one exception is when height is an odd number, 3285 * which makes the bottom row slightly random). 3286 * @param random the RNG used for a random factor 3287 * @return this for chaining 3288 */ 3289 public GreasedRegion disperseRandom(RandomnessSource random) { 3290 GreasedRegion result = this; 3291 if (width < 1 || ySections <= 0) { 3292 } else { 3293 int len = data.length; 3294 for (int j = 0; j < len; j++) { 3295 data[j] &= randomInterleave(random); 3296 } 3297 tallied = false; 3298 } 3299 return result; 3300 } 3301 /** 3302 * Generates a random 64-bit long with a number of '1' bits (Hamming weight) equal on average to bitCount. 3303 * For example, calling this with a parameter of 32 will be equivalent to calling nextLong() on the given 3304 * RandomnessSource, which could be an {@link RNG} or any RandomnessSource with good 64-bit generation quality. 3305 * Calling this with a parameter of 16 will have on average 16 of the 64 bits in the returned long set to '1', 3306 * distributed pseudo-randomly, while a parameter of 47 will have on average 47 bits set. This can be useful for 3307 * certain code that uses bits to represent data but needs a different ratio of set bits to unset bits than 1:1. 3308 * <br> 3309 * The parameter {@code random} can be an object like a {@link DiverRNG}, an {@link RNG} backed by a 3310 * well-distributed RandomnessSource like its default, DiverRNG, a {@link GWTRNG} (especially if you target GWT, 3311 * where it will perform much better than most alternatives), or any of various other RandomnessSource 3312 * implementations that distribute bits well for {@link RandomnessSource#nextLong()}, but should not be 3313 * intentionally-biased RNGs like {@link DharmaRNG} or {@link EditRNG}, nor double-based QRNGs like 3314 * {@link VanDerCorputQRNG} or {@link SobolQRNG}. 3315 * @param random used to determine random factors; likely to be an {@link RNG}, {@link DiverRNG}, or {@link GWTRNG} 3316 * @param bitCount an int, only considered if between 0 and 64, that is the average number of bits to set 3317 * @return a 64-bit long that, on average, should have bitCount bits set to 1, potentially anywhere in the long 3318 */ 3319 public static long approximateBits(RandomnessSource random, int bitCount) { 3320 long result; 3321 if (bitCount <= 0) { 3322 result = 0L; 3323 } else if (bitCount >= 64) { 3324 result = -1L; 3325 } else if (bitCount == 32) { 3326 result = random.nextLong(); 3327 } else { 3328 boolean high = bitCount > 32; 3329 int altered = (high ? 64 - bitCount : bitCount), lsb = NumberTools.lowestOneBit(altered); 3330 long data = random.nextLong(); 3331 for (int i = lsb << 1; i <= 16; i <<= 1) { 3332 if ((altered & i) == 0) 3333 data &= random.nextLong(); 3334 else 3335 data |= random.nextLong(); 3336 } 3337 result = high ? ~(random.nextLong() & data) : (random.nextLong() & data); 3338 } 3339 return result; 3340 } 3341 3342 /** 3343 * Gets a somewhat-random long with exactly 32 bits set; in each pair of bits starting at bit 0 and bit 1, then bit 3344 * 2 and bit 3, up to bit 62 and bit 3, one bit will be 1 and one bit will be 0 in each pair. 3345 * <br> 3346 * Not exactly general-use; meant for generating data for GreasedRegion. 3347 * @return a random long with 32 "1" bits, distributed so exactly one bit is "1" for each pair of bits 3348 */ 3349 public static long randomInterleave(RandomnessSource random) { 3350 long bits = random.nextLong() & 0xFFFFFFFFL, ib = ~bits & 0xFFFFFFFFL; 3351 bits |= (bits << 16); 3352 ib |= (ib << 16); 3353 bits &= 0x0000FFFF0000FFFFL; 3354 ib &= 0x0000FFFF0000FFFFL; 3355 bits |= (bits << 8); 3356 ib |= (ib << 8); 3357 bits &= 0x00FF00FF00FF00FFL; 3358 ib &= 0x00FF00FF00FF00FFL; 3359 bits |= (bits << 4); 3360 ib |= (ib << 4); 3361 bits &= 0x0F0F0F0F0F0F0F0FL; 3362 ib &= 0x0F0F0F0F0F0F0F0FL; 3363 bits |= (bits << 2); 3364 ib |= (ib << 2); 3365 bits &= 0x3333333333333333L; 3366 ib &= 0x3333333333333333L; 3367 bits |= (bits << 1); 3368 ib |= (ib << 1); 3369 bits &= 0x5555555555555555L; 3370 ib &= 0x5555555555555555L; 3371 return (bits | (ib << 1)); 3372 } 3373 3374 /** 3375 * Takes the "on" cells in this GreasedRegion and expands them by one cell in the 4 orthogonal directions, making 3376 * each "on" cell take up a plus-shaped area that may overlap with other "on" cells (which is just a normal "on" 3377 * cell then). 3378 * <br> 3379 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3380 * methods (including expand(), {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3381 * very well by operating in bulk on up to 64 cells at a time. 3382 * @return this for chaining 3383 */ 3384 public GreasedRegion expand() { 3385 GreasedRegion result = this; 3386 if (width < 2 || ySections == 0) { 3387 } else { 3388 final long[] next = new long[width * ySections]; 3389 System.arraycopy(data, 0, next, 0, width * ySections); 3390 for (int a = 0; a < ySections; a++) { 3391 next[a] |= (data[a] << 1) | (data[a] >>> 1) | data[a + ySections]; 3392 next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] << 1) | (data[(width - 1) * ySections + a] >>> 1) | data[(width - 2) * ySections + a]; 3393 3394 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3395 next[i] |= (data[i] << 1) | (data[i] >>> 1) | data[i - ySections] | data[i + ySections]; 3396 } 3397 3398 if (a > 0) { 3399 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3400 next[i] |= (data[i - 1] & 0x8000000000000000L) >>> 63; 3401 } 3402 } 3403 3404 if (a < ySections - 1) { 3405 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3406 next[i] |= (data[i + 1] & 1L) << 63; 3407 } 3408 } 3409 } 3410 if (ySections > 0 && yEndMask != -1) { 3411 for (int a = ySections - 1; a < next.length; a += ySections) { 3412 next[a] &= yEndMask; 3413 } 3414 } 3415 data = next; 3416 tallied = false; 3417 } 3418 3419 return result; 3420 } 3421 /** 3422 * Takes the "on" cells in this GreasedRegion and expands them by amount cells in the 4 orthogonal directions, 3423 * making each "on" cell take up a plus-shaped area that may overlap with other "on" cells (which is just a normal 3424 * "on" cell then). 3425 * <br> 3426 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3427 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3428 * very well by operating in bulk on up to 64 cells at a time. 3429 * @return this for chaining 3430 */ 3431 @Override 3432 public GreasedRegion expand(int amount) 3433 { 3434 for (int i = 0; i < amount; i++) { 3435 expand(); 3436 } 3437 return this; 3438 } 3439 /** 3440 * Takes the "on" cells in this GreasedRegion and produces amount GreasedRegions, each one expanded by 1 cell in 3441 * the 4 orthogonal directions relative to the previous GreasedRegion, making each "on" cell take up a plus-shaped 3442 * area that may overlap with other "on" cells (which is just a normal "on" cell then). This returns an array of 3443 * GreasedRegions with progressively greater expansions, and does not modify this GreasedRegion. 3444 * <br> 3445 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3446 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3447 * very well by operating in bulk on up to 64 cells at a time. 3448 * @return an array of new GreasedRegions, length == amount, where each one is expanded by 1 relative to the last 3449 */ 3450 public GreasedRegion[] expandSeries(int amount) { 3451 GreasedRegion[] result; 3452 if (amount <= 0) { 3453 result = new GreasedRegion[0]; 3454 } else { 3455 GreasedRegion[] regions = new GreasedRegion[amount]; 3456 GreasedRegion temp = new GreasedRegion(this); 3457 for (int i = 0; i < amount; i++) { 3458 regions[i] = new GreasedRegion(temp.expand()); 3459 } 3460 result = regions; 3461 } 3462 return result; 3463 } 3464 3465 public ArrayList<GreasedRegion> expandSeriesToLimit() 3466 { 3467 ArrayList<GreasedRegion> regions = new ArrayList<>(); 3468 GreasedRegion temp = new GreasedRegion(this); 3469 while (temp.size() != temp.expand().size()) { 3470 regions.add(new GreasedRegion(temp)); 3471 } 3472 return regions; 3473 } 3474 /** 3475 * Takes the "on" cells in this GreasedRegion and expands them by one cell in the 4 orthogonal directions, producing 3476 * a diamoond shape, then removes the original area before expansion, producing only the cells that were "off" in 3477 * this and within 1 cell (orthogonal-only) of an "on" cell. This method is similar to {@link #surface()}, but 3478 * surface finds cells inside the current GreasedRegion, while fringe finds cells outside it. 3479 * <br> 3480 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3481 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3482 * very well by operating in bulk on up to 64 cells at a time. The surface and fringe methods do allocate one 3483 * temporary GreasedRegion to store the original before modification, but the others generally don't. 3484 * @return this for chaining 3485 */ 3486 public GreasedRegion fringe() 3487 { 3488 GreasedRegion cpy = new GreasedRegion(this); 3489 expand(); 3490 return andNot(cpy); 3491 } 3492 /** 3493 * Takes the "on" cells in this GreasedRegion and expands them by amount cells in the 4 orthogonal directions 3494 * (iteratively, producing a diamond shape), then removes the original area before expansion, producing only the 3495 * cells that were "off" in this and within amount cells (orthogonal-only) of an "on" cell. This method is similar 3496 * to {@link #surface()}, but surface finds cells inside the current GreasedRegion, while fringe finds cells outside 3497 * it. 3498 * <br> 3499 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3500 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3501 * very well by operating in bulk on up to 64 cells at a time. The surface and fringe methods do allocate one 3502 * temporary GreasedRegion to store the original before modification, but the others generally don't. 3503 * @return this for chaining 3504 */ 3505 3506 public GreasedRegion fringe(int amount) 3507 { 3508 GreasedRegion cpy = new GreasedRegion(this); 3509 expand(amount); 3510 return andNot(cpy); 3511 } 3512 3513 /** 3514 * Takes the "on" cells in this GreasedRegion and produces amount GreasedRegions, each one expanded by 1 cell in 3515 * the 4 orthogonal directions relative to the previous GreasedRegion, making each "on" cell take up a diamond- 3516 * shaped area. After producing the expansions, this removes the previous GreasedRegion from the next GreasedRegion 3517 * in the array, making each "fringe" in the series have 1 "thickness," which can be useful for finding which layer 3518 * of expansion a cell lies in. This returns an array of GreasedRegions with progressively greater expansions 3519 * without the cells of this GreasedRegion, and does not modify this GreasedRegion. 3520 * <br> 3521 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3522 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3523 * very well by operating in bulk on up to 64 cells at a time. 3524 * @return an array of new GreasedRegions, length == amount, where each one is a 1-depth fringe pushed further out from this 3525 */ 3526 public GreasedRegion[] fringeSeries(int amount) { 3527 GreasedRegion[] result; 3528 if (amount <= 0) { 3529 result = new GreasedRegion[0]; 3530 } else { 3531 GreasedRegion[] regions = new GreasedRegion[amount]; 3532 GreasedRegion temp = new GreasedRegion(this); 3533 regions[0] = new GreasedRegion(temp); 3534 for (int i = 1; i < amount; i++) { 3535 regions[i] = new GreasedRegion(temp.expand()); 3536 } 3537 for (int i = 0; i < amount - 1; i++) { 3538 regions[i].xor(regions[i + 1]); 3539 } 3540 regions[amount - 1].fringe(); 3541 result = regions; 3542 } 3543 return result; 3544 } 3545 public ArrayList<GreasedRegion> fringeSeriesToLimit() 3546 { 3547 ArrayList<GreasedRegion> regions = expandSeriesToLimit(); 3548 for (int i = regions.size() - 1; i > 0; i--) { 3549 regions.get(i).xor(regions.get(i-1)); 3550 } 3551 regions.get(0).xor(this); 3552 return regions; 3553 } 3554 3555 /** 3556 * Takes the "on" cells in this GreasedRegion and retracts them by one cell in the 4 orthogonal directions, 3557 * making each "on" cell that was orthogonally adjacent to an "off" cell into an "off" cell. 3558 * <br> 3559 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3560 * methods (including {@link #expand()}, retract(), {@link #fringe()}, and {@link #surface()}) all perform 3561 * very well by operating in bulk on up to 64 cells at a time. 3562 * @return this for chaining 3563 */ 3564 public GreasedRegion retract() { 3565 GreasedRegion result = this; 3566 if (width <= 2 || ySections <= 0) { 3567 } else { 3568 final long[] next = new long[width * ySections]; 3569 System.arraycopy(data, ySections, next, ySections, (width - 2) * ySections); 3570 for (int a = 0; a < ySections; a++) { 3571 if (a > 0 && a < ySections - 1) { 3572 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3573 next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63)) 3574 & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63)) 3575 & data[i - ySections] 3576 & data[i + ySections]; 3577 } 3578 } else if (a > 0) { 3579 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3580 next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63)) 3581 & (data[i] >>> 1) 3582 & data[i - ySections] 3583 & data[i + ySections]; 3584 } 3585 } else if (a < ySections - 1) { 3586 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3587 next[i] &= (data[i] << 1) 3588 & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63)) 3589 & data[i - ySections] 3590 & data[i + ySections]; 3591 } 3592 } else // only the case when ySections == 1 3593 { 3594 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3595 next[i] &= (data[i] << 1) & (data[i] >>> 1) & data[i - ySections] & data[i + ySections]; 3596 } 3597 } 3598 } 3599 if (yEndMask != -1) { 3600 for (int a = ySections - 1; a < next.length; a += ySections) { 3601 next[a] &= yEndMask; 3602 } 3603 } 3604 data = next; 3605 tallied = false; 3606 } 3607 3608 return result; 3609 } 3610 3611 /** 3612 * Takes the "on" cells in this GreasedRegion and retracts them by one cell in the 4 orthogonal directions, doing 3613 * this iteeratively amount times, making each "on" cell that was within amount orthogonal distance to an "off" cell 3614 * into an "off" cell. 3615 * <br> 3616 * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease 3617 * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform 3618 * very well by operating in bulk on up to 64 cells at a time. 3619 * @return this for chaining 3620 */ 3621 public GreasedRegion retract(int amount) 3622 { 3623 for (int i = 0; i < amount; i++) { 3624 retract(); 3625 } 3626 return this; 3627 } 3628 3629 public GreasedRegion[] retractSeries(int amount) { 3630 GreasedRegion[] result; 3631 if (amount <= 0) { 3632 result = new GreasedRegion[0]; 3633 } else { 3634 GreasedRegion[] regions = new GreasedRegion[amount]; 3635 GreasedRegion temp = new GreasedRegion(this); 3636 for (int i = 0; i < amount; i++) { 3637 regions[i] = new GreasedRegion(temp.retract()); 3638 } 3639 result = regions; 3640 } 3641 return result; 3642 } 3643 3644 public ArrayList<GreasedRegion> retractSeriesToLimit() 3645 { 3646 ArrayList<GreasedRegion> regions = new ArrayList<>(); 3647 GreasedRegion temp = new GreasedRegion(this); 3648 while (!temp.retract().isEmpty()) { 3649 regions.add(new GreasedRegion(temp)); 3650 } 3651 return regions; 3652 } 3653 3654 public GreasedRegion surface() 3655 { 3656 GreasedRegion cpy = new GreasedRegion(this).retract(); 3657 return xor(cpy); 3658 } 3659 public GreasedRegion surface(int amount) 3660 { 3661 GreasedRegion cpy = new GreasedRegion(this).retract(amount); 3662 return xor(cpy); 3663 } 3664 3665 public GreasedRegion[] surfaceSeries(int amount) 3666 { 3667 if(amount <= 0) return new GreasedRegion[0]; 3668 GreasedRegion[] regions = new GreasedRegion[amount]; 3669 GreasedRegion temp = new GreasedRegion(this); 3670 regions[0] = new GreasedRegion(temp); 3671 for (int i = 1; i < amount; i++) { 3672 regions[i] = new GreasedRegion(temp.retract()); 3673 } 3674 for (int i = 0; i < amount - 1; i++) { 3675 regions[i].xor(regions[i + 1]); 3676 } 3677 regions[amount - 1].surface(); 3678 return regions; 3679 } 3680 3681 public ArrayList<GreasedRegion> surfaceSeriesToLimit() { 3682 ArrayList<GreasedRegion> result; 3683 ArrayList<GreasedRegion> regions = retractSeriesToLimit(); 3684 if (regions.isEmpty()) { 3685 result = regions; 3686 } else { 3687 regions.add(0, regions.get(0).copy().xor(this)); 3688 for (int i = 1; i < regions.size() - 1; i++) { 3689 regions.get(i).xor(regions.get(i + 1)); 3690 } 3691 result = regions; 3692 } 3693 return result; 3694 } 3695 public GreasedRegion expand8way() { 3696 GreasedRegion result = this; 3697 if (width < 2 || ySections <= 0) { 3698 } else { 3699 final long[] next = new long[width * ySections]; 3700 System.arraycopy(data, 0, next, 0, width * ySections); 3701 for (int a = 0; a < ySections; a++) { 3702 next[a] |= (data[a] << 1) | (data[a] >>> 1) 3703 | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1); 3704 next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] << 1) | (data[(width - 1) * ySections + a] >>> 1) 3705 | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1); 3706 3707 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3708 next[i] |= (data[i] << 1) | (data[i] >>> 1) 3709 | data[i - ySections] | (data[i - ySections] << 1) | (data[i - ySections] >>> 1) 3710 | data[i + ySections] | (data[i + ySections] << 1) | (data[i + ySections] >>> 1); 3711 } 3712 3713 if (a > 0) { 3714 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3715 next[i] |= ((data[i - 1] & 0x8000000000000000L) >>> 63) | 3716 ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63) | 3717 ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63); 3718 } 3719 } 3720 3721 if (a < ySections - 1) { 3722 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3723 next[i] |= ((data[i + 1] & 1L) << 63) | 3724 ((data[i - ySections + 1] & 1L) << 63) | 3725 ((data[i + ySections + 1] & 1L) << 63); 3726 } 3727 } 3728 } 3729 if (ySections > 0 && yEndMask != -1) { 3730 for (int a = ySections - 1; a < next.length; a += ySections) { 3731 next[a] &= yEndMask; 3732 } 3733 } 3734 data = next; 3735 tallied = false; 3736 } 3737 3738 return result; 3739 } 3740 3741 @Override 3742 public GreasedRegion expand8way(int amount) 3743 { 3744 for (int i = 0; i < amount; i++) { 3745 expand8way(); 3746 } 3747 return this; 3748 } 3749 3750 public GreasedRegion[] expandSeries8way(int amount) { 3751 GreasedRegion[] result; 3752 if (amount <= 0) { 3753 result = new GreasedRegion[0]; 3754 } else { 3755 GreasedRegion[] regions = new GreasedRegion[amount]; 3756 GreasedRegion temp = new GreasedRegion(this); 3757 for (int i = 0; i < amount; i++) { 3758 regions[i] = new GreasedRegion(temp.expand8way()); 3759 } 3760 result = regions; 3761 } 3762 return result; 3763 } 3764 public ArrayList<GreasedRegion> expandSeriesToLimit8way() 3765 { 3766 ArrayList<GreasedRegion> regions = new ArrayList<>(); 3767 GreasedRegion temp = new GreasedRegion(this); 3768 while (temp.size() != temp.expand8way().size()) { 3769 regions.add(new GreasedRegion(temp)); 3770 } 3771 return regions; 3772 } 3773 3774 public GreasedRegion fringe8way() 3775 { 3776 GreasedRegion cpy = new GreasedRegion(this); 3777 expand8way(); 3778 return andNot(cpy); 3779 } 3780 public GreasedRegion fringe8way(int amount) 3781 { 3782 GreasedRegion cpy = new GreasedRegion(this); 3783 expand8way(amount); 3784 return andNot(cpy); 3785 } 3786 3787 public GreasedRegion[] fringeSeries8way(int amount) { 3788 GreasedRegion[] result; 3789 if (amount <= 0) { 3790 result = new GreasedRegion[0]; 3791 } else { 3792 GreasedRegion[] regions = new GreasedRegion[amount]; 3793 GreasedRegion temp = new GreasedRegion(this); 3794 regions[0] = new GreasedRegion(temp); 3795 for (int i = 1; i < amount; i++) { 3796 regions[i] = new GreasedRegion(temp.expand8way()); 3797 } 3798 for (int i = 0; i < amount - 1; i++) { 3799 regions[i].xor(regions[i + 1]); 3800 } 3801 regions[amount - 1].fringe8way(); 3802 result = regions; 3803 } 3804 return result; 3805 } 3806 public ArrayList<GreasedRegion> fringeSeriesToLimit8way() 3807 { 3808 ArrayList<GreasedRegion> regions = expandSeriesToLimit8way(); 3809 for (int i = regions.size() - 1; i > 0; i--) { 3810 regions.get(i).xor(regions.get(i-1)); 3811 } 3812 regions.get(0).xor(this); 3813 return regions; 3814 } 3815 3816 public GreasedRegion retract8way() { 3817 GreasedRegion result = this; 3818 if (width <= 2 || ySections <= 0) { 3819 } else { 3820 final long[] next = new long[width * ySections]; 3821 System.arraycopy(data, ySections, next, ySections, (width - 2) * ySections); 3822 for (int a = 0; a < ySections; a++) { 3823 if (a > 0 && a < ySections - 1) { 3824 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3825 next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63)) 3826 & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63)) 3827 & data[i - ySections] 3828 & data[i + ySections] 3829 & ((data[i - ySections] << 1) | ((data[i - 1 - ySections] & 0x8000000000000000L) >>> 63)) 3830 & ((data[i + ySections] << 1) | ((data[i - 1 + ySections] & 0x8000000000000000L) >>> 63)) 3831 & ((data[i - ySections] >>> 1) | ((data[i + 1 - ySections] & 1L) << 63)) 3832 & ((data[i + ySections] >>> 1) | ((data[i + 1 + ySections] & 1L) << 63)); 3833 } 3834 } else if (a > 0) { 3835 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3836 next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63)) 3837 & (data[i] >>> 1) 3838 & data[i - ySections] 3839 & data[i + ySections] 3840 & ((data[i - ySections] << 1) | ((data[i - 1 - ySections] & 0x8000000000000000L) >>> 63)) 3841 & ((data[i + ySections] << 1) | ((data[i - 1 + ySections] & 0x8000000000000000L) >>> 63)) 3842 & (data[i - ySections] >>> 1) 3843 & (data[i + ySections] >>> 1); 3844 } 3845 } else if (a < ySections - 1) { 3846 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3847 next[i] &= (data[i] << 1) 3848 & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63)) 3849 & data[i - ySections] 3850 & data[i + ySections] 3851 & (data[i - ySections] << 1) 3852 & (data[i + ySections] << 1) 3853 & ((data[i - ySections] >>> 1) | ((data[i + 1 - ySections] & 1L) << 63)) 3854 & ((data[i + ySections] >>> 1) | ((data[i + 1 + ySections] & 1L) << 63)); 3855 } 3856 } else // only the case when ySections == 1 3857 { 3858 for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) { 3859 next[i] &= (data[i] << 1) 3860 & (data[i] >>> 1) 3861 & data[i - ySections] 3862 & data[i + ySections] 3863 & (data[i - ySections] << 1) 3864 & (data[i + ySections] << 1) 3865 & (data[i - ySections] >>> 1) 3866 & (data[i + ySections] >>> 1); 3867 } 3868 } 3869 } 3870 if (yEndMask != -1) { 3871 for (int a = ySections - 1; a < next.length; a += ySections) { 3872 next[a] &= yEndMask; 3873 } 3874 } 3875 data = next; 3876 tallied = false; 3877 } 3878 3879 return result; 3880 } 3881 3882 public GreasedRegion retract8way(int amount) 3883 { 3884 for (int i = 0; i < amount; i++) { 3885 retract8way(); 3886 } 3887 return this; 3888 } 3889 3890 public GreasedRegion[] retractSeries8way(int amount) { 3891 GreasedRegion[] result; 3892 if (amount <= 0) { 3893 result = new GreasedRegion[0]; 3894 } else { 3895 GreasedRegion[] regions = new GreasedRegion[amount]; 3896 GreasedRegion temp = new GreasedRegion(this); 3897 for (int i = 0; i < amount; i++) { 3898 regions[i] = new GreasedRegion(temp.retract8way()); 3899 } 3900 result = regions; 3901 } 3902 return result; 3903 } 3904 3905 public ArrayList<GreasedRegion> retractSeriesToLimit8way() 3906 { 3907 ArrayList<GreasedRegion> regions = new ArrayList<>(); 3908 GreasedRegion temp = new GreasedRegion(this); 3909 while (!temp.retract8way().isEmpty()) { 3910 regions.add(new GreasedRegion(temp)); 3911 } 3912 return regions; 3913 } 3914 3915 public GreasedRegion surface8way() 3916 { 3917 GreasedRegion cpy = new GreasedRegion(this).retract8way(); 3918 return xor(cpy); 3919 } 3920 3921 public GreasedRegion surface8way(int amount) 3922 { 3923 GreasedRegion cpy = new GreasedRegion(this).retract8way(amount); 3924 return xor(cpy); 3925 } 3926 3927 public GreasedRegion[] surfaceSeries8way(int amount) { 3928 GreasedRegion[] result; 3929 if (amount <= 0) { 3930 result = new GreasedRegion[0]; 3931 } else { 3932 GreasedRegion[] regions = new GreasedRegion[amount]; 3933 GreasedRegion temp = new GreasedRegion(this); 3934 regions[0] = new GreasedRegion(temp); 3935 for (int i = 1; i < amount; i++) { 3936 regions[i] = new GreasedRegion(temp.retract8way()); 3937 } 3938 for (int i = 0; i < amount - 1; i++) { 3939 regions[i].xor(regions[i + 1]); 3940 } 3941 regions[amount - 1].surface8way(); 3942 result = regions; 3943 } 3944 return result; 3945 } 3946 public ArrayList<GreasedRegion> surfaceSeriesToLimit8way() { 3947 ArrayList<GreasedRegion> result; 3948 ArrayList<GreasedRegion> regions = retractSeriesToLimit8way(); 3949 if (regions.isEmpty()) { 3950 result = regions; 3951 } else { 3952 regions.add(0, regions.get(0).copy().xor(this)); 3953 for (int i = 1; i < regions.size() - 1; i++) { 3954 regions.get(i).xor(regions.get(i + 1)); 3955 } 3956 result = regions; 3957 } 3958 return result; 3959 } 3960 3961 /** 3962 * Like {@link #expand()}, but limits expansion to the "on" cells of {@code bounds}. Expands in all 4-way directions 3963 * by one cell simultaneously, and only successfully affects the cells that are adjacent to this and are in bounds. 3964 * @param bounds the set of "on" cells that limits where this can expand into 3965 * @return this, after expanding, for chaining 3966 */ 3967 public GreasedRegion flood(GreasedRegion bounds) { 3968 GreasedRegion result = this; 3969 if (width < 2 || ySections <= 0 || bounds == null || bounds.width < 2 || bounds.ySections <= 0) { 3970 } else { 3971 final long[] next = new long[width * ySections]; 3972 for (int a = 0; a < ySections && a < bounds.ySections; a++) { 3973 next[a] |= (data[a] | (data[a] << 1) | (data[a] >>> 1) | data[a + ySections]) & bounds.data[a]; 3974 next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] | (data[(width - 1) * ySections + a] << 1) 3975 | (data[(width - 1) * ySections + a] >>> 1) | data[(width - 2) * ySections + a]) & bounds.data[(width - 1) * bounds.ySections + a]; 3976 3977 for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections && 3978 j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) { 3979 next[i] |= (data[i] | (data[i] << 1) | (data[i] >>> 1) | data[i - ySections] | data[i + ySections]) & bounds.data[j]; 3980 } 3981 3982 if (a > 0) { 3983 for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections; 3984 i += ySections, j += bounds.ySections) { 3985 next[i] |= (data[i] | ((data[i - 1] & 0x8000000000000000L) >>> 63)) & bounds.data[j]; 3986 } 3987 } 3988 3989 if (a < ySections - 1 && a < bounds.ySections - 1) { 3990 for (int i = ySections + a, j = bounds.ySections + a; 3991 i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) { 3992 next[i] |= (data[i] | ((data[i + 1] & 1L) << 63)) & bounds.data[j]; 3993 } 3994 } 3995 } 3996 if (yEndMask != -1 && bounds.yEndMask != -1) { 3997 if (ySections == bounds.ySections) { 3998 long mask = ((yEndMask >>> 1) <= (bounds.yEndMask >>> 1)) 3999 ? yEndMask : bounds.yEndMask; 4000 for (int a = ySections - 1; a < next.length; a += ySections) { 4001 next[a] &= mask; 4002 } 4003 } else if (ySections < bounds.ySections) { 4004 for (int a = ySections - 1; a < next.length; a += ySections) { 4005 next[a] &= yEndMask; 4006 } 4007 } else { 4008 for (int a = bounds.ySections - 1; a < next.length; a += ySections) { 4009 next[a] &= bounds.yEndMask; 4010 } 4011 } 4012 } 4013 data = next; 4014 tallied = false; 4015 } 4016 4017 return result; 4018 } 4019 4020 /** 4021 * Like {@link #expand(int)}, but limits expansion to the "on" cells of {@code bounds}. Repeatedly expands in the 4022 * 4-way directions by one cell simultaneously, and only successfully affects the cells that are adjacent to the 4023 * previous expansion and are in bounds. This won't skip over gaps in bounds, even if amount is high enough that a 4024 * call to {@link #expand(int)} would reach past the gap; it will stop at the gap and only pass it if expansion 4025 * takes it around. 4026 * @param bounds the set of "on" cells that limits where this can expand into 4027 * @param amount how far to expand this outward by, in cells 4028 * @return this, after expanding, for chaining 4029 */ 4030 public GreasedRegion flood(GreasedRegion bounds, int amount) 4031 { 4032 int ct = size(), ct2; 4033 for (int i = 0; i < amount; i++) { 4034 flood(bounds); 4035 if(ct == (ct2 = size())) 4036 break; 4037 else 4038 ct = ct2; 4039 4040 } 4041 return this; 4042 } 4043 4044 /** 4045 * Repeatedly calls {@link #flood(GreasedRegion)} {@code amount} times and returns the intermediate steps in a 4046 * GreasedRegion array of size {@code amount}. Doesn't modify this GreasedRegion, and doesn't return it in the array 4047 * (it may return a copy of it if and only if no flood8way() calls can expand the area). If this fills 4048 * {@code bounds} as fully as possible and still has steps left, the remaining steps are all copies of the 4049 * fully-filled area. 4050 * @param bounds the set of "on" cells that this will attempt to fill in steps 4051 * @param amount how many steps to flood outward, and the size of the array to return 4052 * @return an array of GreasedRegion, {@code amount} in size, containing larger and larger expansions of this 4053 */ 4054 public GreasedRegion[] floodSeries(GreasedRegion bounds, int amount) { 4055 GreasedRegion[] result; 4056 if (amount <= 0) { 4057 result = new GreasedRegion[0]; 4058 } else { 4059 int ct = size(), ct2; 4060 GreasedRegion[] regions = new GreasedRegion[amount]; 4061 boolean done = false; 4062 GreasedRegion temp = new GreasedRegion(this); 4063 for (int i = 0; i < amount; i++) { 4064 if (done) { 4065 regions[i] = new GreasedRegion(temp); 4066 } else { 4067 regions[i] = new GreasedRegion(temp.flood(bounds)); 4068 if (ct == (ct2 = temp.size())) 4069 done = true; 4070 else 4071 ct = ct2; 4072 } 4073 } 4074 result = regions; 4075 } 4076 return result; 4077 } 4078 4079 /** 4080 * Repeatedly generates new GreasedRegions, each one cell expanded in 4 directions from the previous GreasedRegion 4081 * and staying inside the "on" cells of {@code bounds}, until it can't expand any more. Returns an ArrayList of the 4082 * GreasedRegion steps this generated; this list does not include this GreasedRegion (or any unmodified copy of this 4083 * GreasedRegion), and this method does not modify it. 4084 * @param bounds the set of "on" cells that this will attempt to fill in steps 4085 * @return an ArrayList of steps from one {@link #flood(GreasedRegion)} call to possibly many chained after it 4086 */ 4087 public ArrayList<GreasedRegion> floodSeriesToLimit(GreasedRegion bounds) { 4088 int ct = size(), ct2; 4089 ArrayList<GreasedRegion> regions = new ArrayList<>(); 4090 GreasedRegion temp = new GreasedRegion(this); 4091 while (true) { 4092 temp.flood(bounds); 4093 if (ct == (ct2 = temp.size())) 4094 return regions; 4095 else { 4096 ct = ct2; 4097 regions.add(new GreasedRegion(temp)); 4098 } 4099 } 4100 } 4101 4102 /** 4103 * Like {@link #expand8way()}, but limits expansion to the "on" cells of {@code bounds}. Expands in all directions 4104 * by one cell simultaneously, and only successfully affects the cells that are adjacent to this and are in bounds. 4105 * @param bounds the set of "on" cells that limits where this can expand into 4106 * @return this, after expanding, for chaining 4107 */ 4108 public GreasedRegion flood8way(GreasedRegion bounds) { 4109 GreasedRegion result = this; 4110 if (width < 2 || ySections <= 0 || bounds == null || bounds.width < 2 || bounds.ySections <= 0) { 4111 } else { 4112 final long[] next = new long[width * ySections]; 4113 for (int a = 0; a < ySections && a < bounds.ySections; a++) { 4114 next[a] |= (data[a] | (data[a] << 1) | (data[a] >>> 1) 4115 | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1)) & bounds.data[a]; 4116 next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] 4117 | (data[(width - 1) * ySections + a] << 1) | (data[(width - 1) * ySections + a] >>> 1) 4118 | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1)) 4119 & bounds.data[(width - 1) * bounds.ySections + a]; 4120 4121 for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections && 4122 j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) { 4123 next[i] |= (data[i] | (data[i] << 1) | (data[i] >>> 1) 4124 | data[i - ySections] | (data[i - ySections] << 1) | (data[i - ySections] >>> 1) 4125 | data[i + ySections] | (data[i + ySections] << 1) | (data[i + ySections] >>> 1)) 4126 & bounds.data[j]; 4127 } 4128 4129 if (a > 0) { 4130 for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections; 4131 i += ySections, j += bounds.ySections) { 4132 next[i] |= (data[i] | ((data[i - 1] & 0x8000000000000000L) >>> 63) | 4133 ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63) | 4134 ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63)) & bounds.data[j]; 4135 } 4136 } 4137 4138 if (a < ySections - 1 && a < bounds.ySections - 1) { 4139 for (int i = ySections + a, j = bounds.ySections + a; 4140 i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) { 4141 next[i] |= (data[i] | ((data[i + 1] & 1L) << 63) | 4142 ((data[i - ySections + 1] & 1L) << 63) | 4143 ((data[i + ySections + 1] & 1L) << 63)) & bounds.data[j]; 4144 } 4145 } 4146 } 4147 if (yEndMask != -1 && bounds.yEndMask != -1) { 4148 if (ySections == bounds.ySections) { 4149 long mask = ((yEndMask >>> 1) <= (bounds.yEndMask >>> 1)) 4150 ? yEndMask : bounds.yEndMask; 4151 for (int a = ySections - 1; a < next.length; a += ySections) { 4152 next[a] &= mask; 4153 } 4154 } else if (ySections < bounds.ySections) { 4155 for (int a = ySections - 1; a < next.length; a += ySections) { 4156 next[a] &= yEndMask; 4157 } 4158 } else { 4159 for (int a = bounds.ySections - 1; a < next.length; a += ySections) { 4160 next[a] &= bounds.yEndMask; 4161 } 4162 } 4163 } 4164 data = next; 4165 tallied = false; 4166 } 4167 4168 return result; 4169 } 4170 /** 4171 * Like {@link #expand8way(int)}, but limits expansion to the "on" cells of {@code bounds}. Repeatedly expands in 4172 * all directions by one cell simultaneously, and only successfully affects the cells that are adjacent to the 4173 * previous expansion and are in bounds. This won't skip over gaps in bounds, even if amount is high enough that a 4174 * call to {@link #expand8way(int)} would reach past the gap; it will stop at the gap and only pass it if expansion 4175 * takes it around. 4176 * @param bounds the set of "on" cells that limits where this can expand into 4177 * @param amount how far to expand this outward by, in cells 4178 * @return this, after expanding, for chaining 4179 */ 4180 public GreasedRegion flood8way(GreasedRegion bounds, int amount) 4181 { 4182 int ct = size(), ct2; 4183 for (int i = 0; i < amount; i++) { 4184 flood8way(bounds); 4185 if(ct == (ct2 = size())) 4186 break; 4187 else 4188 ct = ct2; 4189 } 4190 return this; 4191 } 4192 4193 /** 4194 * Repeatedly calls {@link #flood8way(GreasedRegion)} {@code amount} times and returns the intermediate steps in a 4195 * GreasedRegion array of size {@code amount}. Doesn't modify this GreasedRegion, and doesn't return it in the array 4196 * (it may return a copy of it if and only if no flood8way() calls can expand the area). If this fills 4197 * {@code bounds} as fully as possible and still has steps left, the remaining steps are all copies of the 4198 * fully-filled area. 4199 * @param bounds the set of "on" cells that this will attempt to fill in steps 4200 * @param amount how many steps to flood outward, and the size of the array to return 4201 * @return an array of GreasedRegion, {@code amount} in size, containing larger and larger expansions of this 4202 */ 4203 public GreasedRegion[] floodSeries8way(GreasedRegion bounds, int amount) { 4204 GreasedRegion[] result; 4205 if (amount <= 0) { 4206 result = new GreasedRegion[0]; 4207 } else { 4208 int ct = size(), ct2; 4209 GreasedRegion[] regions = new GreasedRegion[amount]; 4210 boolean done = false; 4211 GreasedRegion temp = new GreasedRegion(this); 4212 for (int i = 0; i < amount; i++) { 4213 if (done) { 4214 regions[i] = new GreasedRegion(temp); 4215 } else { 4216 regions[i] = new GreasedRegion(temp.flood8way(bounds)); 4217 if (ct == (ct2 = temp.size())) 4218 done = true; 4219 else 4220 ct = ct2; 4221 } 4222 } 4223 result = regions; 4224 } 4225 return result; 4226 } 4227 4228 /** 4229 * Repeatedly generates new GreasedRegions, each one cell expanded in 8 directions from the previous GreasedRegion 4230 * and staying inside the "on" cells of {@code bounds}, until it can't expand any more. Returns an ArrayList of the 4231 * GreasedRegion steps this generated; this list does not include this GreasedRegion (or any unmodified copy of this 4232 * GreasedRegion), and this method does not modify it. 4233 * @param bounds the set of "on" cells that this will attempt to fill in steps 4234 * @return an ArrayList of steps from one {@link #flood8way(GreasedRegion)} call to possibly many chained after it 4235 */ 4236 public ArrayList<GreasedRegion> floodSeriesToLimit8way(GreasedRegion bounds) { 4237 int ct = size(), ct2; 4238 ArrayList<GreasedRegion> regions = new ArrayList<>(); 4239 GreasedRegion temp = new GreasedRegion(this); 4240 while (true) { 4241 temp.flood8way(bounds); 4242 if (ct == (ct2 = temp.size())) 4243 return regions; 4244 else { 4245 ct = ct2; 4246 regions.add(new GreasedRegion(temp)); 4247 } 4248 } 4249 } 4250 4251 /** 4252 * A randomized flood-fill that modifies this GreasedRegion so it randomly adds adjacent cells while staying inside 4253 * the "on" cells of {@code bounds}, until {@link #size()} is equal to {@code volume} or there are no more cells 4254 * this can expand into. This GreasedRegion acts as the initial state, and often contains just one cell before this 4255 * is called. This method is useful for imitating the movement of fluids like water or smoke within some boundaries. 4256 * @param bounds this GreasedRegion will only expand to cells that are "on" in bounds; bounds should overlap with this 4257 * @param volume the maximum {@link #size()} this GreasedRegion can reach before this stops expanding 4258 * @param rng a random number generator, like {@link RNG} or {@link GWTRNG} 4259 * @return this, after expanding randomly, for chaining 4260 */ 4261 public GreasedRegion spill(GreasedRegion bounds, int volume, IRNG rng) { 4262 GreasedRegion result = this; 4263 if (width < 2 || ySections <= 0 || bounds == null || bounds.width < 2 || bounds.ySections <= 0) { 4264 } else { 4265 int current = size(); 4266 if (current >= volume) { 4267 } else { 4268 GreasedRegion t = new GreasedRegion(this).notAnd(bounds); 4269 long[] b2 = new long[t.data.length]; 4270 System.arraycopy(t.data, 0, b2, 0, b2.length); 4271 t.remake(this).fringe().and(bounds).tally(); 4272 if (t.ct <= 0) { 4273 } else { 4274 Coord c; 4275 int x, y, p; 4276 for (int i = current; i < volume; i++) { 4277 c = t.singleRandom(rng); 4278 x = c.x; 4279 y = c.y; 4280 if (data[p = x * ySections + (y >> 6)] != (data[p] |= 1L << (y & 63))) { 4281 counts[p]++; 4282 for (int j = p + 1; j < data.length; j++) { 4283 if (counts[j] > 0) ++counts[j]; 4284 } 4285 ct++; 4286 t.data[p] &= ~(1L << (y & 63)); 4287 if (x < width - 1 && (b2[p = (x + 1) * ySections + (y >> 6)] & 1L << (y & 63)) != 0) { 4288 t.data[p] |= 1L << (y & 63); 4289 } 4290 if (y < height - 1 && (b2[p = x * ySections + (y + 1 >> 6)] & 1L << (y + 1 & 63)) != 0) { 4291 t.data[p] |= 1L << (y + 1 & 63); 4292 } 4293 if (y > 0 && (b2[p = x * ySections + (y - 1 >> 6)] & 1L << (y - 1 & 63)) != 0) { 4294 t.data[p] |= 1L << (y - 1 & 63); 4295 } 4296 if (x > 0 && (b2[p = (x - 1) * ySections + (y >> 6)] & 1L << (y & 63)) != 0) { 4297 4298 t.data[p] |= 1L << (y & 63); 4299 } 4300 t.tally(); 4301 if (t.ct <= 0) break; 4302 } 4303 } 4304 tallied = false; 4305 } 4306 } 4307 } 4308 return result; 4309 } 4310 4311 /** 4312 * Where a cell is "on" but forms a right-angle with exactly two orthogonally-adjacent "on" cells and exactly two 4313 * orthogonally-adjacent "off" cells, this turns each of those cells "off." This won't affect east-west lines of 4314 * flat "on" cells, nor north-south lines. 4315 * @return this, after removing right-angle corner "on" cells, for chaining 4316 */ 4317 public GreasedRegion removeCorners() 4318 { 4319 if(width <= 2 || ySections <= 0) 4320 return this; 4321 4322 final long[] next = new long[width * ySections]; 4323 System.arraycopy(data, 0, next, 0, width * ySections); 4324 for (int a = 0; a < ySections; a++) { 4325 if(a > 0 && a < ySections - 1) { 4326 next[a] &= (((data[a] << 1) | ((data[a - 1] & 0x8000000000000000L) >>> 63)) 4327 & ((data[a] >>> 1) | ((data[a + 1] & 1L) << 63))); 4328 next[(width - 1) * ySections + a] &= (((data[(width - 1) * ySections + a] << 1) 4329 | ((data[(width - 1) * ySections + a - 1] & 0x8000000000000000L) >>> 63)) 4330 & ((data[(width - 1) * ySections + a] >>> 1) 4331 | ((data[(width - 1) * ySections + a + 1] & 1L) << 63))); 4332 for (int i = ySections+a; i < (width - 1) * ySections; i+= ySections) { 4333 next[i] &= (((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63)) 4334 & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63))) 4335 | (data[i - ySections] 4336 & data[i + ySections]); 4337 } 4338 } 4339 else if(a > 0) { 4340 next[a] &= (((data[a] << 1) | ((data[a - 1] & 0x8000000000000000L) >>> 63)) 4341 & (data[a] >>> 1)); 4342 next[(width - 1) * ySections + a] &= (((data[(width - 1) * ySections + a] << 1) 4343 | ((data[(width - 1) * ySections + a - 1] & 0x8000000000000000L) >>> 63)) 4344 & (data[(width - 1) * ySections + a] >>> 1)); 4345 for (int i = ySections+a; i < (width - 1) * ySections; i+= ySections) { 4346 next[i] &= (((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63)) 4347 & (data[i] >>> 1)) 4348 | (data[i - ySections] 4349 & data[i + ySections]); 4350 } 4351 } 4352 else if(a < ySections - 1) { 4353 next[a] &= ((data[a] << 1) 4354 & ((data[a] >>> 1) 4355 | ((data[a + 1] & 1L) << 63))); 4356 next[(width - 1) * ySections + a] &= ((data[(width - 1) * ySections + a] << 1) 4357 & ((data[(width - 1) * ySections + a] >>> 1) 4358 | ((data[(width - 1) * ySections + a + 1] & 1L) << 63))); 4359 for (int i = ySections+a; i < (width - 1) * ySections; i+= ySections) { 4360 next[i] &= ((data[i] << 1) 4361 & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63))) 4362 | (data[i - ySections] 4363 & data[i + ySections]); 4364 } 4365 } 4366 else // only the case when ySections == 1 4367 { 4368 next[0] &= (data[0] << 1) & (data[0] >>> 1); 4369 next[width-1] &= (data[width-1] << 1) & (data[width-1] >>> 1); 4370 for (int i = 1+a; i < (width - 1); i++) { 4371 next[i] &= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections]); 4372 } 4373 } 4374 } 4375 4376 if(yEndMask != -1) { 4377 for (int a = ySections - 1; a < next.length; a += ySections) { 4378 next[a] &= yEndMask; 4379 } 4380 } 4381 data = next; 4382 tallied = false; 4383 return this; 4384 } 4385 4386 /** 4387 * If this GreasedRegion stores multiple unconnected "on" areas, this finds each isolated area (areas that 4388 * are only adjacent diagonally are considered separate from each other) and returns it as an element in an 4389 * ArrayList of GreasedRegion, with one GreasedRegion per isolated area. Not to be confused with 4390 * {@link #split8way()}, which considers diagonally-adjacent cells as part of one region, while this method requires 4391 * cells to be orthogonally adjacent. 4392 * <br> 4393 * Useful when you have, for example, all the rooms in a dungeon with their connecting corridors removed, but want 4394 * to separate the rooms. You can get the aforementioned data assuming a bare dungeon called map using: 4395 * <br> 4396 * {@code GreasedRegion floors = new GreasedRegion(map, '.'), 4397 * rooms = floors.copy().retract8way().flood(floors, 2), 4398 * corridors = floors.copy().andNot(rooms), 4399 * doors = rooms.copy().and(corridors.copy().fringe());} 4400 * <br> 4401 * You can then get all rooms as separate regions with {@code List<GreasedRegion> apart = split(rooms);}, or 4402 * substitute {@code split(corridors)} to get the corridors. The room-finding technique works by shrinking floors 4403 * by a radius of 1 (8-way), which causes thin areas like corridors of 2 or less width to be removed, then 4404 * flood-filling the floors out from the area that produces by 2 cells (4-way this time) to restore the original 4405 * size of non-corridor areas (plus some extra to ensure odd shapes are kept). Corridors are obtained by removing 4406 * the rooms from floors. The example code also gets the doors (which overlap with rooms, not corridors) by finding 4407 * where the a room and a corridor are adjacent. This technique is used with some enhancements in the RoomFinder 4408 * class. 4409 * @see squidpony.squidgrid.mapping.RoomFinder for a class that uses this technique without exposing GreasedRegion 4410 * @return an ArrayList containing each unconnected area from packed as a GreasedRegion element 4411 */ 4412 public ArrayList<GreasedRegion> split() 4413 { 4414 ArrayList<GreasedRegion> scattered = new ArrayList<>(32); 4415 int fst = firstTight(); 4416 GreasedRegion remaining = new GreasedRegion(this); 4417 while (fst >= 0) { 4418 GreasedRegion filled = new GreasedRegion(width, height).insert(fst).flood(remaining, width * height); 4419 scattered.add(filled); 4420 remaining.andNot(filled); 4421 fst = remaining.firstTight(); 4422 } 4423 return scattered; 4424 } 4425 /** 4426 * If this GreasedRegion stores multiple unconnected "on" areas, this finds each isolated area (areas that 4427 * are only adjacent diagonally are considered <b>one area</b> with this) and returns it as an element in an 4428 * ArrayList of GreasedRegion, with one GreasedRegion per isolated area. This should not be confused with 4429 * {@link #split()}, which is almost identical except that split() considers only orthogonal connections, while this 4430 * method considers both orthogonal and diagonal connections between cells as joining an area. 4431 * <br> 4432 * Useful when you have, for example, all the rooms in a dungeon with their connecting corridors removed, but want 4433 * to separate the rooms. You can get the aforementioned data assuming a bare dungeon called map using: 4434 * <br> 4435 * {@code GreasedRegion floors = new GreasedRegion(map, '.'), 4436 * rooms = floors.copy().retract8way().flood(floors, 2), 4437 * corridors = floors.copy().andNot(rooms), 4438 * doors = rooms.copy().and(corridors.copy().fringe());} 4439 * <br> 4440 * You can then get all rooms as separate regions with {@code List<GreasedRegion> apart = split(rooms);}, or 4441 * substitute {@code split(corridors)} to get the corridors. The room-finding technique works by shrinking floors 4442 * by a radius of 1 (8-way), which causes thin areas like corridors of 2 or less width to be removed, then 4443 * flood-filling the floors out from the area that produces by 2 cells (4-way this time) to restore the original 4444 * size of non-corridor areas (plus some extra to ensure odd shapes are kept). Corridors are obtained by removing 4445 * the rooms from floors. The example code also gets the doors (which overlap with rooms, not corridors) by finding 4446 * where the a room and a corridor are adjacent. This technique is used with some enhancements in the RoomFinder 4447 * class. 4448 * @see squidpony.squidgrid.mapping.RoomFinder for a class that uses this technique without exposing GreasedRegion 4449 * @return an ArrayList containing each unconnected area from packed as a GreasedRegion element 4450 */ 4451 public ArrayList<GreasedRegion> split8way() 4452 { 4453 ArrayList<GreasedRegion> scattered = new ArrayList<>(32); 4454 int fst = firstTight(); 4455 GreasedRegion remaining = new GreasedRegion(this); 4456 while (fst >= 0) { 4457 GreasedRegion filled = new GreasedRegion(width, height).insert(fst).flood8way(remaining, width * height); 4458 scattered.add(filled); 4459 remaining.andNot(filled); 4460 fst = remaining.firstTight(); 4461 } 4462 return scattered; 4463 } 4464 4465 /** 4466 * Finds the largest contiguous area of "on" cells in this GreasedRegion and returns it; does not modify this 4467 * GreasedRegion. If there are multiple areas that are all equally large with no larger area, this returns the 4468 * region it checks first and still is largest (first determined by the same ordering {@link #nth(int)} takes). 4469 * This may return an empty GreasedRegion if there are no "on" cells, but it will never return null. 4470 * Here, contiguous means adjacent on an orthogonal direction, and this doesn't consider diagonally-connected cells 4471 * as contiguous unless they also have an orthogonal connection. 4472 * @return a new GreasedRegion that corresponds to the largest contiguous sub-region of "on" cells in this. 4473 */ 4474 public GreasedRegion largestPart() 4475 { 4476 int fst = firstTight(), bestSize = 0, currentSize; 4477 GreasedRegion remaining = new GreasedRegion(this), filled = new GreasedRegion(width, height), 4478 choice = new GreasedRegion(width, height); 4479 while (fst >= 0) { 4480 filled.empty().insert(fst).flood(remaining, width * height); 4481 if((currentSize = filled.size()) > bestSize) 4482 { 4483 bestSize = currentSize; 4484 choice.remake(filled); 4485 } 4486 remaining.andNot(filled); 4487 fst = remaining.firstTight(); 4488 } 4489 return choice; 4490 } 4491 4492 /** 4493 * Finds the largest contiguous area of "on" cells in this GreasedRegion and returns it; does not modify this 4494 * GreasedRegion. If there are multiple areas that are all equally large with no larger area, this returns the 4495 * region it checks first and still is largest (first determined by the same ordering {@link #nth(int)} takes). 4496 * This may return an empty GreasedRegion if there are no "on" cells, but it will never return null. 4497 * Here, contiguous means adjacent on any 8-way direction, and considers cells as part of a contiguous area even if 4498 * all connections but one, which can be orthogonal or diagonal, are blocked by "off" cells. 4499 * @return a new GreasedRegion that corresponds to the largest contiguous sub-region of "on" cells in this. 4500 */ 4501 public GreasedRegion largestPart8way() 4502 { 4503 int fst = firstTight(), bestSize = 0, currentSize; 4504 GreasedRegion remaining = new GreasedRegion(this), filled = new GreasedRegion(width, height), 4505 choice = new GreasedRegion(width, height); 4506 while (fst >= 0) { 4507 filled.empty().insert(fst).flood8way(remaining, width * height); 4508 if((currentSize = filled.size()) > bestSize) 4509 { 4510 bestSize = currentSize; 4511 choice.remake(filled); 4512 } 4513 remaining.andNot(filled); 4514 fst = remaining.firstTight(); 4515 } 4516 return choice; 4517 } 4518 4519 /** 4520 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor upwards when this is called. 4521 * Up is defined as negative y. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor 4522 * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction. 4523 * @return this, after modifications, for chaining 4524 */ 4525 public GreasedRegion neighborUp() { 4526 GreasedRegion result = this; 4527 if (width < 2 || ySections <= 0) { 4528 } else { 4529 for (int a = ySections - 1; a >= 0; a--) { 4530 if (a > 0) { 4531 for (int i = a; i < width * ySections; i += ySections) { 4532 data[i] = (data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63); 4533 } 4534 } else { 4535 for (int i = a; i < width * ySections; i += ySections) { 4536 data[i] = (data[i] << 1); 4537 } 4538 } 4539 } 4540 tallied = false; 4541 } 4542 return result; 4543 } 4544 4545 /** 4546 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor downwards when this is called. 4547 * Down is defined as positive y. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor 4548 * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction. 4549 * @return this, after modifications, for chaining 4550 */ 4551 public GreasedRegion neighborDown() { 4552 GreasedRegion result = this; 4553 if (width < 2 || ySections <= 0) { 4554 } else { 4555 for (int a = 0; a < ySections; a++) { 4556 if (a < ySections - 1) { 4557 for (int i = a; i < width * ySections; i += ySections) { 4558 data[i] = (data[i] >>> 1) | ((data[i + 1] & 1L) << 63); 4559 } 4560 } else { 4561 for (int i = a; i < width * ySections; i += ySections) { 4562 data[i] = (data[i] >>> 1); 4563 } 4564 } 4565 } 4566 tallied = false; 4567 } 4568 return result; 4569 } 4570 4571 /** 4572 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor to the left when this is called. 4573 * Left is defined as negative x. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor 4574 * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction. 4575 * @return this, after modifications, for chaining 4576 */ 4577 public GreasedRegion neighborLeft() { 4578 GreasedRegion result = this; 4579 if (width < 2 || ySections <= 0) { 4580 } else { 4581 for (int a = 0; a < ySections; a++) { 4582 for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) { 4583 data[i] = data[i - ySections]; 4584 } 4585 data[a] = 0L; 4586 } 4587 tallied = false; 4588 } 4589 return result; 4590 } 4591 4592 /** 4593 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor to the right when this is called. 4594 * Right is defined as positive x. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor 4595 * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction. 4596 * @return this, after modifications, for chaining 4597 */ 4598 public GreasedRegion neighborRight() { 4599 GreasedRegion result = this; 4600 if (width < 2 || ySections <= 0) { 4601 } else { 4602 for (int a = 0; a < ySections; a++) { 4603 for (int i = a; i < (width - 1) * ySections; i += ySections) { 4604 data[i] = data[i + ySections]; 4605 } 4606 data[(width - 1) * ySections + a] = 0L; 4607 } 4608 tallied = false; 4609 } 4610 return result; 4611 } 4612 4613 /** 4614 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor upwards and to the left when this 4615 * is called. Up is defined as negative y, left as negative x. Neighbors are "on" cells exactly one cell away. A 4616 * cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from "on" 4617 * cells in one direction. 4618 * @return this, after modifications, for chaining 4619 */ 4620 public GreasedRegion neighborUpLeft() { 4621 GreasedRegion result = this; 4622 if (width < 2 || ySections <= 0) { 4623 } else { 4624 for (int a = ySections - 1; a >= 0; a--) { 4625 if (a > 0) { 4626 for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) { 4627 data[i] = (data[i - ySections] << 1) | ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63); 4628 } 4629 data[a] = 0L; 4630 } else { 4631 for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) { 4632 data[i] = (data[i - ySections] << 1); 4633 } 4634 data[a] = 0L; 4635 } 4636 } 4637 } 4638 return result; 4639 } 4640 4641 /** 4642 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor upwards and to the right when 4643 * this is called. Up is defined as negative y, right as positive x. Neighbors are "on" cells exactly one cell away. 4644 * A cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from 4645 * "on" cells in one direction. 4646 * @return this, after modifications, for chaining 4647 */ 4648 public GreasedRegion neighborUpRight() { 4649 GreasedRegion result = this; 4650 if (width < 2 || ySections <= 0) { 4651 } else { 4652 for (int a = ySections - 1; a >= 0; a--) { 4653 if (a > 0) { 4654 for (int i = a; i < (width - 1) * ySections; i += ySections) { 4655 data[i] = (data[i + ySections] << 1) | ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63); 4656 } 4657 data[(width - 1) * ySections + a] = 0L; 4658 } else { 4659 for (int i = a; i < (width - 1) * ySections; i += ySections) { 4660 data[i] = (data[i + ySections] << 1); 4661 } 4662 data[(width - 1) * ySections + a] = 0L; 4663 } 4664 } 4665 tallied = false; 4666 } 4667 return result; 4668 } 4669 4670 /** 4671 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor downwards and to the left when 4672 * this is called. Down is defined as positive y, left as negative x. Neighbors are "on" cells exactly one cell 4673 * away. A cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from 4674 * "on" cells in one direction. 4675 * @return this, after modifications, for chaining 4676 */ 4677 public GreasedRegion neighborDownLeft() { 4678 GreasedRegion result = this; 4679 if (width < 2 || ySections <= 0) { 4680 } else { 4681 for (int a = 0; a < ySections; a++) { 4682 if (a < ySections - 1) { 4683 for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) { 4684 data[i] = (data[i - ySections] >>> 1) | ((data[i - ySections + 1] & 1L) << 63); 4685 } 4686 data[a] = 0L; 4687 } else { 4688 for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) { 4689 data[i] = (data[i - ySections] >>> 1); 4690 } 4691 data[a] = 0L; 4692 } 4693 } 4694 tallied = false; 4695 } 4696 return result; 4697 } 4698 4699 /** 4700 * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor downwards and to the right when 4701 * this is called. Down is defined as positive y, right as positive x. Neighbors are "on" cells exactly one cell 4702 * away. A cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from 4703 * "on" cells in one direction. 4704 * @return this, after modifications, for chaining 4705 */ 4706 public GreasedRegion neighborDownRight() { 4707 GreasedRegion result = this; 4708 if (width < 2 || ySections <= 0) { 4709 } else { 4710 for (int a = 0; a < ySections; a++) { 4711 if (a < ySections - 1) { 4712 for (int i = a; i < (width - 1) * ySections; i += ySections) { 4713 data[i] = (data[i + ySections] >>> 1) | ((data[i + ySections + 1] & 1L) << 63); 4714 } 4715 data[(width - 1) * ySections + a] = 0L; 4716 } else { 4717 for (int i = a; i < (width - 1) * ySections; i += ySections) { 4718 data[i] = (data[i + ySections] >>> 1); 4719 } 4720 data[(width - 1) * ySections + a] = 0L; 4721 } 4722 } 4723 tallied = false; 4724 } 4725 return result; 4726 } 4727 4728 public GreasedRegion removeIsolated() 4729 { 4730 int fst = firstTight(); 4731 GreasedRegion remaining = new GreasedRegion(this), filled = new GreasedRegion(this); 4732 while (fst >= 0) { 4733 filled.empty().insert(fst).flood(remaining, 8); 4734 if(filled.size() <= 4) 4735 andNot(filled); 4736 remaining.andNot(filled); 4737 fst = remaining.firstTight(); 4738 } 4739 return this; 4740 } 4741 4742 /** 4743 * Returns true if any cell is "on" in both this GreasedRegion and in other; returns false otherwise. For example, 4744 * if (1,1) is "on" in this and (1,1) is "on" in other, this would return true, regardless of other cells. 4745 * @param other another GreasedRegion; its size does not have to match this GreasedRegion's size 4746 * @return true if this shares any "on" cells with other 4747 */ 4748 public boolean intersects(GreasedRegion other) { 4749 boolean result = false; 4750 if (other != null) { 4751 OUTER: 4752 for (int x = 0; x < width && x < other.width; x++) { 4753 for (int y = 0; y < ySections && y < other.ySections; y++) { 4754 if ((data[x * ySections + y] & other.data[x * ySections + y]) != 0) { 4755 result = true; 4756 break OUTER; 4757 } 4758 } 4759 } 4760 } 4761 return result; 4762 } 4763 4764 public static OrderedSet<GreasedRegion> whichContain(int x, int y, GreasedRegion ... packed) 4765 { 4766 OrderedSet<GreasedRegion> found = new OrderedSet<>(packed.length); 4767 GreasedRegion tmp; 4768 for (int i = 0; i < packed.length; i++) { 4769 if((tmp = packed[i]) != null && tmp.contains(x, y)) 4770 found.add(tmp); 4771 } 4772 return found; 4773 } 4774 4775 public static OrderedSet<GreasedRegion> whichContain(int x, int y, Collection<GreasedRegion> packed) 4776 { 4777 OrderedSet<GreasedRegion> found = new OrderedSet<>(packed.size()); 4778 for (GreasedRegion tmp : packed) { 4779 if(tmp != null && tmp.contains(x, y)) 4780 found.add(tmp); 4781 } 4782 return found; 4783 } 4784 /** 4785 * Tries to look up the position x,y in each GreasedRegion in packed; each GreasedRegion that contains that x,y 4786 * point is appended into the Collection {@code into}. 4787 * @param into a Collection of GreasedRegion that will be modified if this succeeds 4788 * @param x the x-coordinate to look up 4789 * @param y the y-coordinate to look up 4790 * @param packed the array or varargs of GreasedRegion to try to look up the given position in 4791 * @return {@code into}, potentially modified 4792 */ 4793 public static Collection<GreasedRegion> appendContaining(Collection<GreasedRegion> into, int x, int y, GreasedRegion ... packed) 4794 { 4795 GreasedRegion tmp; 4796 for (int i = 0; i < packed.length; i++) { 4797 if((tmp = packed[i]) != null && tmp.contains(x, y)) 4798 into.add(tmp); 4799 } 4800 return into; 4801 } 4802 4803 /** 4804 * Tries to look up the position x,y in each GreasedRegion in packed; each GreasedRegion that contains that x,y 4805 * point is appended into the Collection {@code into}. 4806 * @param into a Collection of GreasedRegion that will be modified if this succeeds 4807 * @param x the x-coordinate to look up 4808 * @param y the y-coordinate to look up 4809 * @param packed the Collection of GreasedRegion to try to look up the given position in 4810 * @return {@code into}, potentially modified 4811 */ 4812 public static Collection<GreasedRegion> appendContaining(Collection<GreasedRegion> into, int x, int y, Collection<GreasedRegion> packed) 4813 { 4814 for (GreasedRegion tmp : packed) { 4815 if(tmp != null && tmp.contains(x, y)) 4816 into.add(tmp); 4817 } 4818 return into; 4819 } 4820 4821 4822 public int size() 4823 { 4824 if(!tallied) 4825 tally(); 4826 return ct; 4827 } 4828 4829 public Coord fit(double xFraction, double yFraction) { 4830 Coord result = null; 4831 boolean finished = false; 4832 int tmp, xTotal = 0, yTotal = 0, xTarget, yTarget, bestX = -1; 4833 long t; 4834 int[] xCounts = new int[width]; 4835 for (int x = 0; x < width; x++) { 4836 for (int s = 0; s < ySections; s++) { 4837 t = data[x * ySections + s] | 0L; 4838 if (t != 0) { 4839 tmp = Long.bitCount(t); 4840 xCounts[x] += tmp; 4841 xTotal += tmp; 4842 } 4843 } 4844 } 4845 xTarget = (int) (xTotal * xFraction); 4846 for (int x = 0; x < width; x++) { 4847 if ((xTarget -= xCounts[x]) < 0) { 4848 bestX = x; 4849 yTotal = xCounts[x]; 4850 break; 4851 } 4852 } 4853 if (bestX < 0) { 4854 result = Coord.get(-1, -1); 4855 } else { 4856 yTarget = (int) (yTotal * yFraction); 4857 for (int s = 0, y = 0; s < ySections; s++) { 4858 t = data[bestX * ySections + s] | 0L; 4859 for (long cy = 1L; cy != 0L && y < height; y++, cy <<= 1) { 4860 if ((t & (cy | 0L)) != 0 && --yTarget < 0) { 4861 result = Coord.get(bestX, y); 4862 finished = true; 4863 break; 4864 } 4865 } 4866 if (finished) break; 4867 } 4868 if (!finished) { 4869 result = Coord.get(-1, -1); 4870 } 4871 } 4872 4873 return result; 4874 } 4875 4876 public int[][] fit(int[][] basis, int defaultValue) { 4877 int[][] result; 4878 int[][] next = ArrayTools.fill(defaultValue, width, height); 4879 if (basis == null || basis.length <= 0 || basis[0] == null || basis[0].length <= 0) { 4880 result = next; 4881 } else { 4882 int tmp, xTotal = 0, yTotal, xTarget, yTarget, bestX, oX = basis.length, oY = basis[0].length, ao; 4883 long t; 4884 int[] xCounts = new int[width]; 4885 for (int x = 0; x < width; x++) { 4886 for (int s = 0; s < ySections; s++) { 4887 t = data[x * ySections + s] | 0L; 4888 if (t != 0) { 4889 tmp = Long.bitCount(t); 4890 xCounts[x] += tmp; 4891 xTotal += tmp; 4892 } 4893 } 4894 } 4895 if (xTotal <= 0) { 4896 result = next; 4897 } else { 4898 for (int aX = 0; aX < oX; aX++) { 4899 CELL_WISE: 4900 for (int aY = 0; aY < oY; aY++) { 4901 if ((ao = basis[aX][aY]) == defaultValue) 4902 continue; 4903 xTarget = xTotal * aX / oX; 4904 for (int x = 0; x < width; x++) { 4905 if ((xTarget -= xCounts[x]) < 0) { 4906 bestX = x; 4907 yTotal = xCounts[x]; 4908 yTarget = yTotal * aY / oY; 4909 for (int s = 0, y = 0; s < ySections; s++) { 4910 t = data[bestX * ySections + s] | 0L; 4911 for (long cy = 1L; cy != 0L && y < height; y++, cy <<= 1) { 4912 if ((t & (cy | 0L)) != 0 && --yTarget < 0) { 4913 next[bestX][y] = ao; 4914 continue CELL_WISE; 4915 } 4916 } 4917 } 4918 continue CELL_WISE; 4919 } 4920 } 4921 4922 } 4923 } 4924 result = next; 4925 } 4926 } 4927 4928 return result; 4929 } 4930 4931 /** 4932 * Don't use this in new code; prefer {@link #mixedRandomSeparated(double)}, {@link #quasiRandomSeparated(double)}, 4933 * or {@link #separatedZCurve(double)}. This method has issues with being unable to fill the requested fraction, but 4934 * the others mentioned don't. See their documentation for what all these group of methods do. 4935 * @param fraction between 0.0 and 1.0 4936 * @return a Coord array that may not have the full fraction used; you have been advised 4937 */ 4938 public Coord[] separatedPortion(double fraction) { 4939 Coord[] result; 4940 if (fraction < 0) { 4941 result = new Coord[0]; 4942 } else { 4943 if (fraction > 1) 4944 fraction = 1; 4945 int ct, tmp, xTotal = 0, yTotal = 0, xTarget, yTarget, bestX = -1; 4946 long t; 4947 int[] xCounts = new int[width]; 4948 for (int s = 0; s < ySections; s++) { 4949 for (int x = 0; x < width; x++) { 4950 t = data[x * ySections + s] | 0L; 4951 if (t != 0) { 4952 tmp = Long.bitCount(t); 4953 xCounts[x] += tmp; 4954 xTotal += tmp; 4955 } 4956 } 4957 } 4958 Coord[] vl = new Coord[ct = (int) (fraction * xTotal)]; 4959 double[] vec = new double[2]; 4960 sobol.skipTo(1337); 4961 EACH_SOBOL: 4962 for (int i = 0; i < ct; i++) { 4963 sobol.fillVector(vec); 4964 xTarget = (int) (xTotal * vec[0]); 4965 for (int x = 0; x < width; x++) { 4966 if ((xTarget -= xCounts[x]) < 0) { 4967 bestX = x; 4968 yTotal = xCounts[x]; 4969 break; 4970 } 4971 } 4972 yTarget = (int) (yTotal * vec[1]); 4973 4974 for (int s = 0, y = 0; s < ySections; s++) { 4975 t = data[bestX * ySections + s] | 0L; 4976 for (long cy = 1L; cy != 0L && y < height; y++, cy <<= 1) { 4977 if ((t & (cy | 0L)) != 0 && --yTarget < 0) { 4978 vl[i] = Coord.get(bestX, y); 4979 continue EACH_SOBOL; 4980 } 4981 } 4982 } 4983 } 4984 result = vl; 4985 } 4986 4987 return result; 4988 } 4989 /** 4990 * Don't use this in new code; prefer {@link #mixedRandomSeparated(double, int, long)} with a random long as the 4991 * last parameter. This method has issues with being unable to fill the requested fraction, but mixedRandomSeparated 4992 * does not. See its documentation for what this method is supposed to do. 4993 * @param fraction between 0.0 and 1.0 4994 * @param rng an IRNG that will be used to get a random starting point in the Sobol sequence this uses internally 4995 * @return a Coord array that may not have the full fraction used; you have been advised 4996 */ 4997 public Coord[] randomSeparated(double fraction, IRNG rng) 4998 { 4999 return randomSeparated(fraction, rng, -1); 5000 } 5001 /** 5002 * Don't use this in new code; prefer {@link #mixedRandomSeparated(double, int, long)} with a random long as the 5003 * last parameter. This method has issues with being unable to fill the requested fraction, but mixedRandomSeparated 5004 * does not. See its documentation for what this method is supposed to do. 5005 * @param fraction between 0.0 and 1.0 5006 * @param rng an IRNG that will be used to get a random starting point in the Sobol sequence this uses internally 5007 * @param limit how many Coord values this should return, at most; typically this will return less 5008 * @return a Coord array that may not have the full fraction used; you have been advised 5009 */ 5010 public Coord[] randomSeparated(double fraction, IRNG rng, int limit) { 5011 Coord[] result; 5012 if (fraction < 0) { 5013 result = new Coord[0]; 5014 } else { 5015 if (fraction > 1) 5016 fraction = 1; 5017 int ct, tmp, xTotal = 0, yTotal = 0, xTarget, yTarget, bestX = -1; 5018 long t; 5019 int[] xCounts = new int[width]; 5020 for (int x = 0; x < width; x++) { 5021 for (int s = 0; s < ySections; s++) { 5022 t = data[x * ySections + s] | 0L; 5023 if (t != 0) { 5024 tmp = Long.bitCount(t); 5025 xCounts[x] += tmp; 5026 xTotal += tmp; 5027 } 5028 } 5029 } 5030 ct = (int) (fraction * xTotal); 5031 if (limit >= 0 && limit < ct) 5032 ct = limit; 5033 Coord[] vl = new Coord[ct]; 5034 double[] vec = new double[2]; 5035 sobol.skipTo(rng.between(1000, 65000)); 5036 EACH_SOBOL: 5037 for (int i = 0; i < ct; i++) { 5038 sobol.fillVector(vec); 5039 xTarget = (int) (xTotal * vec[0]); 5040 for (int x = 0; x < width; x++) { 5041 if ((xTarget -= xCounts[x]) < 0) { 5042 bestX = x; 5043 yTotal = xCounts[x]; 5044 break; 5045 } 5046 } 5047 yTarget = (int) (yTotal * vec[1]); 5048 5049 for (int s = 0, y = 0; s < ySections; s++) { 5050 t = data[bestX * ySections + s] | 0L; 5051 for (long cy = 1; cy != 0 && y < height; y++, cy <<= 1) { 5052 if ((t & (cy | 0L)) != 0 && --yTarget < 0) { 5053 vl[i] = Coord.get(bestX, y); 5054 continue EACH_SOBOL; 5055 } 5056 } 5057 } 5058 } 5059 result = vl; 5060 } 5061 5062 return result; 5063 } 5064 5065 /** 5066 * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming 5067 * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells 5068 * in this. This is pseudo-random with a fixed seed, but is very good at avoiding overlap (just as good as 5069 * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a 5070 * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in 5071 * testing. Does not restrict the size of the returned array other than only using up to 5072 * {@code fraction * size()} cells. 5073 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5074 * @return a freshly-allocated Coord array containing the quasi-random cells 5075 */ 5076 public Coord[] mixedRandomSeparated(double fraction) 5077 { 5078 return mixedRandomSeparated(fraction, -1, 1L); 5079 } 5080 5081 /** 5082 * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming 5083 * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells 5084 * in this. This is pseudo-random with a fixed seed, but is very good at avoiding overlap (just as good as 5085 * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a 5086 * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in 5087 * testing. Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells 5088 * are "on"). If limit is negative, this will not restrict the size. 5089 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5090 * @param limit the maximum size of the array to return 5091 * @return a freshly-allocated Coord array containing the pseudo-random cells 5092 */ 5093 public Coord[] mixedRandomSeparated(double fraction, int limit) 5094 { 5095 return mixedRandomSeparated(fraction, limit, 1L); 5096 } 5097 5098 /** 5099 * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming 5100 * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells 5101 * in this. This is pseudo-random with the given seed (which will be made into an odd number if it is not one 5102 * already), and is very good at avoiding overlap (just as good as {@link #separatedZCurve(double, int)}, and 5103 * probably faster). If you request too many cells (too high of a value for fraction), it will start to overlap, but 5104 * a fraction value of 0.4 reliably has had no overlap in testing. Restricts the total size of the returned array to 5105 * a maximum of {@code limit} (minimum is 0 if no cells are "on"). If limit is negative, this will not restrict the 5106 * size. 5107 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5108 * @param limit the maximum size of the array to return 5109 * @param seed a long seed to change the points; the most significant 21 bits (except the sign bit) and least significant bit are ignored 5110 * @return a freshly-allocated Coord array containing the pseudo-random cells 5111 */ 5112 public Coord[] mixedRandomSeparated(double fraction, int limit, long seed) { 5113 Coord[] result; 5114 if (fraction < 0) { 5115 result = new Coord[0]; 5116 } else { 5117 if (fraction > 1) 5118 fraction = 1; 5119 int tmp, ic; 5120 long t, w; 5121 seed |= 1L; 5122 final int total = size(); 5123 int ct = (int) (total * fraction); 5124 if (limit >= 0 && limit < ct) 5125 ct = limit; 5126 Coord[] vl = new Coord[ct]; 5127 EACH_QUASI: 5128 for (int i = 0; i < ct; i++) { 5129 tmp = (int) (VanDerCorputQRNG.altDetermine(seed, i + 1) * total); 5130 5131 for (int s = 0; s < ySections; s++) { 5132 for (int x = 0; x < width; x++) { 5133 if ((ic = counts[x * ySections + s]) > tmp) { 5134 t = data[x * ySections + s] | 0L; 5135 for (--ic; t != 0; ic--) { 5136 w = NumberTools.lowestOneBit(t); 5137 if (ic == tmp) { 5138 vl[i] = Coord.get(x, (s << 6) | Long.bitCount(w - 1)); 5139 continue EACH_QUASI; 5140 } 5141 t ^= w; 5142 } 5143 } 5144 } 5145 } 5146 } 5147 result = vl; 5148 } 5149 return result; 5150 } 5151 5152 5153 5154 /** 5155 * Modifies this GreasedRegion so it contains a deterministic but random-seeming subset of its previous contents, 5156 * choosing cells so that the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells 5157 * in this. This is pseudo-random with a fixed seed, and is very good at avoiding overlap (just as good as 5158 * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a 5159 * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in 5160 * testing. Does not restrict the count of "on" cells after this returns other than by only using up to 5161 * {@code fraction * size()} cells. 5162 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5163 * @return this for chaining 5164 */ 5165 public GreasedRegion mixedRandomRegion(double fraction) { 5166 return mixedRandomRegion(fraction, -1, 1L); 5167 } 5168 /** 5169 * Modifies this GreasedRegion so it contains a deterministic but random-seeming subset of its previous contents, 5170 * choosing cells so that the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells 5171 * in this. This is pseudo-random with a fixed seed, and is very good at avoiding overlap (just as good as 5172 * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a 5173 * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in 5174 * testing. Restricts the total count of "on" cells after this returns to a maximum of {@code limit} (minimum is 0 5175 * if no cells are "on"). If limit is negative, this will not restrict the count. 5176 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5177 * @param limit the maximum count of "on" cells to keep 5178 * @return this for chaining 5179 */ 5180 public GreasedRegion mixedRandomRegion(double fraction, int limit) { 5181 return mixedRandomRegion(fraction, limit, 1L); 5182 } 5183 /** 5184 * Modifies this GreasedRegion so it contains a deterministic but random-seeming subset of its previous contents, 5185 * choosing cells so that the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells 5186 * in this. This is pseudo-random with the given seed (which will be made into an odd number if it is not one 5187 * already), and is very good at avoiding overlap (just as good as {@link #separatedZCurve(double, int)}, and 5188 * probably faster). If you request too many cells (too high of a value for fraction), it will start to overlap, but 5189 * a fraction value of 0.4 reliably has had no overlap in testing. Restricts the total count of "on" cells after 5190 * this returns to a maximum of {@code limit} (minimum is 0 if no cells are "on"). If limit is negative, this will 5191 * not restrict the count. 5192 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5193 * @param limit the maximum count of "on" cells to keep 5194 * @return this for chaining 5195 */ 5196 public GreasedRegion mixedRandomRegion(double fraction, int limit, long seed) { 5197 GreasedRegion result = this; 5198 int idx, run = 0; 5199 final int total = size(); 5200 if (total <= limit) { 5201 } else if (total <= 0) { 5202 result = empty(); 5203 } else { 5204 if (limit < 0) 5205 limit = (int) (fraction * ct); 5206 if (limit <= 0) { 5207 result = empty(); 5208 } else { 5209 seed |= 1L; 5210 int[] order = new int[limit]; 5211 for (int i = 0, m = 1; i < limit; i++, m++) { 5212 idx = (int) (VanDerCorputQRNG.altDetermine(seed, m) * total); 5213 BIG: 5214 while (true) { 5215 for (int j = 0; j < i; j++) { 5216 if (order[j] == idx) { 5217 idx = (int) (VanDerCorputQRNG.altDetermine(seed, ++m) * total); 5218 continue BIG; 5219 } 5220 } 5221 break; 5222 } 5223 order[i] = idx; 5224 } 5225 idx = 0; 5226 Arrays.sort(order); 5227 long t, w; 5228 ALL: 5229 for (int s = 0; s < ySections; s++) { 5230 for (int x = 0; x < width; x++) { 5231 if ((t = data[x * ySections + s] | 0L) != 0L) { 5232 w = NumberTools.lowestOneBit(t); 5233 while (w != 0) { 5234 if (run++ == order[idx]) { 5235 if (++idx >= limit) { 5236 data[x * ySections + s] &= (w << 1) - 1; 5237 for (int rx = x + 1; rx < width; rx++) { 5238 data[rx * ySections + s] = 0; 5239 } 5240 for (int rs = s + 1; rs < ySections; rs++) { 5241 for (int rx = 0; rx < width; rx++) { 5242 data[rx * ySections + rs] = 0; 5243 } 5244 } 5245 break ALL; 5246 } 5247 } else { 5248 data[x * ySections + s] ^= w; 5249 } 5250 t ^= w; 5251 w = NumberTools.lowestOneBit(t); 5252 } 5253 } 5254 } 5255 } 5256 tallied = false; 5257 } 5258 } 5259 return result; 5260 } 5261 5262 /** 5263 * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells 5264 * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 5265 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 5266 * request too many cells (too high of a value for fraction), it will start to overlap, however. 5267 * Does not restrict the size of the returned array other than only using up to {@code fraction * size()} cells. 5268 * <br> 5269 * You can choose between {@link #mixedRandomSeparated(double)}, {@link #separatedZCurve(double)}, 5270 * {@link #separatedBlue(double)}, and this method, where all are quasi-random, mixedRandom and 5271 * separatedBlue are probably fastest, ZCurve and separatedBlue may have better 2-dimensional gaps 5272 * between cells, and this method is somewhere in the middle. 5273 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5274 * @return a freshly-allocated Coord array containing the quasi-random cells 5275 */ 5276 public Coord[] quasiRandomSeparated(double fraction) 5277 { 5278 return quasiRandomSeparated(fraction, -1); 5279 } 5280 5281 /** 5282 * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells 5283 * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 5284 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 5285 * request too many cells (too high of a value for fraction), it will start to overlap, however. 5286 * Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells are "on"). 5287 * If limit is negative, this will not restrict the size. 5288 * <br> 5289 * You can choose between {@link #mixedRandomSeparated(double)}, {@link #separatedZCurve(double)}, 5290 * {@link #separatedBlue(double)}, and this method, where all are quasi-random, mixedRandom and 5291 * separatedBlue are probably fastest, ZCurve and separatedBlue may have better 2-dimensional gaps 5292 * between cells, and this method is somewhere in the middle. 5293 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5294 * @param limit the maximum size of the array to return 5295 * @return a freshly-allocated Coord array containing the quasi-random cells 5296 */ 5297 public Coord[] quasiRandomSeparated(double fraction, int limit) { 5298 Coord[] result; 5299 if (fraction < 0) { 5300 result = new Coord[0]; 5301 } else { 5302 if (fraction > 1) 5303 fraction = 1; 5304 int ct = 0, tmp, total, ic; 5305 long t, w; 5306 int[] counts = new int[width * ySections]; 5307 for (int i = 0; i < width * ySections; i++) { 5308 tmp = Long.bitCount(data[i]); 5309 counts[i] = tmp == 0 ? -1 : (ct += tmp); 5310 } 5311 total = ct; 5312 ct *= fraction;// (int)(fraction * ct); 5313 if (limit >= 0 && limit < ct) 5314 ct = limit; 5315 Coord[] vl = new Coord[ct]; 5316 EACH_QUASI: 5317 for (int i = 0; i < ct; i++) { 5318 tmp = (int) (VanDerCorputQRNG.determine2(i ^ i >>> 1) * total); 5319 for (int s = 0; s < ySections; s++) { 5320 for (int x = 0; x < width; x++) { 5321 if ((ic = counts[x * ySections + s]) > tmp) { 5322 t = data[x * ySections + s]; 5323 w = NumberTools.lowestOneBit(t); 5324 for (--ic; w != 0; ic--) { 5325 if (ic == tmp) { 5326 vl[i] = Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 5327 continue EACH_QUASI; 5328 } 5329 t ^= w; 5330 w = NumberTools.lowestOneBit(t); 5331 } 5332 } 5333 } 5334 } 5335 vl[i] = atFraction(tmp); 5336 } 5337 result = vl; 5338 } 5339 return result; 5340 } 5341 5342 5343 5344 /** 5345 * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that 5346 * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 5347 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 5348 * request too many cells (too high of a value for fraction), it will start to overlap, however. 5349 * Does not restrict the count of "on" cells after this returns other than by only using up to 5350 * {@code fraction * size()} cells. 5351 * <br> 5352 * You can choose between {@link #mixedRandomRegion(double)}, {@link #separatedRegionZCurve(double)}, 5353 * {@link #separatedRegionBlue(double)}, and this method, where all are quasi-random, mixedRandom and 5354 * separatedRegionBlue are probably fastest, ZCurve and separatedRegionBlue may have better 2-dimensional gaps 5355 * between cells, and this method is somewhere in the middle. 5356 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5357 * @return this for chaining 5358 */ 5359 public GreasedRegion quasiRandomRegion(double fraction) { 5360 return quasiRandomRegion(fraction, -1); 5361 } 5362 /** 5363 * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that 5364 * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 5365 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 5366 * request too many cells (too high of a value for fraction), it will start to overlap, however. 5367 * Restricts the total count of "on" cells after this returns to a maximum of {@code limit} (minimum is 0 if no 5368 * cells are "on"). If limit is negative, this will not restrict the count. 5369 * <br> 5370 * You can choose between {@link #mixedRandomRegion(double)}, {@link #separatedRegionZCurve(double)}, 5371 * {@link #separatedRegionBlue(double)}, and this method, where all are quasi-random, mixedRandom and 5372 * separatedRegionBlue are probably fastest, ZCurve and separatedRegionBlue may have better 2-dimensional gaps 5373 * between cells, and this method is somewhere in the middle. 5374 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 5375 * @param limit the maximum count of "on" cells to keep 5376 * @return this for chaining 5377 */ 5378 public GreasedRegion quasiRandomRegion(double fraction, int limit) { 5379 GreasedRegion result = this; 5380 int ct = size(), idx, run = 0; 5381 if (ct <= limit) { 5382 } else if (ct <= 0) { 5383 result = empty(); 5384 } else { 5385 if (limit < 0) 5386 limit = (int) (fraction * ct); 5387 if (limit <= 0) { 5388 result = empty(); 5389 } else { 5390 int[] order = new int[limit]; 5391 for (int i = 0, m = 1; i < limit; i++, m++) { 5392 idx = (int) (VanDerCorputQRNG.determine2(m) * ct); 5393 BIG: 5394 while (true) { 5395 for (int j = 0; j < i; j++) { 5396 if (order[j] == idx) { 5397 idx = (int) (VanDerCorputQRNG.determine2(++m) * ct); 5398 continue BIG; 5399 } 5400 } 5401 break; 5402 } 5403 order[i] = idx; 5404 } 5405 idx = 0; 5406 Arrays.sort(order); 5407 long t, w; 5408 ALL: 5409 for (int s = 0; s < ySections; s++) { 5410 for (int x = 0; x < width; x++) { 5411 if ((t = data[x * ySections + s]) != 0) { 5412 w = NumberTools.lowestOneBit(t); 5413 while (w != 0) { 5414 if (run++ == order[idx]) { 5415 if (++idx >= limit) { 5416 data[x * ySections + s] &= (w << 1) - 1; 5417 for (int rx = x + 1; rx < width; rx++) { 5418 data[rx * ySections + s] = 0; 5419 } 5420 for (int rs = s + 1; rs < ySections; rs++) { 5421 for (int rx = 0; rx < width; rx++) { 5422 data[rx * ySections + rs] = 0; 5423 } 5424 } 5425 break ALL; 5426 } 5427 } else { 5428 data[x * ySections + s] ^= w; 5429 } 5430 t ^= w; 5431 w = NumberTools.lowestOneBit(t); 5432 } 5433 } 5434 } 5435 } 5436 tallied = false; 5437 } 5438 } 5439 return result; 5440 } 5441 5442 /** 5443 * Like {@link #retract()}, this removes the "on" cells that are 4-way-adjacent to any "off" cell, but unlike that 5444 * method it keeps a fraction of those surface cells, quasi-randomly selecting them. This can be thought of as 5445 * running {@link #surface()} on a copy of this GreasedRegion, running {@link #quasiRandomRegion(double)} on that 5446 * surface with the given fractionKept, taking the original GreasedRegion and removing its whole surface with 5447 * {@link #retract()}, then inserting the quasi-randomly-removed surface into this GreasedRegion to replace its 5448 * surface with a randomly "damaged" one. 5449 * @param fractionKept the fraction between 0.0 and 1.0 of how many cells on the outer surface of this to keep "on" 5450 * @return this for chaining 5451 */ 5452 public GreasedRegion fray(double fractionKept) 5453 { 5454 GreasedRegion cpy = new GreasedRegion(this).retract(); 5455 return xor(cpy).quasiRandomRegion(1.0 - fractionKept).or(cpy); 5456 } 5457 5458 /** 5459 * Modifies this GreasedRegion so it contains a random subset of its previous contents, choosing cells so that the 5460 * distance between any two "on" cells is at least {@code minimumDistance}, with at least one cell as "on" if any 5461 * were "on" in this originally. Does not limit the count of "on" cells in the result. 5462 * @param rng used to generate random positions 5463 * @param minimumDistance the minimum distance between "on" cells in the result 5464 * @return this for chaining 5465 */ 5466 public GreasedRegion randomScatter(IRNG rng, int minimumDistance) { 5467 return randomScatter(rng, minimumDistance, -1); 5468 } 5469 /** 5470 * Modifies this GreasedRegion so it contains a random subset of its previous contents, choosing cells so that the 5471 * distance between any two "on" cells is at least {@code minimumDistance}, with at least one cell as "on" if any 5472 * were "on" in this originally. 5473 * Restricts the total count of "on" cells after this returns to a maximum of {@code limit} (minimum is 0 if no 5474 * cells are "on"). If limit is negative, this will not restrict the count. 5475 * @param rng used to generate random positions 5476 * @param minimumDistance the minimum distance between "on" cells in the result 5477 * @param limit the maximum count of "on" cells to keep 5478 * @return this for chaining 5479 */ 5480 public GreasedRegion randomScatter(IRNG rng, int minimumDistance, int limit) { 5481 GreasedRegion result = this; 5482 int tmp, total = 0, ct; 5483 tally(); 5484 if (this.ct == 0) { 5485 } else if (limit == 0) { 5486 result = empty(); 5487 } else { 5488 if (limit < 0) 5489 limit = width * height; 5490 long t, w; 5491 long[] data2 = new long[data.length]; 5492 MAIN_LOOP: 5493 while (total < limit) { 5494 if (!tallied) 5495 tally(); 5496 tmp = rng.nextInt(this.ct); 5497 5498 for (int s = 0; s < ySections; s++) { 5499 for (int x = 0; x < width; x++) { 5500 if ((ct = counts[x * ySections + s]) > tmp) { 5501 t = data[x * ySections + s]; 5502 w = NumberTools.lowestOneBit(t); 5503 for (--ct; w != 0; ct--) { 5504 if (ct == tmp) { 5505 removeRectangle(x - minimumDistance, 5506 ((s << 6) | Long.numberOfTrailingZeros(w)) - minimumDistance, 5507 minimumDistance << 1 | 1, minimumDistance << 1 | 1); 5508 data2[x * ySections + s] |= w; 5509 ++total; 5510 continue MAIN_LOOP; 5511 } 5512 t ^= w; 5513 w = NumberTools.lowestOneBit(t); 5514 } 5515 } 5516 } 5517 } 5518 break; 5519 } 5520 data = data2; 5521 tallied = false; 5522 } 5523 return result; 5524 } 5525 5526 public double rateDensity() { 5527 double result = 0; 5528 double sz = height * width; 5529 if (sz != 0) { 5530 double onAmount = sz - size(), retractedOn = sz - copy().retract().size(); 5531 result = (onAmount + retractedOn) / (sz * 2.0); 5532 } 5533 return result; 5534 } 5535 public double rateRegularity() { 5536 double result = 0; 5537 GreasedRegion me2 = copy().surface8way(); 5538 double irregularCount = me2.size(); 5539 if (irregularCount != 0) { 5540 result = me2.remake(this).surface().size() / irregularCount; 5541 } 5542 return result; 5543 } 5544 5545 private static int median(int[] working, int start, int amount) { 5546 int result; 5547 Arrays.sort(working, start, start + amount); 5548 if ((amount & 1) == 0) { 5549 result = working[start + (amount >> 1) - 1] + working[start + (amount >> 1)] >>> 1; 5550 } else { 5551 result = working[start + (amount >> 1)]; 5552 } 5553 return result; 5554 } 5555 5556 /** 5557 * Calculates a perceptual hash for this GreasedRegion using a method that is only precise for some sizes of 5558 * GreasedRegion; it writes a result to into, and uses working as a temporary buffer. The lengths of into and 5559 * working should be related; if into is length 1, then working should be length 64, and though the hash won't be 5560 * very detailed, it will work well for images with width and height that are multiples of 8; if into is length 4, 5561 * then working should be length 256, and this will work with more detail on images that have width and height that 5562 * are multiples of 16. If working is null or is too small, then this won't reuse it and will allocate an 5563 * appropriately-sized array for internal use. 5564 * <br> 5565 * Ported from https://github.com/commonsmachinery/blockhash/blob/master/blockhash.c , which is MIT-licensed. 5566 * @param into should be a long array of length 1 or 4; the contents don't matter and this will be where output is written to 5567 * @param working should be an int array of length 64 (if into has length 1) or 256 (if into has length 4); may be null if you like garbage collection 5568 */ 5569 public void perceptualHashQuick(long[] into, int[] working) 5570 { 5571 final int bits = 8 << (Integer.numberOfTrailingZeros(Integer.highestOneBit(into.length)) >> 1); 5572 if(working == null || working.length < bits * bits) 5573 working = new int[bits * bits]; 5574 final int blockWidth = width / bits, blockHeight = height / bits, blockWidthSections = blockWidth * ySections; 5575 if(blockHeight == 1) 5576 { 5577 for (int y = 0; y < bits; y++) { 5578 for (int x = 0; x < bits; x++) { 5579 int value = 0; 5580 for (int ix = 0; ix < blockWidthSections; ix += ySections) { 5581 value += (data[x * blockWidthSections + ix + (y >> 6)] >>> (y & 63) & 1L); 5582 } 5583 working[x * bits + y] = value; 5584 } 5585 } 5586 } 5587 else if(blockHeight < 64 && Integer.bitCount(blockHeight) == 1) { 5588 final long yBlockMask = ~(-1L << blockHeight); 5589 final int divisorMask = (64 / blockHeight) - 1; 5590 long currentMask; 5591 int blockY = 0; 5592 for (int y = 0; y < bits; y++, blockY += blockHeight) { 5593 currentMask = yBlockMask << ((y & divisorMask) << blockHeight); 5594 for (int x = 0; x < bits; x++) { 5595 int value = 0; 5596 for (int ix = 0; ix < blockWidthSections; ix += ySections) { 5597 value += Long.bitCount(data[x * blockWidthSections + ix + (blockY >> 6)] & currentMask); 5598 } 5599 working[x * bits + y] = value; 5600 } 5601 } 5602 } 5603 final int cellsPerBlock = blockWidth * blockHeight, numBlocks = bits * bits, 5604 halfCellCount = cellsPerBlock >>> 1; 5605 int bandSize = numBlocks >>> 2; 5606 int m, v; 5607 int currentInto = 0; 5608 long currentIntoPos = 1L; 5609 for (int i = 0; i < 4; i++) { 5610 m = median(working, i * bandSize, bandSize); 5611 for (int j = i * bandSize; j < (i + 1) * bandSize; j++) { 5612 v = working[j]; 5613 if(v > m || (v - m == 0 && m > halfCellCount)) into[currentInto] |= currentIntoPos; 5614 if((currentIntoPos <<= 1) == 0) 5615 { 5616 ++currentInto; 5617 currentIntoPos = 1L; 5618 } 5619 } 5620 } 5621 } 5622 5623 /* 5624 // This showed a strong x-y correlation because it didn't have a way to use a non-base-2 van der Corput sequence. 5625 // It also produced very close-together points, unfortunately. 5626 public static double quasiRandomX(int idx) 5627 { 5628 return atVDCSequence(26 + idx * 5); 5629 } 5630 public static double quasiRandomY(int idx) 5631 { 5632 return atVDCSequence(19 + idx * 3); 5633 } 5634 5635 private static double atVDCSequence(int idx) 5636 { 5637 int leading = Integer.numberOfLeadingZeros(idx); 5638 return (Integer.reverse(idx) >>> leading) / (1.0 * (1 << (32 - leading))); 5639 } 5640 */ 5641 public Coord[] asCoords() 5642 { 5643 return asCoords(new Coord[size()]); 5644 5645 } 5646 public Coord[] asCoords(Coord[] points) { 5647 Coord[] result = null; 5648 if (points == null) 5649 points = new Coord[size()]; 5650 int idx = 0, len = points.length; 5651 long t, w; 5652 for (int x = 0; x < width; x++) { 5653 for (int s = 0; s < ySections; s++) { 5654 if ((t = data[x * ySections + s]) != 0) { 5655 w = NumberTools.lowestOneBit(t); 5656 while (w != 0) { 5657 if (idx >= len) { 5658 result = points; 5659 break; 5660 } 5661 points[idx++] = Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 5662 t ^= w; 5663 w = NumberTools.lowestOneBit(t); 5664 } 5665 if (result != null) break; 5666 } 5667 } 5668 if (result != null) break; 5669 } 5670 if (result == null) { 5671 result = points; 5672 } 5673 return result; 5674 } 5675 public int[] asEncoded() 5676 { 5677 int ct = size(), idx = 0; 5678 int[] points = new int[ct]; 5679 long t, w; 5680 for (int x = 0; x < width; x++) { 5681 for (int s = 0; s < ySections; s++) { 5682 if((t = data[x * ySections + s]) != 0) 5683 { 5684 w = NumberTools.lowestOneBit(t); 5685 while (w != 0) { 5686 points[idx++] = Coord.pureEncode(x, (s << 6) | Long.numberOfTrailingZeros(w)); 5687 t ^= w; 5688 w = NumberTools.lowestOneBit(t); 5689 } 5690 } 5691 } 5692 } 5693 return points; 5694 } 5695 public int[] asTightEncoded() 5696 { 5697 int ct = size(), idx = 0; 5698 int[] points = new int[ct]; 5699 long t, w; 5700 for (int x = 0; x < width; x++) { 5701 for (int s = 0; s < ySections; s++) { 5702 if((t = data[x * ySections + s]) != 0) 5703 { 5704 w = NumberTools.lowestOneBit(t); 5705 while (w != 0) { 5706 points[idx++] = ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x; 5707 t ^= w; 5708 w = NumberTools.lowestOneBit(t); 5709 } 5710 } 5711 } 5712 } 5713 return points; 5714 } 5715 5716 /** 5717 * @return All cells in this zone. 5718 */ 5719 @Override 5720 public List<Coord> getAll() { 5721 ArrayList<Coord> points = new ArrayList<>(); 5722 long t, w; 5723 for (int x = 0; x < width; x++) { 5724 for (int s = 0; s < ySections; s++) { 5725 if((t = data[x * ySections + s]) != 0) 5726 { 5727 w = NumberTools.lowestOneBit(t); 5728 while (w != 0) { 5729 points.add(Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w))); 5730 t ^= w; 5731 w = NumberTools.lowestOneBit(t); 5732 } 5733 } 5734 } 5735 } 5736 return points; 5737 5738 } 5739 5740 /** 5741 * Gets the first Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty. 5742 * @return the first Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty 5743 */ 5744 public Coord first() 5745 { 5746 long w; 5747 for (int x = 0; x < width; x++) { 5748 for (int s = 0; s < ySections; s++) { 5749 if ((w = (data[x * ySections + s])) != 0) { 5750 return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 5751 } 5752 } 5753 } 5754 return Coord.get(-1, -1); 5755 } 5756 5757 public int firstTight() 5758 { 5759 long w; 5760 for (int x = 0; x < width; x++) { 5761 for (int s = 0; s < ySections; s++) { 5762 if ((w = (data[x * ySections + s])) != 0) { 5763 return ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x; 5764 } 5765 } 5766 } 5767 return -1; 5768 } 5769 5770 /** 5771 * Gets the last Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty. 5772 * @return the last Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty 5773 */ 5774 public Coord last() 5775 { 5776 long w; 5777 for (int x = width - 1; x >= 0; x--) { 5778 for (int s = ySections - 1; s >= 0; s--) { 5779 if ((w = (data[x * ySections + s])) != 0) { 5780 return Coord.get(x, (s << 6) | 63 - Long.numberOfLeadingZeros(w)); 5781 } 5782 } 5783 } 5784 return Coord.get(-1, -1); 5785 } 5786 5787 public int lastTight() 5788 { 5789 long w; 5790 for (int x = width - 1; x >= 0; x--) { 5791 for (int s = ySections - 1; s >= 0; s--) { 5792 if ((w = (data[x * ySections + s])) != 0) { 5793 return ((s << 6) | 63 - Long.numberOfLeadingZeros(w)) * width + x; 5794 } 5795 } 5796 } 5797 return -1; 5798 } 5799 5800 public Coord nth(final int index) 5801 { 5802 if(index < 0) 5803 return Coord.get(-1, -1); 5804 int ct = size(), tmp; 5805 if(index >= ct) 5806 return Coord.get(-1, -1); 5807 long t, w; 5808 for (int s = 0; s < ySections; s++) { 5809 for (int x = 0; x < width; x++) { 5810 if ((ct = counts[x * ySections + s]) > index) { 5811 t = data[x * ySections + s]; 5812 w = NumberTools.lowestOneBit(t); 5813 for (--ct; w != 0; ct--) { 5814 if (ct == index) 5815 return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 5816 t ^= w; 5817 w = NumberTools.lowestOneBit(t); 5818 } 5819 } 5820 } 5821 } 5822 return Coord.get(-1, -1); 5823 } 5824 5825 public Coord atFraction(final double fraction) 5826 { 5827 int ct = size(), tmp; 5828 if(ct <= 0) return Coord.get(-1, -1); 5829 tmp = Math.abs((int)(fraction * ct) % ct); 5830 long t, w; 5831 for (int s = 0; s < ySections; s++) { 5832 for (int x = 0; x < width; x++) { 5833 if ((ct = counts[x * ySections + s]) > tmp) { 5834 t = data[x * ySections + s]; 5835 w = NumberTools.lowestOneBit(t); 5836 for (--ct; w != 0; ct--) { 5837 if (ct == tmp) 5838 return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 5839 t ^= w; 5840 w = NumberTools.lowestOneBit(t); 5841 } 5842 } 5843 } 5844 } 5845 return Coord.get(-1, -1); 5846 } 5847 5848 public int atFractionTight(final double fraction) 5849 { 5850 int ct = size(), tmp; 5851 if(ct <= 0) return -1; 5852 tmp = Math.abs((int)(fraction * ct) % ct); 5853 long t, w; 5854 5855 for (int x = 0; x < width; x++) { 5856 for (int s = 0; s < ySections; s++) { 5857 if ((ct = counts[x * ySections + s]) > tmp) { 5858 t = data[x * ySections + s]; 5859 w = NumberTools.lowestOneBit(t); 5860 for (--ct; w != 0; ct--) { 5861 if (ct == tmp) 5862 return ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x; 5863 t ^= w; 5864 w = NumberTools.lowestOneBit(t); 5865 } 5866 } 5867 } 5868 } 5869 return -1; 5870 } 5871 5872 /** 5873 * Gets a single random Coord from the "on" positions in this GreasedRegion, or the Coord (-1,-1) if this is empty. 5874 * Uses the given IRNG to generate one random int, which is used as an index. The technique this uses to iterate 5875 * over bits can be credited to Erling Ellingsen and Daniel Lemire, found 5876 * <a href="https://lemire.me/blog/2013/12/23/even-faster-bitmap-decoding/">here</a>, and seems to be a little 5877 * faster than the previous method. The fastest way to get a random Coord from a GreasedRegion is to avoid iterating 5878 * over the bits at all, so if your region data doesn't change you should get it as a Coord array with 5879 * {@link #asCoords()} and call {@link IRNG#getRandomElement(Object[])} with that array as the parameter. If you 5880 * take the asCoords() call out of consideration, getting random elements out of an array (especially a large one) 5881 * can be hundreds of times faster. 5882 * @param rng an IRNG such as an {@link RNG} or {@link GWTRNG} 5883 * @return a single randomly-chosen Coord from the "on" positions in this GreasedRegion, or (-1,-1) if empty 5884 */ 5885 public Coord singleRandom(IRNG rng) 5886 { 5887 int ct = size(), tmp = rng.nextInt(ct); 5888 long t, w; 5889 for (int s = 0; s < ySections; s++) { 5890 for (int x = 0; x < width; x++) { 5891 if ((ct = counts[x * ySections + s]) > tmp) { 5892 t = data[x * ySections + s] | 0L; 5893 for (--ct; t != 0; ct--) { 5894 w = NumberTools.lowestOneBit(t); 5895 if (ct == tmp) 5896 return Coord.get(x, (s << 6) | Long.bitCount(w-1)); 5897 t ^= w; 5898 } 5899 } 5900 } 5901 } 5902 return Coord.get(-1, -1); 5903 } 5904 5905 public int singleRandomTight(IRNG rng) 5906 { 5907 int ct = size(), tmp = rng.nextInt(ct); 5908 long t, w; 5909 for (int s = 0; s < ySections; s++) { 5910 for (int x = 0; x < width; x++) { 5911 if ((ct = counts[x * ySections + s]) > tmp) { 5912 t = data[x * ySections + s]; 5913 w = NumberTools.lowestOneBit(t); 5914 for (--ct; w != 0; ct--) { 5915 if (ct == tmp) 5916 return ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x; 5917 t ^= w; 5918 w = NumberTools.lowestOneBit(t); 5919 } 5920 } 5921 } 5922 } 5923 5924 return -1; 5925 } 5926 5927 /** 5928 * Narrow-purpose; takes an x and a y value, each between 0 and 65535 inclusive, and interleaves their bits so the 5929 * least significant bit and every other bit after it are filled with the bits of x, while the 5930 * second-least-significant bit and every other bit after that are filled with the bits of y. Essentially, this 5931 * takes two numbers with bits labeled like {@code a b c} for x and {@code R S T} for y and makes a number with 5932 * those bits arranged like {@code R a S b T c}. 5933 * @param x an int between 0 and 65535, inclusive 5934 * @param y an int between 0 and 65535, inclusive 5935 * @return an int that interleaves x and y, with x in the least significant bit position 5936 */ 5937 public static int interleaveBits(int x, int y) 5938 { 5939 x |= y << 16; 5940 x = ((x & 0x0000ff00) << 8) | ((x >>> 8) & 0x0000ff00) | (x & 0xff0000ff); 5941 x = ((x & 0x00f000f0) << 4) | ((x >>> 4) & 0x00f000f0) | (x & 0xf00ff00f); 5942 x = ((x & 0x0c0c0c0c) << 2) | ((x >>> 2) & 0x0c0c0c0c) | (x & 0xc3c3c3c3); 5943 return ((x & 0x22222222) << 1) | ((x >>> 1) & 0x22222222) | (x & 0x99999999); 5944 } 5945 5946 /** 5947 * Narrow-purpose; takes an int that represents a distance down the Z-order curve and moves its bits around so that 5948 * its x component is stored in the bottom 16 bits (use {@code (n & 0xffff)} to obtain) and its y component is 5949 * stored in the upper 16 bits (use {@code (n >>> 16)} to obtain). This may be useful for ordering traversals of all 5950 * points in a GreasedRegion less predictably. 5951 * @param n an int that has already been interleaved, though this can really be any int 5952 * @return an int with x in its lower bits ({@code x = n & 0xffff;}) and y in its upper bits ({@code y = n >>> 16;}) 5953 */ 5954 public static int disperseBits(int n) 5955 { 5956 n = ((n & 0x22222222) << 1) | ((n >>> 1) & 0x22222222) | (n & 0x99999999); 5957 n = ((n & 0x0c0c0c0c) << 2) | ((n >>> 2) & 0x0c0c0c0c) | (n & 0xc3c3c3c3); 5958 n = ((n & 0x00f000f0) << 4) | ((n >>> 4) & 0x00f000f0) | (n & 0xf00ff00f); 5959 return ((n & 0x0000ff00) << 8) | ((n >>> 8) & 0x0000ff00) | (n & 0xff0000ff); 5960 } 5961 private static int nextPowerOfTwo(int n) 5962 { 5963 final int highest = Integer.highestOneBit(n); 5964 return (highest == NumberTools.lowestOneBit(n)) ? highest : highest << 1; 5965 } 5966 5967 /** 5968 * Like {@link #nth(int)}, this gets the Coord at a given index along a path through the GreasedRegion, but unlike 5969 * nth(), this traverses the path in a zig-zag pattern called the Z-Order Curve. This method is often not very fast 5970 * compared to nth(), but this different path can help if iteration needs to seem less regular while still covering 5971 * all "on" cells in the GresedRegion eventually. 5972 * @param index the distance along the Z-order curve to travel, only counting "on" cells in this GreasedRegion. 5973 * @return the Coord at the given distance, or the Coord with x and y both -1 if index is too high or low 5974 */ 5975 public Coord nthZCurve(final int index) 5976 { 5977 int ct = -1, x, y, s, d, max = nextPowerOfTwo(width) * nextPowerOfTwo(height); 5978 long t, w; 5979 for (int o = 0; o < max; o++) { 5980 d = disperseBits(o); 5981 x = d & 0xffff; 5982 y = d >>> 16; 5983 if(x >= width && y >= height) 5984 break; 5985 if(x >= width) 5986 continue; 5987 if(y >= height) 5988 continue; 5989 s = d >>> 22; 5990 t = data[x * ySections + s]; 5991 if((ct += (t >>> (y & 63) & 1L)) == index) 5992 return Coord.get(x, y); 5993 } 5994 return Coord.get(-1, -1); 5995 } 5996 /** 5997 * Like {@link #nth(int)}, this finds a given index along a path through the GreasedRegion, but unlike nth(), this 5998 * traverses the path in a zig-zag pattern called the Z-Order Curve, and unlike {@link #nthZCurve(int)}, this does 5999 * not return a Coord and instead produces a "tight"-encoded int. This method is often not very fast compared to 6000 * nth(), but this different path can help if iteration needs to seem less regular while still covering all "on" 6001 * cells in the GreasedRegion eventually, and the tight encoding may be handy if you need to use ints. 6002 * @param index the distance along the Z-order curve to travel, only counting "on" cells in this GreasedRegion. 6003 * @return the "tight" encoded point at the given distance, or -1 if index is too high or low 6004 */ 6005 6006 public int nthZCurveTight(final int index) 6007 { 6008 int ct = -1, x, y, s, d, max = nextPowerOfTwo(width) * nextPowerOfTwo(height); 6009 long t; 6010 for (int o = 0; o < max; o++) { 6011 d = disperseBits(o); 6012 x = d & 0xffff; 6013 y = d >>> 16; 6014 if(x >= width && y >= height) 6015 break; 6016 if(x >= width) 6017 continue; 6018 if(y >= height) 6019 continue; 6020 s = d >>> 22; 6021 t = data[x * ySections + s]; 6022 if((ct += (t >>> (y & 63) & 1L)) == index) 6023 return y * width + x; 6024 } 6025 return -1; 6026 } 6027 6028 /** 6029 * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells 6030 * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6031 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 6032 * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with 6033 * {@link #mixedRandomSeparated(double, int)}, which tends to overlap more frequently. This method seems to work 6034 * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler 6035 * approach mixedRandomSeparated uses to traverse points (which just runs through the "on" cells a column at a time, 6036 * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon 6037 * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomSeparated has 6038 * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by 6039 * {@link #quasiRandomSeparated(double)} has it overlapping at the same rate as this method, though it should be 6040 * much faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that are 6041 * outside the GreasedRegion but are on the Z-Order Curve. 6042 * Does not restrict the size of the returned array other than only using up to {@code fraction * size()} cells. 6043 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6044 * @return a freshly-allocated Coord array containing the quasi-random cells 6045 */ 6046 public Coord[] separatedZCurve(double fraction) 6047 { 6048 return separatedZCurve(fraction, -1); 6049 } 6050 6051 /** 6052 * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells 6053 * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6054 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 6055 * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with 6056 * {@link #mixedRandomSeparated(double, int)}, which tends to overlap more frequently. This method seems to work 6057 * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler 6058 * approach mixedRandomSeparated uses to traverse points (which just runs through the "on" cells a column at a time, 6059 * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon 6060 * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomSeparated has 6061 * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by 6062 * {@link #quasiRandomSeparated(double, int)} has it overlapping at the same rate as this method, though it should 6063 * be much faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that 6064 * are outside the GreasedRegion but are on the Z-Order Curve. 6065 * Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells are "on"). 6066 * If limit is negative, this will not restrict the size. 6067 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6068 * @param limit the maximum size of the array to return 6069 * @return a freshly-allocated Coord array containing the quasi-random cells 6070 */ 6071 public Coord[] separatedZCurve(double fraction, int limit) 6072 { 6073 if(fraction < 0) 6074 return new Coord[0]; 6075 if(fraction > 1) 6076 fraction = 1; 6077 int ct = size(), total; 6078 total = ct; 6079 ct *= fraction; 6080 if(limit >= 0 && limit < ct) 6081 ct = limit; 6082 Coord[] vl = new Coord[ct]; 6083 for (int i = 0; i < ct; i++) 6084 { 6085 vl[i] = nthZCurve((int) (VanDerCorputQRNG.determine2(i ^ i >>> 1) * total)); 6086 } 6087 return vl; 6088 } 6089 /** 6090 * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that 6091 * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6092 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 6093 * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with 6094 * {@link #mixedRandomRegion(double)}, which tends to overlap more frequently. This method seems to work 6095 * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler 6096 * approach mixedRandomRegion uses to traverse points (which just runs through the "on" cells a column at a time, 6097 * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon 6098 * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomRegion has 6099 * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by 6100 * {@link #quasiRandomRegion(double)} has it overlapping at the same rate as this method, though it should be much 6101 * faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that are 6102 * outside the GreasedRegion but are on the Z-Order Curve. 6103 * Does not restrict the size of the returned array other than only using up to {@code fraction * size()} cells. 6104 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6105 * @return this, after modifications, for chaining 6106 */ 6107 public GreasedRegion separatedRegionZCurve(double fraction) 6108 { 6109 return separatedRegionZCurve(fraction, -1); 6110 } 6111 6112 /** 6113 * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that 6114 * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6115 * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you 6116 * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with 6117 * {@link #mixedRandomRegion(double, int)}, which tends to overlap more frequently. This method seems to work 6118 * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler 6119 * approach mixedRandomRegion uses to traverse points (which just runs through the "on" cells a column at a time, 6120 * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon 6121 * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomRegion has 6122 * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by 6123 * {@link #quasiRandomRegion(double, int)} has it overlapping at the same rate as this method, though it should be 6124 * much faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that 6125 * are outside the GreasedRegion but are on the Z-Order Curve. 6126 * Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells are "on"). 6127 * If limit is negative, this will not restrict the size. 6128 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6129 * @param limit the maximum size of the array to return 6130 * @return this, after modifications, for chaining 6131 */ 6132 public GreasedRegion separatedRegionZCurve(double fraction, int limit) 6133 { 6134 if(fraction <= 0) 6135 return empty(); 6136 if(fraction >= 1) 6137 return this; 6138 int ct = size(), total; 6139 if(limit >= ct) 6140 return this; 6141 total = ct; 6142 ct *= fraction; 6143 if(limit >= 0 && limit < ct) 6144 ct = limit; 6145 int[] vl = new int[ct]; 6146 for (int i = 0; i < ct; i++) 6147 { 6148 vl[i] = nthZCurveTight((int) (VanDerCorputQRNG.determine2(i ^ i >>> 1) * total)); 6149 } 6150 return empty().insertSeveral(vl); 6151 } 6152 6153 /** 6154 * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells 6155 * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6156 * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being 6157 * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to 6158 * have nearby cells, however. Does not restrict the size of the returned array other than only using up to 6159 * {@code fraction * size()} cells. 6160 * <br> 6161 * Also take a look at {@link #separatedZCurve(double, int)}, {@link #quasiRandomSeparated(double, int)}, 6162 * {@link #mixedRandomSeparated(double, int, long)}, and {@link #separatedRegionBlue(double, int)}. Internally, this 6163 * is a thin wrapper around {@link #separatedRegionBlue(double, int)}, and won't be more efficient than that method. 6164 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6165 * @return a freshly-allocated Coord array containing the quasi-random cells 6166 */ 6167 public Coord[] separatedBlue(double fraction) 6168 { 6169 return separatedBlue(fraction, -1); 6170 } 6171 6172 /** 6173 * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells 6174 * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6175 * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being 6176 * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to 6177 * have nearby cells, however. Restricts the total size of the returned array to a maximum of {@code limit} (minimum 6178 * is 0 if no cells are "on"). If limit is negative, this will not restrict the size. 6179 * <br> 6180 * Also take a look at {@link #separatedZCurve(double, int)}, {@link #quasiRandomSeparated(double, int)}, 6181 * {@link #mixedRandomSeparated(double, int, long)}, and {@link #separatedRegionBlue(double, int)}. Internally, this 6182 * is a thin wrapper around {@link #separatedRegionBlue(double, int)}, and won't be more efficient than that method. 6183 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6184 * @param limit the maximum size of the array to return; may return less 6185 * @return a freshly-allocated Coord array containing the quasi-random cells 6186 */ 6187 public Coord[] separatedBlue(double fraction, int limit) 6188 { 6189 if(fraction <= 0) 6190 return new Coord[0]; 6191 if(fraction > 0xFFp-8) 6192 fraction = 0xFFp-8; 6193 int ct = size(); 6194 if(limit >= ct) 6195 return asCoords(); 6196 return copy().separatedRegionBlue(fraction, limit).asCoords(); 6197 } 6198 /** 6199 * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that 6200 * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6201 * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being 6202 * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to 6203 * have nearby cells, however. Does not restrict the size of the returned GreasedRegion other than only using up to 6204 * {@code fraction * size()} cells. 6205 * <br> 6206 * Also take a look at {@link #separatedRegionZCurve(double, int)}, {@link #quasiRandomRegion(double, int)}, 6207 * {@link #mixedRandomRegion(double, int, long)}, and {@link #separatedBlue(double, int)}. 6208 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 6209 * @return this, after modifications, for chaining 6210 */ 6211 public GreasedRegion separatedRegionBlue(double fraction) 6212 { 6213 return separatedRegionBlue(fraction, -1); 6214 } 6215 6216 /** 6217 * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that 6218 * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi- 6219 * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being 6220 * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to 6221 * have nearby cells, however. Restricts the total size of the returned GreasedRegion to a maximum of {@code limit} 6222 * (minimum is 0 if no cells are "on"). If limit is negative, this will not restrict the size. 6223 * <br> 6224 * Also take a look at {@link #separatedRegionZCurve(double, int)}, {@link #quasiRandomRegion(double, int)}, 6225 * {@link #mixedRandomRegion(double, int, long)}, and {@link #separatedBlue(double, int)}. 6226 * @param fraction the fraction of "on" cells to quasi-randomly select, between 0.0 and 1.0 6227 * @param limit the maximum size of the array to return; may return less 6228 * @return this, after modifications, for chaining 6229 */ 6230 public GreasedRegion separatedRegionBlue(double fraction, int limit) 6231 { 6232 if(fraction <= 0) 6233 return empty(); 6234 if(fraction > 0xFFp-8) 6235 fraction = 0xFFp-8; 6236 int ct = size(); 6237 if(limit >= ct) 6238 return this; 6239 ct *= fraction; 6240 if(limit >= 0 && limit < ct) 6241 ct = limit; 6242 for (int i = 255; i >= 0; i--) { 6243 if(ct >= andWrapping64(BlueNoise.LEVELS[i]).size()) 6244 return this; 6245 } 6246 return this; 6247 } 6248 6249 public Coord[] randomPortion(IRNG rng, int size) 6250 { 6251 int ct = size(), idx = 0, run = 0; 6252 if(ct <= 0 || size <= 0) 6253 return new Coord[0]; 6254 if(ct <= size) 6255 return asCoords(); 6256 Coord[] points = new Coord[size]; 6257 int[] order = rng.randomOrdering(ct); 6258 Arrays.sort(order, 0, size); 6259 long t, w; 6260 ALL: 6261 for (int s = 0; s < ySections; s++) { 6262 for (int x = 0; x < width; x++) { 6263 if((t = data[x * ySections + s]) != 0) 6264 { 6265 w = NumberTools.lowestOneBit(t); 6266 while (w != 0) { 6267 if (run++ == order[idx]) { 6268 points[idx++] = Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 6269 if (idx >= size) break ALL; 6270 } 6271 t ^= w; 6272 w = NumberTools.lowestOneBit(t); 6273 } 6274 } 6275 } 6276 } 6277 return points; 6278 } 6279 6280 public GreasedRegion randomRegion(IRNG rng, int size) 6281 { 6282 int ct = size(), idx = 0, run = 0; 6283 if(ct <= 0 || size <= 0) 6284 return empty(); 6285 if(ct <= size) 6286 return this; 6287 int[] order = rng.randomOrdering(ct); 6288 Arrays.sort(order, 0, size); 6289 long t, w; 6290 ALL: 6291 for (int s = 0; s < ySections; s++) { 6292 for (int x = 0; x < width; x++) { 6293 if((t = data[x * ySections + s]) != 0) 6294 { 6295 w = NumberTools.lowestOneBit(t); 6296 while (w != 0) { 6297 if (run++ == order[idx]) { 6298 if(++idx >= size) break ALL; 6299 } 6300 else 6301 { 6302 data[x * ySections + s] &= ~(1L << Long.numberOfTrailingZeros(w)); 6303 } 6304 t ^= w; 6305 w = NumberTools.lowestOneBit(t); 6306 } 6307 } 6308 } 6309 } 6310 tallied = false; 6311 return this; 6312 } 6313 6314 @Override 6315 public boolean contains(int x, int y) 6316 { 6317 return x >= 0 && y >= 0 && x < width && y < height && ySections > 0 && 6318 ((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0); 6319 } 6320 6321 6322 /** 6323 * @return Whether this zone is empty. 6324 */ 6325 @Override 6326 public boolean isEmpty() { 6327 if(tallied) return ct > 0; 6328 for (int i = 0; i < data.length; i++) { 6329 if(data[i] != 0L) return false; 6330 } 6331 return true; 6332 } 6333 6334 /** 6335 * Generates a 2D int array from an array or vararg of GreasedRegions, starting at all 0 and adding 1 to the int at 6336 * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to 6337 * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can 6338 * produce any number between 0 and 16 in a cell. 6339 * @param regions an array or vararg of GreasedRegions; must all have the same width and height 6340 * @return a 2D int array with the same width and height as the regions, where an int cell equals the number of given GreasedRegions that had an "on" cell at that position 6341 */ 6342 public static int[][] sum(GreasedRegion... regions) 6343 { 6344 if(regions == null || regions.length <= 0) 6345 return new int[0][0]; 6346 int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections; 6347 int[][] numbers = new int[w][h]; 6348 for (int x = 0; x < w; x++) { 6349 for (int y = 0; y < h; y++) { 6350 for (int i = 0; i < l; i++) { 6351 numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0; 6352 } 6353 } 6354 } 6355 return numbers; 6356 } 6357 6358 /** 6359 * Generates a 2D int array from a List of GreasedRegions, starting at all 0 and adding 1 to the int at 6360 * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to 6361 * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can 6362 * produce any number between 0 and 16 in a cell. 6363 * @param regions a List of GreasedRegions; must all have the same width and height 6364 * @return a 2D int array with the same width and height as the regions, where an int cell equals the number of given GreasedRegions that had an "on" cell at that position 6365 */ 6366 public static int[][] sum(List<GreasedRegion> regions) 6367 { 6368 if(regions == null || regions.isEmpty()) 6369 return new int[0][0]; 6370 GreasedRegion t = regions.get(0); 6371 int w = t.width, h = t.height, l = regions.size(), ys = t.ySections; 6372 int[][] numbers = new int[w][h]; 6373 for (int x = 0; x < w; x++) { 6374 for (int y = 0; y < h; y++) { 6375 for (int i = 0; i < l; i++) { 6376 numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0; 6377 } 6378 } 6379 } 6380 return numbers; 6381 } 6382 6383 /** 6384 * Generates a 2D double array from an array or vararg of GreasedRegions, starting at all 0 and adding 1 to the double at 6385 * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to 6386 * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can 6387 * produce any number between 0 and 16 in a cell. 6388 * @param regions an array or vararg of GreasedRegions; must all have the same width and height 6389 * @return a 2D double array with the same width and height as the regions, where an double cell equals the number of given GreasedRegions that had an "on" cell at that position 6390 */ 6391 public static double[][] sumDouble(GreasedRegion... regions) 6392 { 6393 if(regions == null || regions.length <= 0) 6394 return new double[0][0]; 6395 int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections; 6396 double[][] numbers = new double[w][h]; 6397 for (int x = 0; x < w; x++) { 6398 for (int y = 0; y < h; y++) { 6399 for (int i = 0; i < l; i++) { 6400 numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1.0 : 0.0; 6401 } 6402 } 6403 } 6404 return numbers; 6405 } 6406 6407 /** 6408 * Generates a 2D double array from a List of GreasedRegions, starting at all 0 and adding 1 to the double at 6409 * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to 6410 * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can 6411 * produce any number between 0 and 16 in a cell. 6412 * @param regions a List of GreasedRegions; must all have the same width and height 6413 * @return a 2D double array with the same width and height as the regions, where an double cell equals the number of given GreasedRegions that had an "on" cell at that position 6414 */ 6415 public static double[][] sumDouble(List<GreasedRegion> regions) 6416 { 6417 if(regions == null || regions.isEmpty()) 6418 return new double[0][0]; 6419 GreasedRegion t = regions.get(0); 6420 int w = t.width, h = t.height, l = regions.size(), ys = t.ySections; 6421 double[][] numbers = new double[w][h]; 6422 for (int x = 0; x < w; x++) { 6423 for (int y = 0; y < h; y++) { 6424 for (int i = 0; i < l; i++) { 6425 numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1.0 : 0.0; 6426 } 6427 } 6428 } 6429 return numbers; 6430 } 6431 6432 /** 6433 * Generates a 2D int array from an array of GreasedRegions and an array of weights, starting the 2D result at all 0 6434 * and, for every GreasedRegion that has that cell as "on," adding the int in the corresponding weights array at 6435 * the position of that cell. This means if you give an array of 4 GreasedRegions to this method along with the 6436 * weights {@code 1, 2, 3, 4}, it can produce a number between 0 and 10 in a cell (where 10 is used when all 4 6437 * GreasedRegions have a cell "on," since {@code 1 + 2 + 3 + 4 == 10}); if the weights are instead 6438 * {@code 1, 10, 100, 1000}, then the results can vary between 0 and 1111, where 1111 is only if all GreasedRegions 6439 * have a cell as "on." The weights array must have a length at least equal to the length of the regions array. 6440 * @param regions an array of GreasedRegions; must all have the same width and height 6441 * @param weights an array of ints; must have length at least equal to regions' length 6442 * @return a 2D int array with the same width and height as the regions, where an int cell equals the sum of the weights corresponding to GreasedRegions that had an "on" cell at that position 6443 */ 6444 public static int[][] sumWeighted(GreasedRegion[] regions, int[] weights) 6445 { 6446 if(regions == null || regions.length <= 0 || weights == null || weights.length < regions.length) 6447 return new int[0][0]; 6448 int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections; 6449 int[][] numbers = new int[w][h]; 6450 for (int x = 0; x < w; x++) { 6451 for (int y = 0; y < h; y++) { 6452 for (int i = 0; i < l; i++) { 6453 numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? weights[i] : 0; 6454 } 6455 } 6456 } 6457 return numbers; 6458 } 6459 6460 /** 6461 * Generates a 2D double array from an array of GreasedRegions and an array of weights, starting the 2D result at 6462 * all 0 and, for every GreasedRegion that has that cell as "on," adding the double in the corresponding weights 6463 * array at the position of that cell. This means if you give an array of 4 GreasedRegions to this method along with 6464 * the weights {@code 1, 2, 3, 4}, it can produce a number between 0 and 10 in a cell (where 10 is used when all 4 6465 * GreasedRegions have a cell "on," since {@code 1 + 2 + 3 + 4 == 10}); if the weights are instead 6466 * {@code 1, 10, 100, 1000}, then the results can vary between 0 and 1111, where 1111 is only if all GreasedRegions 6467 * have a cell as "on." The weights array must have a length at least equal to the length of the regions array. 6468 * @param regions an array of GreasedRegions; must all have the same width and height 6469 * @param weights an array of doubles; must have length at least equal to regions' length 6470 * @return a 2D double array with the same width and height as the regions, where an double cell equals the sum of the weights corresponding to GreasedRegions that had an "on" cell at that position 6471 */ 6472 public static double[][] sumWeightedDouble(GreasedRegion[] regions, double[] weights) 6473 { 6474 if(regions == null || regions.length <= 0 || weights == null || weights.length < regions.length) 6475 return new double[0][0]; 6476 int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections; 6477 double[][] numbers = new double[w][h]; 6478 for (int x = 0; x < w; x++) { 6479 for (int y = 0; y < h; y++) { 6480 for (int i = 0; i < l; i++) { 6481 numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? weights[i] : 0.0; 6482 } 6483 } 6484 } 6485 return numbers; 6486 } 6487 6488 /** 6489 * Adds to an existing 2D int array with an array or vararg of GreasedRegions, adding 1 to the int in existing at 6490 * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to 6491 * this method, it can increment by any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can 6492 * increase the value in existing by any number between 0 and 16 in a cell. 6493 * @param existing a non-null 2D int array that will have each cell incremented by the sum of the GreasedRegions 6494 * @param regions an array or vararg of GreasedRegions; must all have the same width and height 6495 * @return existing, after modification, where an int cell will be changed by the number of given GreasedRegions that had an "on" cell at that position 6496 */ 6497 public static int[][] sumInto(int[][] existing, GreasedRegion... regions) 6498 { 6499 if(regions == null || regions.length <= 0 || existing == null || existing.length == 0 || existing[0].length == 0) 6500 return existing; 6501 int w = existing.length, h = existing[0].length, l = regions.length, ys; 6502 for (int i = 0; i < l; i++) { 6503 GreasedRegion region = regions[i]; 6504 ys = region.ySections; 6505 for (int x = 0; x < w && x < region.width; x++) { 6506 for (int y = 0; y < h && y < region.height; y++) { 6507 existing[x][y] += (region.data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0; 6508 } 6509 } 6510 } 6511 return existing; 6512 } 6513 6514 6515 /** 6516 * Adds to an existing 2D double array with an array or vararg of GreasedRegions, adding 1 to the double in existing 6517 * at a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to 6518 * this method, it can increment by any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can 6519 * increase the value in existing by any number between 0 and 16 in a cell. 6520 * @param existing a non-null 2D double array that will have each cell incremented by the sum of the GreasedRegions 6521 * @param regions an array or vararg of GreasedRegions; must all have the same width and height 6522 * @return existing, after modification, where a double cell will be changed by the number of given GreasedRegions that had an "on" cell at that position 6523 */ 6524 public static double[][] sumIntoDouble(double[][] existing, GreasedRegion... regions) 6525 { 6526 if(regions == null || regions.length <= 0 || existing == null || existing.length == 0 || existing[0].length == 0) 6527 return existing; 6528 int w = existing.length, h = existing[0].length, l = regions.length, ys = regions[0].ySections; 6529 for (int i = 0; i < l; i++) { 6530 for (int x = 0; x < w && x < regions[i].width; x++) { 6531 for (int y = 0; y < h && y < regions[i].height; y++) { 6532 existing[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1.0 : 0.0; 6533 } 6534 } 6535 } 6536 return existing; 6537 } 6538 6539 /** 6540 * Discouraged from active use; slower than {@link squidpony.squidai.DijkstraMap} and has less features. 6541 * @param map a 2D char array where '#' is a wall 6542 * @param goals an array or vararg of Coord to get the distances toward 6543 * @return a 2D double array of distances from a cell to the nearest goal 6544 */ 6545 public static double[][] dijkstraScan(char[][] map, Coord... goals) 6546 { 6547 if(map == null || map.length <= 0 || map[0].length <= 0 || goals == null || goals.length <= 0) 6548 return new double[0][0]; 6549 int w = map.length, h = map[0].length, ys = (h + 63) >>> 6; 6550 double[][] numbers = new double[w][h]; 6551 GreasedRegion walls = new GreasedRegion(map, '#'), floors = new GreasedRegion(walls).not(), 6552 middle = new GreasedRegion(w, h, goals).and(floors); 6553 ArrayList<GreasedRegion> regions = middle.floodSeriesToLimit(floors); 6554 int l = regions.size(); 6555 for (int x = 0; x < w; x++) { 6556 for (int y = 0; y < h; y++) { 6557 for (int i = 0; i < l; i++) { 6558 numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0; 6559 } 6560 } 6561 } 6562 return numbers; 6563 } 6564 /** 6565 * Discouraged from active use; slower than {@link squidpony.squidai.DijkstraMap} and has less features. 6566 * @param map a 2D char array where '#' is a wall 6567 * @param goals an array or vararg of Coord to get the distances toward 6568 * @return a 2D double array of distances from a cell to the nearest goal 6569 */ 6570 public static double[][] dijkstraScan8way(char[][] map, Coord... goals) 6571 { 6572 if(map == null || map.length <= 0 || map[0].length <= 0 || goals == null || goals.length <= 0) 6573 return new double[0][0]; 6574 int w = map.length, h = map[0].length, ys = (h + 63) >>> 6; 6575 double[][] numbers = new double[w][h]; 6576 GreasedRegion walls = new GreasedRegion(map, '#'), floors = new GreasedRegion(walls).not(), 6577 middle = new GreasedRegion(w, h, goals).and(floors); 6578 ArrayList<GreasedRegion> regions = middle.floodSeriesToLimit8way(floors); 6579 int l = regions.size(); 6580 for (int x = 0; x < w; x++) { 6581 for (int y = 0; y < h; y++) { 6582 for (int i = 0; i < l; i++) { 6583 numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0; 6584 } 6585 } 6586 } 6587 return numbers; 6588 } 6589 6590 /** 6591 * Generates a 2D int array from an array or vararg of GreasedRegions, treating each cell in the nth region as the 6592 * nth bit of the int at the corresponding x,y cell in the int array. This means if you give 8 GreasedRegions to 6593 * this method, it can produce any 8-bit number in a cell (0-255); if you give 16 GreasedRegions, then it can 6594 * produce any 16-bit number (0-65535). 6595 * @param regions an array or vararg of GreasedRegions; must all have the same width and height 6596 * @return a 2D int array with the same width and height as the regions, with bits per int taken from the regions 6597 */ 6598 public static int[][] bitSum(GreasedRegion... regions) 6599 { 6600 if(regions == null || regions.length <= 0) 6601 return new int[0][0]; 6602 int w = regions[0].width, h = regions[0].height, l = Math.min(32, regions.length), ys = regions[0].ySections; 6603 int[][] numbers = new int[w][h]; 6604 for (int x = 0; x < w; x++) { 6605 for (int y = 0; y < h; y++) { 6606 for (int i = 0; i < l; i++) { 6607 numbers[x][y] |= (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 << i : 0; 6608 } 6609 } 6610 } 6611 return numbers; 6612 } 6613 6614 /* 6615 public static int[][] selectiveNegate(int[][] numbers, GreasedRegion region, int mask) 6616 { 6617 if(region == null) 6618 return numbers; 6619 int w = region.width, h = region.height, ys = region.ySections; 6620 for (int x = 0; x < w; x++) { 6621 for (int y = 0; y < h; y++) { 6622 if((region.data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0) numbers[x][y] = (~numbers[x][y] & mask); 6623 } 6624 } 6625 return numbers; 6626 } 6627 */ 6628 6629 @Override 6630 public boolean equals(Object o) { 6631 if (this == o) return true; 6632 if (o == null || getClass() != o.getClass()) return false; 6633 6634 GreasedRegion that = (GreasedRegion) o; 6635 6636 if (height != that.height) return false; 6637 if (width != that.width) return false; 6638 if (ySections != that.ySections) return false; 6639 if (yEndMask != that.yEndMask) return false; 6640 return Arrays.equals(data, that.data); 6641 } 6642 6643 @Override 6644 public int hashCode() { 6645 return CrossHash.hash(data) ^ GWTRNG.determineInt(GWTRNG.determineInt(height) + width); 6646// long result = 0x1A976FDF6BF60B8EL, z = 0x60642E2A34326F15L; 6647// final int len = data.length; 6648// for (int i = 0; i < len; i++) { 6649// result ^= (z += (data[i] ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL); 6650// result = (result << 54 | result >>> 10); 6651// } 6652// result ^= (z += (height ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL); 6653// result = (result << 54 | result >>> 10); 6654// result ^= (z += (width ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL); 6655// result = (result << 54 | result >>> 10); 6656// 6657// result += (z ^ z >>> 26) * 0x632BE59BD9B4E019L; 6658// result = (result ^ result >>> 33) * 0xFF51AFD7ED558CCDL; 6659// return (int)((result ^ result >>> 33) * 0xC4CEB9FE1A85EC53L); 6660 } 6661 public long hash64() { 6662 return CrossHash.hash64(data) ^ DiverRNG.randomize(DiverRNG.randomize(height) + width); 6663// 6664// long result = 0x1A976FDF6BF60B8EL, z = 0x60642E2A34326F15L; 6665// final int len = data.length; 6666// for (int i = 0; i < len; i++) { 6667// result ^= (z += (data[i] ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL); 6668// result = (result << 54 | result >>> 10); 6669// } 6670// result ^= (z += (height ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL); 6671// result = (result << 54 | result >>> 10); 6672// result ^= (z += (width ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL); 6673// result = (result << 54 | result >>> 10); 6674// 6675// result += (z ^ z >>> 26) * 0x632BE59BD9B4E019L; 6676// result = (result ^ result >>> 33) * 0xFF51AFD7ED558CCDL; 6677// return ((result ^ result >>> 33) * 0xC4CEB9FE1A85EC53L); 6678 } 6679 6680 /** 6681 * Computes a 64-bit hash code of this GreasedRegion given a 64-bit seed; even if given two very similar seeds, 6682 * this should produce very different hash codes for the same GreasedRegion. 6683 * <br> 6684 * Meant for potential use in Bloom filters. Uses {@link CrossHash.Yolk}'s algorithm(s). 6685 * @param seed a seed that will determine how the hashing algorithm works; all 64 bits are used. 6686 * @return a 64-bit hash code for this GreasedRegion 6687 */ 6688 public long hash64(long seed) 6689 { 6690 seed += b1; 6691 seed ^= seed >>> 23 ^ seed >>> 48 ^ seed << 7 ^ seed << 53; 6692 long a = seed + b4, b = seed + b3, c = seed + b2, d = seed + b1; 6693 final int len = data.length; 6694 for (int i = 3; i < len; i+=4) { 6695 a ^= data[i-3] * b1; a = (a << 23 | a >>> 41) * b3; 6696 b ^= data[i-2] * b2; b = (b << 25 | b >>> 39) * b4; 6697 c ^= data[i-1] * b3; c = (c << 29 | c >>> 35) * b5; 6698 d ^= data[i ] * b4; d = (d << 31 | d >>> 33) * b1; 6699 seed += a + b + c + d; 6700 } 6701 seed += b5; 6702 switch (len & 3) { 6703 case 1: seed = wow(seed, b1 ^ data[len-1]); break; 6704 case 2: seed = wow(seed + data[len-2], b2 + data[len-1]); break; 6705 case 3: seed = wow(seed + data[len-3], b2 + data[len-2]) ^ wow(seed + data[len-1], seed ^ b3); break; 6706 } 6707 seed = wow(seed + height, b4 + width); 6708 seed = (seed ^ seed << 16) * (len ^ b0 ^ seed >>> 32); 6709 return seed - (seed >>> 31) + (seed << 33); 6710 6711 } 6712 6713 public String serializeToString() 6714 { 6715 return width + 6716 "," + height + 6717 "," + StringKit.joinAlt(",",data); 6718 } 6719 public static GreasedRegion deserializeFromString(String s) 6720 { 6721 if(s == null || s.isEmpty()) 6722 return null; 6723 int gap = s.indexOf(','), w = Integer.parseInt(s.substring(0, gap)), 6724 gap2 = s.indexOf(',', gap+1), h = Integer.parseInt(s.substring(gap+1, gap2)); 6725 String[] splits = StringKit.split(s.substring(gap2+1), ","); 6726 long[] data = new long[splits.length]; 6727 for (int i = 0; i < splits.length; i++) { 6728 data[i] = StringKit.longFromDec(splits[i]); 6729 } 6730 return new GreasedRegion(data, w, h); 6731 } 6732 6733 /** 6734 * Constructs a GreasedRegion using a vararg for data. Primarily meant for generated code, since 6735 * {@link #serializeToString()} produces a String that happens to be a valid parameter list for this method. 6736 * @param width width of the GreasedRegion to produce 6737 * @param height height of the GreasedRegion to produce 6738 * @param data array or vararg of long containing the exact data, probably from an existing GreasedRegion 6739 * @return a new GreasedRegion with the given width, height, and data 6740 */ 6741 public static GreasedRegion of(final int width, final int height, final long... data) 6742 { 6743 return new GreasedRegion(data, width, height); 6744 } 6745 6746 /** 6747 * Compresses this GreasedRegion into a UTF-16 String and returns the String without modifying this GreasedRegion. 6748 * Uses {@link CoordPacker}'s algorithm and data to compress this GreasedRegion in 256x128 blocks, storing the 6749 * CoordPacker-like data as chars with values from 256 to 33023 (a concept also used in {@link LZSEncoding}), 6750 * and using ASCII semicolons to separate them or store other info (just width and height, which are given first as 6751 * 16 hex digits). This finishes by running the result through {@link LZSEncoding}, a combination which typically 6752 * gets very good compression. 6753 * @return a String that could be used to reconstruct this GreasedRegion using {@link #decompress(String)} 6754 */ 6755 public String toCompressedString() 6756 { 6757 CoordPacker.init(); 6758 StringBuilder packing = new StringBuilder(width * height >> 3); 6759 StringKit.appendHex(packing, width); 6760 StringKit.appendHex(packing, height); 6761 final int chunksX = width + 255 >> 8, chunksY = height + 127 >> 7; 6762 for (int bigX = 0, baseX = 0; bigX < chunksX; bigX++, baseX += 256) { 6763 for (int bigY = 0, baseY = 0; bigY < chunksY; bigY++, baseY += 128) { 6764 packing.append(';'); 6765 boolean on = false, current; 6766 short skip = 0, hx, hy; 6767 int xSize = Math.min(256, width - baseX), ySize = Math.min(128, height - baseY), 6768 limit = 0x8000, mapLimit = xSize * ySize; 6769 if (xSize <= 128) { 6770 limit >>= 1; 6771 if (xSize <= 64) { 6772 limit >>= 1; 6773 if (ySize <= 64) { 6774 limit >>= 1; 6775 if (ySize <= 32) { 6776 limit >>= 1; 6777 if (xSize <= 32) { 6778 limit >>= 1; 6779 } 6780 } 6781 } 6782 } 6783 } 6784 for (int i = 0, ml = 0; i < limit && ml < mapLimit; i++, skip++) { 6785 hx = CoordPacker.hilbertX[i]; 6786 hy = CoordPacker.hilbertY[i]; 6787 if (hx >= xSize || hy >= ySize) { 6788 if (on) { 6789 on = false; 6790 packing.append((char) (skip + 256)); 6791 skip = 0; 6792 } 6793 continue; 6794 } 6795 ml++; 6796 current = ((data[(baseX + hx) * ySections + (baseY + hy >> 6)] & (1L << hy)) != 0); 6797 if (current != on) { 6798 packing.append((char) (skip + 256)); 6799 skip = 0; 6800 on = current; 6801 } 6802 } 6803 if (on) 6804 packing.append((char) (skip + 256)); 6805 } 6806 } 6807 return LZSEncoding.compressToUTF16(packing.toString()); 6808 } 6809 6810 /** 6811 * Decompresses a String returned by {@link #toCompressedString()}, returning a new GreasedRegion with identical 6812 * width, height, and contents to the GreasedRegion before compression. This decompresses the {@link LZSEncoding} 6813 * applied to the data, then decompresses the {@link CoordPacker}-type Hilbert Curve RLE data to get the original 6814 * GreasedRegion back. 6815 * @param compressed a String that was compressed by {@link #toCompressedString()}, without changes 6816 * @return a new copy of the GreasedRegion that was previously compressed 6817 */ 6818 public static GreasedRegion decompress(String compressed) 6819 { 6820 CoordPacker.init(); 6821 GreasedRegion target; 6822 compressed = LZSEncoding.decompressFromUTF16(compressed); 6823 final int width = StringKit.intFromHex(compressed), height = StringKit.intFromHex(compressed, 8, 16); 6824 target = new GreasedRegion(width, height); 6825 final int chunksX = width + 255 >> 8, chunksY = height + 127 >> 7; 6826 int startPack = 16, endPack, idx;//, hy; 6827 boolean on; 6828 for (int bigX = 0, baseX = 0; bigX < chunksX; bigX++, baseX += 256) { 6829 for (int bigY = 0, baseY = 0; bigY < chunksY; bigY++, baseY += 128) { 6830 ++startPack; 6831 endPack = compressed.indexOf(';', startPack); 6832 if(endPack < 0) endPack = compressed.length(); 6833 on = false; 6834 idx = 0; 6835 for(int p = startPack; p < endPack; p++, on = !on) { 6836 if (on) { 6837 for (int toSkip = idx + (compressed.charAt(p) - 256); idx < toSkip && idx < 0x8000; idx++) { 6838 target.insert(CoordPacker.hilbertX[idx] + baseX, CoordPacker.hilbertY[idx] + baseY); 6839 //hy = CoordPacker.hilbertY[idx] + baseY; 6840 //target.data[(CoordPacker.hilbertX[idx] + baseX) * target.ySections + (hy >> 6)] |= 1L << (hy & 63); 6841 } 6842 } else { 6843 idx += compressed.charAt(p) - 256; 6844 } 6845 } 6846 startPack = endPack; 6847 } 6848 } 6849// target.tallied = false; 6850 return target; 6851 } 6852 6853 @Override 6854 public boolean contains(Object o) { 6855 if(o instanceof Coord) 6856 return contains((Coord)o); 6857 return false; 6858 } 6859 6860 @Override 6861 public Iterator<Coord> iterator() { 6862 return new GRIterator(); 6863 } 6864 6865 @Override 6866 public Object[] toArray() { 6867 return asCoords(); 6868 } 6869 6870 @SuppressWarnings("unchecked") 6871 @Override 6872 public <T> T[] toArray(T[] a) { 6873 if(a instanceof Coord[]) 6874 return (T[])asCoords((Coord[])a); 6875 return a; 6876 } 6877 6878 @Override 6879 public boolean add(Coord coord) { 6880 if(contains(coord)) 6881 return false; 6882 insert(coord); 6883 return true; 6884 } 6885 @Override 6886 public void clear() 6887 { 6888 Arrays.fill(data, 0L); 6889 } 6890 6891 @Override 6892 public boolean remove(Object o) { 6893 if(o instanceof Coord) 6894 { 6895 if(contains((Coord)o)) 6896 { 6897 remove((Coord)o); 6898 return true; 6899 } 6900 return false; 6901 } 6902 return false; 6903 } 6904 6905 @Override 6906 public boolean containsAll(Collection<?> c) { 6907 for(Object o : c) 6908 { 6909 if(!contains(o)) 6910 return false; 6911 } 6912 return true; 6913 } 6914 6915 @Override 6916 public boolean addAll(Collection<? extends Coord> c) { 6917 boolean changed = false; 6918 for(Coord co : c) 6919 { 6920 changed |= add(co); 6921 } 6922 return changed; 6923 } 6924 6925 @Override 6926 public boolean removeAll(Collection<?> c) { 6927 boolean changed = false; 6928 for(Object o : c) 6929 { 6930 changed |= remove(o); 6931 } 6932 return changed; 6933 } 6934 6935 @Override 6936 public boolean retainAll(Collection<?> c) { 6937 GreasedRegion g2 = new GreasedRegion(width, height); 6938 for(Object o : c) 6939 { 6940 if(contains(o) && o instanceof Coord) 6941 { 6942 g2.add((Coord)o); 6943 } 6944 } 6945 boolean changed = equals(g2); 6946 remake(g2); 6947 return changed; 6948 } 6949 6950 /** 6951 * Randomly removes points from a GreasedRegion, with larger values for preservation keeping more of the existing 6952 * shape intact. If preservation is 1, roughly 1/2 of all points will be removed; if 2, roughly 1/4, if 3, roughly 6953 * 1/8, and so on, so that preservation can be thought of as a negative exponent of 2. 6954 * @param rng used to determine random factors 6955 * @param preservation roughly what degree of points to remove (higher keeps more); removes about {@code 1/(2^preservation)} points 6956 * @return a randomly modified change to this GreasedRegion 6957 */ 6958 public GreasedRegion deteriorate(RandomnessSource rng, int preservation) { 6959 if(rng == null || width <= 2 || ySections <= 0 || preservation <= 0) 6960 return this; 6961 long mash; 6962 for (int i = 0; i < width * ySections; i++) { 6963 mash = rng.nextLong(); 6964 for (int j = i; j < preservation; j++) { 6965 mash |= rng.nextLong(); 6966 } 6967 data[i] &= mash; 6968 } 6969 tallied = false; 6970 return this; 6971 } 6972 6973 /** 6974 * Randomly removes points from a GreasedRegion, with preservation as a fraction between 1.0 (keep all) and 0.0 6975 * (remove all). If preservation is 0.5, roughly 1/2 of all points will be removed; if 0.25, roughly 3/4 will be 6976 * removed (roughly 0.25 will be _kept_), if 0.8, roughly 1/5 will be removed (and about 0.8 will be kept), and so 6977 * on. Preservation must be between 0.0 and 1.0 for this to have the intended behavior; 1.0 or higher will keep all 6978 * points without change (returning this GreasedRegion), while anything less than 0.015625 (1.0/64) will empty this 6979 * GreasedRegion (using {@link #empty()}) and then return it. The parameter {@code random} can be an object like a 6980 * {@link DiverRNG}, an {@link RNG} backed by a well-distributed RandomnessSource like its default, DiverRNG, a 6981 * {@link GWTRNG} (especially if you target GWT, where it will perform much better than most alternatives), or any 6982 * of various other RandomnessSource implementations that distribute bits well for 6983 * {@link RandomnessSource#nextLong()}, but should not be intentionally-biased RNGs like {@link DharmaRNG} or 6984 * {@link EditRNG}, nor double-based QRNGs like {@link VanDerCorputQRNG} or {@link SobolQRNG}. 6985 * @param random used to determine random factors; likely to be an {@link RNG}, {@link DiverRNG}, or {@link GWTRNG} 6986 * @param preservation the rough fraction of points to keep, between 0.0 and 1.0 6987 * @return a randomly modified change to this GreasedRegion 6988 */ 6989 public GreasedRegion deteriorate(final RandomnessSource random, final double preservation) { 6990 if(random == null || width <= 2 || ySections <= 0 || preservation >= 1) 6991 return this; 6992 if(preservation <= 0) 6993 return empty(); 6994 int bitCount = (int) (preservation * 64); 6995 for (int i = 0; i < width * ySections; i++) { 6996 data[i] &= approximateBits(random, bitCount); 6997 } 6998 tallied = false; 6999 return this; 7000 } 7001 7002 /** 7003 * Changes the on/off state of the cell with the given x and y, making an on cell into an off cell, or an off cell 7004 * into an on cell. 7005 * <br> 7006 * This was called flip(), but that name would be confusing since flipping a rectangular area usually means 7007 * reversing an axis. 7008 * @param x the x position of the cell to flip 7009 * @param y the y position of the cell to flip 7010 * @return this for chaining, modified 7011 */ 7012 public GreasedRegion toggle(int x, int y) { 7013 if(x >= 0 && y >= 0 && x < width && y < height && ySections > 0) 7014 { 7015 data[x * ySections + (y >> 6)] ^= (1L << (y & 63)); 7016 tallied = false; 7017 } 7018 return this; 7019 7020 } 7021 7022 /** 7023 * Returns a new GreasedRegion that has been mirrored along the rightmost edge, parallel to the y-axis. The new 7024 * GreasedRegion will have exactly twice the width, the additional width will have the contents of the original 7025 * GreasesRegion in reversed order. The positions shared by both GreasedRegions will be the same, that is, any area 7026 * not added to the original will be equal to the original. 7027 * @return a new GreasedRegion with twice the width of {@code this}, that is mirrored along the rightmost edge 7028 */ 7029 public GreasedRegion mirrorY() 7030 { 7031 GreasedRegion next = new GreasedRegion(data, width, height, width * 2, height); 7032 for (int i = 0, o = width * 2 - 1; i < width; i++, o--) { 7033 System.arraycopy(data, ySections * i, next.data, ySections * o, ySections); 7034 } 7035 return next; 7036 } 7037 7038 @Override 7039 public boolean intersectsWith(Zone other) { 7040 if (other instanceof GreasedRegion) 7041 return intersects((GreasedRegion) other); 7042 long t, w; 7043 for (int x = 0; x < width; x++) { 7044 for (int s = 0; s < ySections; s++) { 7045 if ((t = data[x * ySections + s]) != 0) { 7046 w = NumberTools.lowestOneBit(t); 7047 while (w != 0) { 7048 if (other.contains(x, (s << 6) | Long.numberOfTrailingZeros(w))) 7049 return true; 7050 t ^= w; 7051 w = NumberTools.lowestOneBit(t); 7052 } 7053 } 7054 } 7055 } 7056 return false; 7057 } 7058 /** 7059 * Translates a copy of {@code this} by the x,y values in {@code c}. 7060 * Implemented with {@code return copy().translate(c.x, c.y);} 7061 * @return {@code this} copied and shifted by {@code (c.x,c.y)} 7062 */ 7063 @Override 7064 public GreasedRegion translate(Coord c) { 7065 return copy().translate(c.x, c.y); 7066 } 7067 /** 7068 * Gets a Collection of Coord values that are not in this GreasedRegion, but are 7069 * adjacent to it, either orthogonally or diagonally. Related to the fringe() 7070 * methods in CoordPacker and GreasedRegion, but guaranteed to use 8-way 7071 * adjacency and to return a new Collection of Coord. This implementation returns 7072 * a GreasedRegion produced simply by {@code return copy().fringe8way();} . 7073 * @return Cells adjacent to {@code this} (orthogonally or diagonally) that 7074 * aren't in {@code this} 7075 */ 7076 @Override 7077 public GreasedRegion getExternalBorder() { 7078 return copy().fringe8way(); 7079 } 7080 7081 /** 7082 * Gets a new Zone that contains all the Coords in {@code this} plus all 7083 * neighboring Coords, which can be orthogonally or diagonally adjacent 7084 * to any Coord this has in it. Related to the expand() methods in 7085 * CoordPacker and GreasedRegion, but guaranteed to use 8-way adjacency 7086 * and to return a new Zone. This implementation returns a GreasedRegion 7087 * produced simply by {@code return copy().expand8way();} . 7088 * @return A new GreasedRegion where "off" cells adjacent to {@code this} 7089 * (orthogonally or diagonally) have been added to the "on" cells 7090 * in {@code this} 7091 */ 7092 @Override 7093 public GreasedRegion extend() { 7094 return copy().expand8way(); 7095 } 7096 7097 /** 7098 * Checks if {@code c} is present in this GreasedRegion. Returns true if and only if c is present in this 7099 * GreasedRegion as an "on" cell. This will never be true if c is null, has negative x or y, has a value for x that 7100 * is equal to or greater than {@link #width}, or has a value for y that is equal to or greater than 7101 * {@link #height}, but none of those conditions will cause Exceptions to be thrown. 7102 * @param c a Coord to try to find in this GreasedRegion; if null this will always return false 7103 * @return true if {@code c} is an "on" cell in this GreasedRegion, or false otherwise, including if c is null 7104 */ 7105 @Override 7106 public boolean contains(Coord c) { 7107 return c != null && contains(c.x, c.y); 7108 } 7109 7110 /** 7111 * Checks whether all Coords in {@code other} are also present in {@code this}. 7112 * Requires that {@code other} won't give a null Coord while this method iterates over it. 7113 * @param other another Zone, such as a GreasedRegion or a {@link squidpony.squidgrid.zone.CoordPackerZone} 7114 * @return true if all Coords in other are "on" in this GreasedRegion, or false otherwise 7115 */ 7116 @Override 7117 public boolean contains(Zone other) { 7118 if(other instanceof Collection) 7119 return containsAll((Collection) other); 7120 for(Coord c : other) 7121 { 7122 if(!contains(c.x, c.y)) 7123 return false; 7124 } 7125 return true; 7126 } 7127 7128 /** 7129 * @param findSmallest if true, finds the smallest x-coordinate value; 7130 * if false, finds the biggest. 7131 * @return The x-coordinate of the Coord within {@code this} that has the 7132 * smallest (or biggest) x-coordinate. Or -1 if the zone is empty. 7133 */ 7134 @Override 7135 public int xBound(boolean findSmallest) { 7136 if(findSmallest) 7137 return first().x; 7138 else 7139 return last().x; 7140 } 7141 7142 /** 7143 * @param findSmallest if true, finds the smallest y-coordinate value; 7144 * if false, finds the biggest. 7145 * @return The y-coordinate of the Coord within {@code this} that has the 7146 * smallest (or biggest) y-coordinate. Or -1 if the zone is empty. 7147 */ 7148 @Override 7149 public int yBound(boolean findSmallest) { 7150 long t = 0L; 7151 if(findSmallest) { 7152 for (int s = 0; s < ySections; s++) { 7153 for (int x = 0; x < width; x++) { 7154 t |= data[x * ySections + s]; 7155 } 7156 if(t != 0L) 7157 return s << 6 | Long.numberOfTrailingZeros(t); 7158 } 7159 return -1; 7160 } 7161 else 7162 { 7163 for (int s = ySections - 1; s >= 0; s--) { 7164 for (int x = 0; x < width; x++) { 7165 t |= data[x * ySections + s]; 7166 } 7167 if(t != 0L) 7168 return s << 6 | 63 - Long.numberOfLeadingZeros(t); 7169 } 7170 return -1; 7171 } 7172 } 7173 7174 /** 7175 * Gets the distance between the minimum x-value contained in this GreasedRegion and the maximum x-value in it. 7176 * Not the same as accessing the field {@link #width} on a GreasedRegion! The field will get the span of the space 7177 * that the GreasedRegion can use, including "on" and "off" cells. This method will only get the distance between 7178 * the furthest-separated "on" cells on the x-axis, and won't consider "off" cells. This method can return -1 if the 7179 * GreasedRegion is empty, 0 if the "on" cells are all in a vertical line (that is, when the minimum x is equal to 7180 * the maximum x), or a positive int in other cases with multiple x-values. 7181 * @return the distance on the x-axis between the "on" cell with the lowest x-value and the one with the highest 7182 */ 7183 @Override 7184 public int getWidth() { 7185 if (super.width == -2) 7186 super.width = isEmpty() ? -1 : xBound(false) - xBound(true); 7187 return super.width; 7188 } 7189 /** 7190 * Gets the distance between the minimum y-value contained in this GreasedRegion and the maximum y-value in it. 7191 * Not the same as accessing the field {@link #height} on a GreasedRegion! The field will get the span of the space 7192 * that the GreasedRegion can use, including "on" and "off" cells. This method will only get the distance between 7193 * the furthest-separated "on" cells on the y-axis, and won't consider "off" cells. This method can return -1 if the 7194 * GreasedRegion is empty, 0 if the "on" cells are all in a horizontal line (that is, when the minimum y is equal to 7195 * the maximum y), or a positive int in other cases with multiple y-values. 7196 * @return the distance on the y-axis between the "on" cell with the lowest y-value and the one with the highest 7197 */ 7198 @Override 7199 public int getHeight() { 7200 if (super.height == -2) 7201 super.height = isEmpty() ? -1 : yBound(false) - yBound(true); 7202 return super.height; 7203 } 7204 7205 /** 7206 * Gets the diagonal distance from the point combining the lowest x-value present in this GreasedRegion with the 7207 * lowest y-value in this, to the point combining the highest x-value and the highest y-value. These minimum and 7208 * maximum values don't necessarily match a single "on" cell for each min and max corner, and can take their x and y 7209 * values from two different points. The diagonal distance uses Euclidean measurement (basic Pythagorean Theorem 7210 * math here), and will be a double. 7211 * @return the diagonal distance from (min x, min y) to (max x, max y), as a double 7212 */ 7213 @Override 7214 public double getDiagonal() { 7215 final int w = getWidth(); 7216 final int h = getHeight(); 7217 return Math.sqrt((w * w) + (h * h)); 7218 } 7219//////Not duplicated because the superclass does this just fine. 7220// @Override 7221// public Coord getCenter() { 7222// return super.getCenter(); 7223// } 7224 7225 @Override 7226 public GreasedRegion getInternalBorder() { 7227 return copy().surface8way(); 7228 } 7229 7230 /** 7231 * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming 7232 * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells 7233 * in this. This is pseudo-random with the given seed (which will be made into an odd number if it is not one 7234 * already), and is very good at avoiding overlap (just as good as {@link #separatedZCurve(double, int)}, and 7235 * much faster). If you request too many cells (too high of a value for fraction), it will start to overlap, but 7236 * a fraction value of 0.4 reliably has had no overlap in testing. Restricts the total size of the returned array to 7237 * a maximum of {@code limit} (minimum is 0 if no cells are "on"). If limit is negative, this will not restrict the 7238 * size. 7239 * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0 7240 * @param limit the maximum size of the array to return 7241 * @param seed a long seed to change the points; the most significant 21 bits (except the sign bit) and least significant bit are ignored 7242 * @return a freshly-allocated Coord array containing the pseudo-random cells 7243 */ 7244 public Coord[] mixedRandomSeparatedAlt(double fraction, int limit, long seed) 7245 { 7246 if(fraction < 0) 7247 return new Coord[0]; 7248 if(fraction > 1) 7249 fraction = 1; 7250 int tmp, ic; 7251 long t, w; 7252 seed |= 1L; 7253 final int total = size(); 7254 int ct = (int)(total * fraction); 7255 if(limit >= 0 && limit < ct) 7256 ct = limit; 7257 Coord[] vl = new Coord[ct]; 7258 EACH_QUASI: 7259 for (int i = 0; i < ct; i++) 7260 { 7261 tmp = (int)(VanDerCorputQRNG.altDetermine(seed, i+1) * total); 7262 7263 for (int s = 0; s < ySections; s++) { 7264 for (int x = 0; x < width; x++) { 7265 if ((ic = counts[x * ySections + s]) > tmp) { 7266 t = data[x * ySections + s]; 7267 for (--ic; t != 0; ic--) { 7268 w = NumberTools.lowestOneBit(t); 7269 if (ic == tmp) 7270 { 7271 vl[i] = Coord.get(x, (s << 6) | Long.bitCount(w-1)); 7272 continue EACH_QUASI; 7273 } 7274 t ^= w; 7275 } 7276 } 7277 } 7278 } 7279 } 7280 return vl; 7281 } 7282 7283 7284 public class GRIterator implements Iterator<Coord> 7285 { 7286 public int index; 7287 private long t, w; 7288 public GRIterator() 7289 { 7290 if(!tallied) 7291 tally(); 7292 } 7293 @Override 7294 public boolean hasNext() { 7295 return index < ct; 7296 } 7297 7298 @Override 7299 public Coord next() { 7300 int c; 7301 if(index >= ct) 7302 return null; 7303 for (int s = 0; s < ySections; s++) { 7304 for (int x = 0; x < width; x++) { 7305 if ((c = counts[x * ySections + s]) > index) { 7306 t = data[x * ySections + s] | 0L; 7307 for (--c; t != 0; c--) { 7308 w = NumberTools.lowestOneBit(t); 7309 if (c == index) 7310 { 7311 index++; 7312 return Coord.get(x, (s << 6) | Long.bitCount(w-1)); 7313 } 7314 t ^= w; 7315 } 7316 } 7317 } 7318 } 7319 return null; 7320 7321 /* 7322 for (int x = 0; x < width; x++) { 7323 for (int s = 0; s < ySections; s++) { 7324 if ((w = NumberTools.lowestOneBit(data[x * ySections + s])) != 0 && i++ >= index) { 7325 if(index++ < limit) 7326 return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)); 7327 else 7328 return null; 7329 } 7330 } 7331 } 7332 */ 7333 } 7334 7335 @Override 7336 public void remove() { 7337 throw new UnsupportedOperationException("remove() is not supported on this Iterator."); 7338 } 7339 } 7340}