/** * Copyright 2014 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loon * @author cping * @email javachenpeng@yahoo.com * @version 0.4.2 */ package loon.action.map; import java.util.Iterator; import loon.LSystem; import loon.action.map.ArrayInt2DAStar.TileFactory; import loon.geom.RectBox; import loon.utils.MathUtils; import loon.utils.TArray; @SuppressWarnings("unchecked") public class HexagonMap<T> implements GeometryMap, Iterable<TileVisit<T>> { public static final int LEFT = -3; public static final int DOWNLEFT = -2; public static final int UPLEFT = -1; public static final int NONE = 0; public static final int DOWNRIGHT = 1; public static final int UPRIGHT = 2; public static final int RIGHT = 3; protected int columns, rows; protected Object[][] tiles; protected Hexagon origin; protected Hexagon[][] hexagons; public HexagonMap() { } public HexagonMap<T> configure(Hexagon origin) { this.origin = origin; return this; } public HexagonMap<T> configure(int columns, int rows) { this.columns = columns; this.rows = rows; this.tiles = new Object[columns][rows]; this.hexagons = new Hexagon[columns][rows]; return this; } public Hexagon getOrigin() { return origin; } private RectBox rect = null; public RectBox getRect() { if (rect == null) { rect = new RectBox(origin.getX(), origin.getY(), origin.getX() + (rows > 1 ? columns * origin.getWidth() + origin.getHalfWidth() : columns * origin.getWidth()), origin.getY() + rows * origin.getBaseHeight() + origin.getEndHeight()); } return rect; } public boolean contains(int[] position) { int m0 = position[0] + (position[1] >> 1); return m0 >= 0 && m0 < columns && position[1] >= 0 && position[1] < rows; } public T getTile(int[] position) { return (T) tiles[position[0] + (position[1] >> 1)][position[1]]; } public void setTile(int[] position, T tile) { tiles[position[0] + (position[1] >> 1)][position[1]] = tile; } public void fill(TileFactory<T> factory) { for (int j = 0; j < columns; j++) { for (int i = 0; i < rows; i++) { tiles[i][j] = factory.create(j - (i >> 1), i); } } } @Override public Geometry coordinate(int[] position) { int m0 = position[0] + (position[1] >> 1); Hexagon hexagon = hexagons[m0][position[1]]; if (hexagon == null) { hexagon = new Hexagon(position[0] * origin.getWidth() + position[1] * origin.getHalfWidth() + origin.getX(), position[1] * origin.getBaseHeight() + origin.getY(), origin.getHalfWidth(), origin.getMidHeight(), origin.getEndHeight()); hexagons[m0][position[1]] = hexagon; } return hexagon; } private int[] position = new int[2]; @Override public int[] decoordinate(int x, int y) { int m0, n; int xBlock = (x - origin.getX()) / (origin.getHalfWidth() + origin.getHalfWidth()); int xOdd = (x - origin.getX()) % (origin.getHalfWidth() + origin.getHalfWidth()); int yBlock = (y - origin.getY()) / (origin.getEndHeight() + origin.getMidHeight()); int yOdd = (y - origin.getY()) % (origin.getEndHeight() + origin.getMidHeight()); int yOdd0 = MathUtils.round( origin.getEndHeight() / origin.getHalfWidth() * xOdd); if ((yBlock & 1) == 0) { if (yOdd < origin.getEndHeight() - yOdd0) { m0 = xBlock - 1; n = yBlock - 1; } else if (yOdd < yOdd0 - origin.getEndHeight()) { m0 = xBlock; n = yBlock - 1; } else { m0 = xBlock; n = yBlock; } } else { if (xOdd < origin.getHalfWidth()) { if (yOdd < yOdd0) { m0 = xBlock; n = yBlock - 1; } else { m0 = xBlock - 1; n = yBlock; } } else { if (yOdd < origin.getEndHeight() + origin.getEndHeight() - yOdd0) { m0 = xBlock; n = yBlock - 1; } else { m0 = xBlock; n = yBlock; } } } if (m0 >= 0 && m0 < columns && n >= 0 && n < rows) { position[0] = m0 - (n >> 1); position[1] = n; return position; } return null; } @Override public int distance(int[] start, int[] end) { Integer c = end[0] - start[0]; Integer r = end[1] - start[1]; if (c.compareTo(0) == r.compareTo(0)) { c = c < 0 ? -c : c; r = r < 0 ? -r : r; return c + r; } else { c = c < 0 ? -c : c; r = r < 0 ? -r : r; return r > c ? r : c; } } static int[][] orientationsByOffset = { { LEFT, UPLEFT, UPRIGHT }, { LEFT, NONE, RIGHT }, { DOWNLEFT, DOWNRIGHT, RIGHT } }; @Override public int orientate(int[] start, int[] end) { Integer c = end[0] - start[0]; Integer r = end[1] - start[1]; c = c.compareTo(0); r = r.compareTo(0); return orientationsByOffset[r + 1][c + 1]; } private int[][] positions = new int[6][2]; @Override public int[][] adjacent(int[] position) { positions[0][0] = position[0] - 1; positions[0][1] = position[1]; positions[1][0] = position[0]; positions[1][1] = position[1] - 1; positions[2][0] = position[0] + 1; positions[2][1] = position[1] - 1; positions[3][0] = position[0] + 1; positions[3][1] = position[1]; positions[4][0] = position[0]; positions[4][1] = position[1] + 1; positions[5][0] = position[0] - 1; positions[5][1] = position[1] + 1; return positions; } static int[][] offsetsByOrientation = { { -1, 0 }, // LEFT { -1, 1 }, // DOWNLEFT { 0, -1 }, // UPLEFT { 0, 0 }, // NONE { 0, 1 }, // DOWNRIGHT { 1, -1 }, // UPRIGHT { 1, 0 }, // RIGHT }; @Override public int[] adjacent(int[] position, int orientation) { int[] offset = offsetsByOrientation[orientation + 3]; this.position[0] = position[0] + offset[0]; this.position[1] = position[1] + offset[1]; return this.position; } @Override public TArray<int[]> lineRegion(int[] start, int[] end) { int dx = end[0] - start[0]; int dy = end[1] - start[1]; if (dx == 0 || dy == 0 || dx == -dy) { TArray<int[]> positions = new TArray<int[]>(); int ax = dx < 0 ? -dx : dx; int ay = dy < 0 ? -dy : dy; int len = ax < ay ? ay : ax; int x = start[0]; int y = start[1]; int x1 = dx / len; int y1 = dy / len; for (int i = 0; i <= len; i++) { positions.add(new int[] { x, y }); x += x1; y += y1; } return positions; } return null; } @Override public TArray<int[]> circleRegion(int[] center, int radius) { TArray<int[]> positions = new TArray<int[]>(); int i, j, k; for (j = -radius; j <= radius; j++) { if (j < 0) { i = -radius - j; k = radius; } else { i = -radius; k = radius - j; } while (i <= k) { positions.add(new int[] { center[0] + i, center[1] + j }); i++; } } return positions; } protected class CellIterator implements Iterator<TileVisit<T>> { int m, n; int columns, rows; int i, j, k; protected CellIterator(int m, int n, int columns, int rows) { this.m = m; this.n = n; this.columns = columns; this.rows = rows; k = n >> 1; } @Override public boolean hasNext() { return i < columns && j < rows; } @Override public TileVisit<T> next() { TileVisit<T> tile = new TileVisit<T>(); tile.tile = (T) tiles[i + m][j + n]; tile.position[0] = i + m - k; tile.position[1] = j + n; if (++i >= columns) { k = (++j + n) >> 1; i = 0; } return tile; } @Override public void remove() { throw LSystem.runThrow("not supported"); } } @Override public Iterator<TileVisit<T>> iterator() { return new CellIterator(0, 0, columns, rows); } public Iterable<TileVisit<T>> part(RectBox inRect) { int m = inRect.Left() / (origin.getHalfWidth() + origin.getHalfWidth()) - 1; if (m < 0) { m = 0; } int n = inRect.Top() / (origin.getEndHeight() + origin.getMidHeight()) - 1; if (n < 0) { n = 0; } int columns = inRect.width / (origin.getHalfWidth() + origin.getHalfWidth()) + 3; if (m + columns >= this.columns) { columns = this.columns - m; } int rows = inRect.height / (origin.getEndHeight() + origin.getMidHeight()) + 3; if (n + rows >= this.rows) { rows = this.rows - n; } final CellIterator iterator = new CellIterator(m, n, columns, rows); return new Iterable<TileVisit<T>>() { @Override public Iterator<TileVisit<T>> iterator() { return iterator; } }; } }