001package squidpony.squidgrid; 002 003import squidpony.squidmath.Coord; 004import squidpony.squidmath.IntDoubleOrderedMap; 005import squidpony.squidmath.IntVLA; 006 007import java.io.Serializable; 008 009/** 010 * Some classes need detailed information about what cells are considered adjacent to other cells, and may 011 * need to construct a customized mapping of cells to their neighbors. Implementations of this abstract 012 * class provide information about all sorts of things, including the distance metric (from DijkstraMap), 013 * but also the maximum number of states that can be moved to in one step (including rotations at the same 014 * point in space, in some cases), and whether the type of map uses a "two-step" rule that needs two 015 * sequential moves in the same direction to be viable and unobstructed to allow movement (which is 016 * important in thin-wall maps). 017 * <br> 018 * When CustomDijkstraMap and similar classes need to store more information about a point than just its 019 * (x,y) position, they also use implementations of this class to cram more information in a single int. 020 * This abstract class provides methods to obtain four different numbers from a single int, though not all 021 * implementations may provide all four as viable options. It also provides a utility to get a Coord from an 022 * int. X and Y are exactly what they always mean in 2D Coords, R is typically used for rotation, and N is 023 * typically used for anything else when it is present. The convention is to use N for the Z-axis when 024 * elevation/depth should be tracked, or for any more specialized extensions to the information carried at 025 * a point. The composite() method produces a compressed int from X, Y, R, and N values, and the validate() 026 * method allows code to quickly check if an int is valid data this class can use. Other information is 027 * tracked by fields, such as height, width, rotations, and depths, where the maximum number of possible 028 * states is given by height * width * rotations * depths, and the minimum for any of these int fields is 1. 029 * <br> 030 * Lastly, the neighborMaps() method produces very important information about what neighbors each cell has, 031 * and by modifying the returned int[][], you can produce "portal" effects, wraparound, and other useful 032 * concepts. The value it returns consists of an array (with length == maxAdjacent) of arrays (each with the 033 * same size, length == width * height * rotations * depth). The values in the inner arrays can be any int 034 * between 0 and (width * height * rotations * depth), which refers to the index in any of the inner arrays of 035 * a neighboring cell, or can be -1 if there is no neighbor possible here (typically at edges or corners of the 036 * map, some of the neighbors are not valid and so use -1). In normal usage, a for loop is used from 0 to 037 * maxAdjacent, and in each iteration the same index is looked up (the current cell, encoded as by composite() 038 * or obtained as an already-composited neighbor earlier), and this normally gets a different neighbor every 039 * time. In methods that do a full-map search or act in a way that can possibly loop back over an existing cell 040 * in the presence of wrapping (toroidal or "modulus" maps) or portals, you may want to consider tracking a 041 * count of how many cells have been processed and terminate any processing of further cells if the count 042 * significantly exceeds the number of cells on the map (terminating when 4 times the cell count is reached may 043 * be the most extreme case for very-portal-heavy maps). 044 * Created by Tommy Ettinger on 8/12/2016. 045 */ 046public abstract class Adjacency implements Serializable { 047 private static final long serialVersionUID = 0L; 048 /** 049 * The array of all possible directions this allows, regardless of cost. 050 */ 051 public Direction[] directions; 052 /** 053 * The maximum number of states that can be considered adjacent; when rotations are present and have a 054 * cost this is almost always 3 (move forward, turn left, turn right), and in most other cases this is 055 * 4 (when using Manhattan distance) or 8 (for other distance metrics). 056 */ 057 public int maxAdjacent; 058 /** 059 * Only needed for thin-wall maps; this requires two steps in the same direction to both be valid moves 060 * for that direction to be considered, and always moves the pathfinder two steps, typically to cells 061 * with even numbers for both x and y (where odd-number-position cells are used for edges or corners 062 * between cells, and can still be obstacles or possible to pass through, but not stay on). 063 */ 064 public boolean twoStepRule; 065 /** 066 * If you want obstacles present in orthogonal cells to prevent pathfinding along the diagonal between them, this 067 * can be used to make single-cell diagonal walls non-viable to move through, or even to prevent diagonal movement if any 068 * one obstacle is orthogonally adjacent to both the start and target cell of a diagonal move. 069 * <br> 070 * If this is 0, as a special case no orthogonal obstacles will block diagonal moves. 071 * <br> 072 * If this is 1, having one orthogonal obstacle adjacent to both the current cell and the cell the pathfinder is 073 * trying to diagonally enter will block diagonal moves. This generally blocks movement around corners, the "hard 074 * corner" rule used in some games. 075 * <br> 076 * If this is 2, having two orthogonal obstacles adjacent to both the current cell and the cell the pathfinder is 077 * trying to diagonally enter will block diagonal moves. As an example, if there is a wall to the north and a wall 078 * to the east, then the pathfinder won't be able to move northeast even if there is a floor there. 079 * <br> 080 * A similar effect can be achieved with a little more control by using thin walls, where the presence of 081 * a "thin corner" can block diagonal movement through that corner, or the absence of a blocking wall in 082 * a corner space allows movement through it. 083 */ 084 public int blockingRule; 085 /** 086 * This affects how distance is measured on diagonal directions vs. orthogonal directions. MANHATTAN should form a 087 * diamond shape on a featureless map, while CHEBYSHEV and EUCLIDEAN will form a square. EUCLIDEAN does not affect 088 * the length of paths, though it will change the DijkstraMap's gradientMap to have many non-integer values, and 089 * that in turn will make paths this finds much more realistic and smooth (favoring orthogonal directions unless a 090 * diagonal one is a better option). 091 */ 092 public Measurement measurement; 093 /** 094 * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. 095 */ 096 public int width, 097 /** 098 * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. 099 */ 100 height, 101 /** 102 * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. 103 */ 104 rotations, 105 /** 106 * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. 107 */ 108 depths; 109 110 protected boolean standardCost = true; 111 112 public boolean hasStandardCost() 113 { 114 return standardCost; 115 } 116 /** 117 * Used in place of a double[][] of costs in CustomDijkstraMap; allows you to set the costs to enter tiles (via 118 * {@link #addCostRule(char, double)} or {@link #addCostRule(char, double, boolean)} if the map has rotations). 119 * A cost of 1.0 is normal for most implementations; higher costs make a movement harder to perform and take more 120 * time if the game uses that mechanic, while lower costs (which should always be greater than 0.0) make a move 121 * easier to perform. Most games can do perfectly well with just 1.0 and 2.0, if they use this at all, plus possibly 122 * a very high value for impossible moves (say, 9999.0 for something like a submarine trying to enter suburbia). 123 * <br> 124 * You should not alter costRules in most cases except through the Adjacency's addCostRule method; most Adjacency 125 * implementations will set a flag if any cost is set through addCostRule that is different from the default, and 126 * this flag determines early-stop behavior in pathfinding (it can be checked with {@link #hasStandardCost()}, but 127 * cannot be set directly). 128 * <br> 129 * Adjacency implementations are expected to set a reasonable default value for when missing keys are queried, using 130 * {@link IntDoubleOrderedMap#defaultReturnValue(double)}; there may be a reason for user code to call this as well. 131 */ 132 public IntDoubleOrderedMap costRules = new IntDoubleOrderedMap(32); 133 134 public abstract int extractX(int data); 135 136 public abstract int extractY(int data); 137 138 public abstract int extractR(int data); 139 140 public abstract int extractN(int data); 141 142 /** 143 * Encodes up to four components used by this Adjacency, putting them into one int. 144 * Returns -1 if the encoded position is out of bounds or otherwise invalid, otherwise any int is possible. 145 * You can get the individual values with {@link #extractX(int)}, {@link #extractY(int)}, {@link #extractR(int)}, 146 * and {@link #extractN(int)}, though not all implementations use R and N. 147 * @param x the x component to encode 148 * @param y the y component to encode 149 * @param r the rotation component to encode; not all implementations use rotation and the max value varies 150 * @param n the bonus component to encode; this can be used for height or other extra data in some implementations 151 * @return the encoded position as an int; -1 if invalid, non-negative for valid positions 152 */ 153 public abstract int composite(int x, int y, int r, int n); 154 155 public abstract boolean validate(int data); 156 157 public Coord extractCoord(int data) { 158 return Coord.get(extractX(data), extractY(data)); 159 } 160 161 public int move(int start, int x, int y, int r, int n) 162 { 163 return composite(extractX(start) + x, extractY(start) + y, extractR(start) + r, extractN(start) + n); 164 } 165 public int move(int start, int x, int y) 166 { 167 return move(start, x, y, 0, 0); 168 } 169 170 public abstract int[][][] neighborMaps(); 171 172 public abstract void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay); 173 174 public abstract boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall); 175 176 public IntDoubleOrderedMap addCostRule(char tile, double cost) 177 { 178 return addCostRule(tile, cost, false); 179 } 180 public abstract IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation); 181 182 public IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value) 183 { 184 return putAllVariants(map, key, value, 1); 185 } 186 public abstract IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size); 187 188 public void putAllVariants(IntVLA list, double[] map, int key, double value) 189 { 190 putAllVariants(list, map, key, value,1); 191 } 192 public abstract void putAllVariants(IntVLA list, double[] map, int key, double value, int size); 193 194 public void resetAllVariants(double[] map, int[] keys, double[] values) 195 { 196 resetAllVariants(map, keys, keys.length, values,1); 197 } 198 199 public void resetAllVariants(double[] map, int[] keys, double[] values, int size) 200 { 201 resetAllVariants(map, keys, keys.length, values,1); 202 } 203 204 public abstract void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size); 205 206 public int[] invertAdjacent; 207 208 public String show(int data) 209 { 210 if(data < 0) 211 return "(-)"; 212 if(rotations <= 1) 213 { 214 if(depths <= 1) 215 return "(" + extractX(data) + ',' + extractY(data) + ')'; 216 return "(" + extractX(data) + ',' + extractY(data) + ',' + extractN(data) + ')'; 217 } 218 if(depths <= 1) 219 return "(" + extractX(data) + ',' + extractY(data) + ',' + extractR(data) + ')'; 220 return "(" + extractX(data) + ',' + extractY(data) + ',' + extractR(data) + ',' + extractN(data) + ')'; 221 } 222 public String showMap(int[] map, int r) 223 { 224 r %= rotations; 225 StringBuilder sb = new StringBuilder(width * height * 8); 226 for (int y = 0; y < height; y++) { 227 for (int x = 0; x < width; x++) { 228 sb.append(show(map[(y * width + x) * rotations + r])).append(' '); 229 } 230 sb.append('\n'); 231 } 232 return sb.toString(); 233 } 234 235 public static class BasicAdjacency extends Adjacency implements Serializable { 236 private static final long serialVersionUID = 0L; 237 238 private BasicAdjacency() { 239 this(20, 20, Measurement.MANHATTAN); 240 } 241 242 public BasicAdjacency(int width, int height, Measurement metric) { 243 this.width = width; 244 this.height = height; 245 rotations = 1; 246 depths = 1; 247 measurement = metric; 248 if(metric == Measurement.MANHATTAN) 249 { 250 directions = Direction.CARDINALS; 251 maxAdjacent = 4; 252 invertAdjacent = new int[]{1, 0, 3, 2}; 253 } 254 else 255 { 256 directions = Direction.OUTWARDS; 257 maxAdjacent = 8; 258 invertAdjacent = new int[]{1, 0, 3, 2, 7, 6, 5, 4}; 259 } 260 twoStepRule = false; 261 blockingRule = 2; 262 costRules.defaultReturnValue(1.0); 263 } 264 265 @Override 266 public int extractX(int data) { 267 return data % width; 268 } 269 270 @Override 271 public int extractY(int data) { 272 return data / width; 273 } 274 275 @Override 276 public int extractR(int data) { 277 return 0; 278 } 279 280 @Override 281 public int extractN(int data) { 282 return 0; 283 } 284 285 @Override 286 public int composite(int x, int y, int r, int n) { 287 if(x < 0 || y < 0 || x >= width || y >= height) 288 return -1; 289 return y * width + x; 290 } 291 292 @Override 293 public int move(int start, int x, int y) { 294 int xx = (start % width) + x, yy = (start / width) + y; 295 if(xx < 0 || yy < 0 || xx >= width || yy >= height) 296 return -1; 297 return yy * width + xx; 298 } 299 300 @Override 301 public boolean validate(int data) { 302 return data >= 0 && extractY(data) < height; 303 } 304 305 @Override 306 public int[][][] neighborMaps() { 307 int[][][] maps = new int[2][maxAdjacent][width * height * rotations * depths]; 308 for (int m = 0; m < maxAdjacent; m++) { 309 Direction dir = directions[m]; 310 for (int x = 0; x < width; x++) { 311 for (int y = 0; y < height; y++) { 312 maps[0][m][y * width + x] = composite(x - dir.deltaX, y - dir.deltaY, 0, 0); 313 maps[1][m][y * width + x] = composite(x + dir.deltaX, y + dir.deltaY, 0, 0); 314 } 315 } 316 } 317 return maps; 318 } 319 320 @Override 321 public boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall) { 322 if(direction < 4) 323 return !validate(start); 324 int[][] near = neighbors[0]; 325 switch (direction) 326 { 327 case 4: //UP_LEFT 328 return (near[0][start] < 0 || map[near[0][start]] >= wall) 329 && (near[2][start] < 0 || map[near[2][start]] >= wall); 330 case 5: //UP_RIGHT 331 return (near[0][start] < 0 || map[near[0][start]] >= wall) 332 && (near[3][start] < 0 || map[near[3][start]] >= wall); 333 case 6: //DOWN_LEFT 334 return (near[1][start] < 0 || map[near[1][start]] >= wall) 335 && (near[2][start] < 0 || map[near[2][start]] >= wall); 336 default: //DOWN_RIGHT 337 return (near[1][start] < 0 || map[near[1][start]] >= wall) 338 && (near[3][start] < 0 || map[near[3][start]] >= wall); 339 } 340 } 341 342 @Override 343 public void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay) { 344 if(neighbors == null || !validate(inputPortal) || !validate(outputPortal) 345 || neighbors.length != maxAdjacent) 346 return; 347 for (int d = 0; d < maxAdjacent; d++) { 348 for (int i = 0; i < width * height; i++) { 349 if(neighbors[1][d][i] == inputPortal) 350 { 351 neighbors[1][d][i] = outputPortal; 352 } 353 else if(twoWay && neighbors[1][d][i] == outputPortal) 354 { 355 neighbors[1][d][i] = inputPortal; 356 } 357 358 if(neighbors[0][d][i] == outputPortal) 359 { 360 neighbors[0][d][i] = inputPortal; 361 } 362 else if(twoWay && neighbors[0][d][i] == inputPortal) 363 { 364 neighbors[0][d][i] = outputPortal; 365 } 366 } 367 } 368 } 369 370 @Override 371 public IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation) { 372 costRules.put(tile, cost); 373 if(cost != costRules.defaultReturnValue()) 374 standardCost = false; 375 return costRules; 376 } 377 378 @Override 379 public IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size) { 380 int baseX = key % width, baseY = key / width, comp; 381 if (key >= 0 && baseY < height) { 382 if (size < 0) { 383 for (int x = size+1; x <= 0; x++) { 384 for (int y = size+1; y <= 0; y++) { 385 comp = composite(baseX + x, baseY + y, 0, 0); 386 if(comp >= 0) 387 map.put(comp, value); 388 } 389 } 390 } else { 391 for (int x = 0; x < size; x++) { 392 for (int y = 0; y < size; y++) { 393 comp = composite(baseX + x, baseY + y, 0, 0); 394 if(comp >= 0) 395 map.put(comp, value); 396 } 397 } 398 } 399 } 400 return map; 401 } 402 403 @Override 404 public void putAllVariants(IntVLA list, double[] map, int key, double value, int size) { 405 int baseX = key % width, baseY = key / width, comp; 406 if (key >= 0 && baseY < height) { 407 if (size < 0) { 408 if(list == null) 409 { 410 for (int x = size + 1; x <= 0; x++) { 411 for (int y = size + 1; y <= 0; y++) { 412 comp = composite(baseX + x, baseY + y, 0, 0); 413 if (comp >= 0) { 414 map[comp] = value; 415 } 416 } 417 } 418 } 419 else { 420 for (int x = size + 1; x <= 0; x++) { 421 for (int y = size + 1; y <= 0; y++) { 422 comp = composite(baseX + x, baseY + y, 0, 0); 423 if (comp >= 0 && !list.contains(comp)) { 424 list.add(comp); 425 map[comp] = value; 426 } 427 } 428 } 429 } 430 } else { 431 if (list == null) { 432 for (int x = 0; x < size; x++) { 433 for (int y = 0; y < size; y++) { 434 comp = composite(baseX + x, baseY + y, 0, 0); 435 if (comp >= 0) { 436 map[comp] = value; 437 } 438 } 439 } 440 } else { 441 for (int x = 0; x < size; x++) { 442 for (int y = 0; y < size; y++) { 443 comp = composite(baseX + x, baseY + y, 0, 0); 444 if (comp >= 0 && !list.contains(comp)) { 445 list.add(comp); 446 map[comp] = value; 447 } 448 } 449 } 450 } 451 } 452 } 453 } 454 455 @Override 456 public void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size) { 457 int key; 458 for (int i = 0; i < usable && i < keys.length; i++) { 459 key = keys[i]; 460 int baseX = key % width, baseY = key / width, comp; 461 if (key >= 0 && baseY < height) { 462 if (size < 0) { 463 for (int x = size + 1; x <= 0; x++) { 464 for (int y = size + 1; y <= 0; y++) { 465 comp = composite(baseX + x, baseY + y, 0, 0); 466 if (comp >= 0) { 467 map[comp] = values[comp]; 468 } 469 } 470 } 471 } else { 472 for (int x = 0; x < size; x++) { 473 for (int y = 0; y < size; y++) { 474 comp = composite(baseX + x, baseY + y, 0, 0); 475 if (comp >= 0) { 476 map[comp] = values[comp]; 477 } 478 } 479 } 480 } 481 } 482 } 483 } 484 } 485 486 public static class ThinWallAdjacency extends BasicAdjacency implements Serializable { 487 private static final long serialVersionUID = 0L; 488 489 private ThinWallAdjacency() { 490 this(20, 20, Measurement.MANHATTAN); 491 } 492 493 public ThinWallAdjacency(int width, int height, Measurement metric) { 494 super(width, height, metric); 495 twoStepRule = true; 496 costRules.defaultReturnValue(0.5); 497 } 498 499 @Override 500 public IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation) { 501 costRules.put(tile, cost * 0.5); 502 if(cost * 0.5 != costRules.defaultReturnValue()) 503 standardCost = false; 504 return costRules; 505 } 506 } 507 508 public static class RotationAdjacency extends Adjacency implements Serializable { 509 private static final long serialVersionUID = 0L; 510 511 private RotationAdjacency() { 512 this(20, 20, Measurement.MANHATTAN); 513 } 514 private int shift; 515 public RotationAdjacency(int width, int height, Measurement metric) { 516 this.width = width; 517 this.height = height; 518 measurement = metric; 519 if(metric == Measurement.MANHATTAN) 520 { 521 rotations = 4; 522 shift = 2; 523 directions = Direction.CARDINALS_CLOCKWISE; 524 invertAdjacent = new int[]{2, 3, 0, 1}; 525 } 526 else 527 { 528 rotations = 8; 529 shift = 3; 530 directions = Direction.CLOCKWISE; 531 invertAdjacent = new int[]{4, 5, 6, 7, 0, 1, 2, 3}; 532 } 533 depths = 1; 534 maxAdjacent = 3; 535 twoStepRule = false; 536 blockingRule = 2; 537 costRules.defaultReturnValue(1.0); 538 //invertAdjacent = new int[]{2, 1, 0}; 539 } 540 541 @Override 542 public int extractX(int data) { 543 return (data >>> shift) % width; 544 } 545 546 @Override 547 public int extractY(int data) { 548 return (data >>> shift) / width; 549 } 550 551 @Override 552 public int extractR(int data) { 553 return data & (rotations - 1); 554 } 555 556 @Override 557 public int extractN(int data) { 558 return 0; 559 } 560 561 @Override 562 public int composite(int x, int y, int r, int n) { 563 if(x < 0 || y < 0 || x >= width || y >= height || r < 0 || r >= rotations) 564 return -1; 565 return ((y * width + x) << shift) | r; 566 } 567 568 @Override 569 public boolean validate(int data) { 570 return data >= 0 && extractY(data) < height; 571 } 572 573 @Override 574 public int[][][] neighborMaps() { 575 int[][][] maps = new int[2][maxAdjacent][width * height * rotations * depths]; 576 int current; 577 Direction dir; 578 for (int r = 0; r < rotations; r++) { 579 dir = directions[r]; 580 for (int x = 0; x < width; x++) { 581 for (int y = 0; y < height; y++) { 582 current = ((y * width + x) << shift) | r; 583 maps[0][1][current] = composite(x - dir.deltaX, y - dir.deltaY, r, 0); 584 maps[1][1][current] = composite(x + dir.deltaX, y + dir.deltaY, r, 0); 585 maps[0][0][current] = maps[1][0][current] = composite(x, y, r - 1 & (rotations - 1), 0); 586 maps[0][2][current] = maps[1][2][current] = composite(x, y, r + 1 & (rotations - 1), 0); 587 //maps[0][composite(x, y, r - 1 & (rotations - 1), 0)] = current; 588 //maps[2][composite(x, y, r + 1 & (rotations - 1), 0)] = current; 589 } 590 } 591 } 592 return maps; 593 } 594 595 @Override 596 public boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall) { 597 if(rotations <= 4 || (direction & 1) == 0) 598 return !validate(start); 599 600 return neighbors[0][0][start] < 0 || map[neighbors[0][0][start]] >= wall 601 || neighbors[0][2][start] < 0 || map[neighbors[0][2][start]] >= wall; 602 } 603 604 @Override 605 public void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay) { 606 if(neighbors == null || !validate(inputPortal) || !validate(outputPortal) 607 || neighbors.length != maxAdjacent) 608 return; 609 for (int i = 0; i < width * height * rotations; i++) { 610 if (neighbors[0][1][i] == inputPortal) { 611 neighbors[0][1][i] = outputPortal; 612 } else if (twoWay && neighbors[0][1][i] == outputPortal) { 613 neighbors[0][1][i] = inputPortal; 614 } 615 616 if (neighbors[1][1][i] == outputPortal) { 617 neighbors[1][1][i] = inputPortal; 618 } else if (twoWay && neighbors[1][1][i] == inputPortal) { 619 neighbors[1][1][i] = outputPortal; 620 } 621 } 622 } 623 624 @Override 625 public IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation) { 626 if(isRotation) 627 { 628 costRules.put(tile | 0x10000, Math.max(0.001, cost)); 629 if(Math.max(0.001, cost) != costRules.defaultReturnValue()) 630 standardCost = false; 631 } 632 else { 633 costRules.put(tile, cost); 634 if(cost != costRules.defaultReturnValue()) 635 standardCost = false; 636 } 637 return costRules; 638 } 639 640 641 @Override 642 public IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size) { 643 int baseX = (key >>> shift) % width, baseY = (key>>>shift) / width, comp; 644 if (key >= 0 && baseY < height) { 645 if(size == 1) 646 { 647 for (int r = 0; r < rotations; r++) { 648 comp = composite(baseX, baseY, r, 0); 649 if(comp >= 0) 650 map.put(comp, value); 651 } 652 } 653 else if (size < 0) { 654 for (int x = size+1; x <= 0; x++) { 655 for (int y = size+1; y <= 0; y++) { 656 for (int r = 0; r < rotations; r++) { 657 comp = composite(baseX + x, baseY + y, r, 0); 658 if(comp >= 0) 659 map.put(comp, value); 660 } 661 } 662 } 663 } else { 664 for (int x = 0; x < size; x++) { 665 for (int y = 0; y < size; y++) { 666 for (int r = 0; r < rotations; r++) { 667 comp = composite(baseX + x, baseY + y, r, 0); 668 if(comp >= 0) 669 map.put(comp, value); 670 } 671 } 672 } 673 } 674 } 675 return map; 676 } 677 678 @Override 679 public void putAllVariants(IntVLA list, double[] map, int key, double value, int size) { 680 int baseX = (key >>> shift) % width, baseY = (key>>>shift) / width, comp; 681 if (key >= 0 && baseY < height) { 682 if(size == 1) 683 { 684 if(list == null) 685 { 686 for (int r = 0; r < rotations; r++) { 687 comp = composite(baseX, baseY, r, 0); 688 if(comp >= 0) { 689 map[comp] = value; 690 } 691 } 692 } 693 else 694 { 695 for (int r = 0; r < rotations; r++) { 696 comp = composite(baseX, baseY, r, 0); 697 if(comp >= 0 && !list.contains(comp)) { 698 list.add(comp); 699 map[comp] = value; 700 } 701 } 702 } 703 } 704 else if (size < 0) { 705 if(list == null) 706 { 707 for (int x = size+1; x <= 0; x++) { 708 for (int y = size+1; y <= 0; y++) { 709 for (int r = 0; r < rotations; r++) { 710 comp = composite(baseX + x, baseY + y, r, 0); 711 if(comp >= 0) { 712 map[comp] = value; 713 } 714 } 715 } 716 } 717 } 718 else { 719 for (int x = size+1; x <= 0; x++) { 720 for (int y = size+1; y <= 0; y++) { 721 for (int r = 0; r < rotations; r++) { 722 comp = composite(baseX + x, baseY + y, r, 0); 723 if(comp >= 0 && !list.contains(comp)) { 724 list.add(comp); 725 map[comp] = value; 726 } 727 } 728 } 729 } 730 } 731 } else { 732 if(list == null) 733 { 734 for (int x = 0; x < size; x++) { 735 for (int y = 0; y < size; y++) { 736 for (int r = 0; r < rotations; r++) { 737 comp = composite(baseX + x, baseY + y, r, 0); 738 if(comp >= 0) { 739 map[comp] = value; 740 } 741 } 742 } 743 } 744 } 745 else { 746 for (int x = 0; x < size; x++) { 747 for (int y = 0; y < size; y++) { 748 for (int r = 0; r < rotations; r++) { 749 comp = composite(baseX + x, baseY + y, r, 0); 750 if(comp >= 0 && !list.contains(comp)) { 751 list.add(comp); 752 map[comp] = value; 753 } 754 } 755 } 756 } 757 } 758 } 759 } 760 } 761 762 @Override 763 public void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size) { 764 int key; 765 for (int i = 0; i < usable && i < keys.length; i++) { 766 key = keys[i]; 767 int baseX = (key >>> shift) % width, baseY = (key >>> shift) / width, comp; 768 if (key >= 0 && baseY < height) { 769 if (size == 1) { 770 for (int r = 0; r < rotations; r++) { 771 comp = composite(baseX, baseY, r, 0); 772 if (comp >= 0) { 773 map[comp] = values[comp]; 774 } 775 } 776 } else if (size < 0) { 777 for (int x = size + 1; x <= 0; x++) { 778 for (int y = size + 1; y <= 0; y++) { 779 for (int r = 0; r < rotations; r++) { 780 comp = composite(baseX + x, baseY + y, r, 0); 781 if (comp >= 0) { 782 map[comp] = values[comp]; 783 } 784 } 785 } 786 } 787 } else { 788 for (int x = 0; x < size; x++) { 789 for (int y = 0; y < size; y++) { 790 for (int r = 0; r < rotations; r++) { 791 comp = composite(baseX + x, baseY + y, r, 0); 792 if (comp >= 0) { 793 map[comp] = values[comp]; 794 } 795 } 796 } 797 } 798 } 799 } 800 } 801 } 802 } 803}