/******************************************************************************* * Copyright (c) 2015 - 2017 * <p> * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * <p> * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * <p> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *******************************************************************************/ package jsettlers.common.map.shapes; import jsettlers.common.movable.EDirection; import jsettlers.common.position.ShortPoint2D; import jsettlers.common.utils.coordinates.CoordinateStream; import jsettlers.common.utils.coordinates.IBooleanCoordinateFunction; import java.io.Serializable; import java.util.Iterator; /** * Represents a hexagon on the grid. * * @author Andreas Eberle */ public final class HexGridArea implements IMapArea { private static final int MAX_DIRECTIONS_IDX = EDirection.NUMBER_OF_DIRECTIONS - 1; private static final byte[] DIRECTION_INCREASE_X = { EDirection.SOUTH_EAST.gridDeltaX, EDirection.SOUTH_WEST.gridDeltaX, EDirection.WEST.gridDeltaX, EDirection.NORTH_WEST.gridDeltaX, EDirection.NORTH_EAST.gridDeltaX, EDirection.EAST.gridDeltaX }; private static final byte[] DIRECTION_INCREASE_Y = { EDirection.SOUTH_EAST.gridDeltaY, EDirection.SOUTH_WEST.gridDeltaY, EDirection.WEST.gridDeltaY, EDirection.NORTH_WEST.gridDeltaY, EDirection.NORTH_EAST.gridDeltaY, EDirection.EAST.gridDeltaY }; private static final long serialVersionUID = -2218632675269689379L; final short cX; final short cY; final short startRadius; final short maxRadius; /** * Hexagon area from including {@link #startRadius} to including {@link #maxRadius} * * @param centerX * center x * @param centerY * center y * @param startRadius * inclusive inner radius * @param maxRadius * inclusive outer radius */ public HexGridArea(int centerX, int centerY, int startRadius, int maxRadius) { this.cX = (short) centerX; this.cY = (short) centerY; this.startRadius = (short) startRadius; this.maxRadius = (short) maxRadius; } public HexGridArea(int centerX, int centerY, int radius) { this(centerX, centerY, 0, radius); } public HexGridArea(ShortPoint2D center, int radius) { this(center.x, center.y, radius); } @Override public boolean contains(ShortPoint2D position) { throw new UnsupportedOperationException("not implemented yet"); } @Override public boolean contains(int x, int y) { throw new UnsupportedOperationException("not implemented yet"); } @Override public HexGridAreaIterator iterator() { return new HexGridAreaIterator(this); } public static final class HexGridAreaIterator implements Iterator<ShortPoint2D>, Serializable { private static final long serialVersionUID = -8760653162789299782L; private final HexGridArea hexGridArea; private short radius; private short x; private short y; private int direction; private short length = 1; public HexGridAreaIterator(HexGridArea hexGridArea) { this.hexGridArea = hexGridArea; radius = hexGridArea.startRadius; x = hexGridArea.cX; y = (short) (hexGridArea.cY - radius); // radius * NORTH_EAST if (hexGridArea.startRadius == 0) { direction = EDirection.NUMBER_OF_DIRECTIONS; } else { direction = 0; x += EDirection.SOUTH_EAST.gridDeltaX; y += EDirection.SOUTH_EAST.gridDeltaY; } } @Override public boolean hasNext() { return radius <= hexGridArea.maxRadius; } @Override public ShortPoint2D next() { ShortPoint2D result = new ShortPoint2D(x, y); if (length >= radius) { length = 0; direction++; if (direction >= EDirection.NUMBER_OF_DIRECTIONS) { x += DIRECTION_INCREASE_X[MAX_DIRECTIONS_IDX]; y += DIRECTION_INCREASE_Y[MAX_DIRECTIONS_IDX]; direction = 0; length = 1; radius++; return result; } } length++; x += DIRECTION_INCREASE_X[direction]; y += DIRECTION_INCREASE_Y[direction]; return result; } @Override public void remove() { throw new UnsupportedOperationException("not implemented!"); } } @Override public CoordinateStream stream() { return stream(cX, cY, startRadius, maxRadius); } public static CoordinateStream stream(int cX, int cY, int startRadius, int maxRadius) { return new CoordinateStream() { @Override public boolean iterate(IBooleanCoordinateFunction function) { if (startRadius == 0 && !function.apply(cX, cY)) { return false; } int x = cX; int y = cY - startRadius; // radius * NORTH_EAST for (int radius = startRadius; radius <= maxRadius; radius++) { for (int direction = 0; direction < EDirection.NUMBER_OF_DIRECTIONS; direction++) { for (int step = 0; step < radius; step++) { x += DIRECTION_INCREASE_X[direction]; y += DIRECTION_INCREASE_Y[direction]; if (!function.apply(x, y)) { return false; } } } y--; // go to next radius / go one NORTH_EAST } return true; } }; } public CoordinateStream streamBorder() { return streamBorder(cX, cY, maxRadius); } public static CoordinateStream streamBorder(short centerX, short centerY, int radius) { return stream(centerX, centerY, radius, radius); } public static CoordinateStream streamBorder(ShortPoint2D center, int radius) { return streamBorder(center.x, center.y, radius); } }