/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3.client.walker.astar;
import java.util.List;
import com.t3.client.walker.WalkerMetric;
import com.t3.model.CellPoint;
import com.t3.model.Zone;
public class AStarSquareEuclideanWalker extends AbstractAStarWalker {
private static final int[] NORTH = { 0, -1 };
private static final int[] WEST = { -1, 0 };
private static final int[] SOUTH = { 0, 1 };
private static final int[] EAST = { 1, 0 };
private static final int[] NORTH_EAST = { 1, -1 };
private static final int[] SOUTH_EAST = { 1, 1 };
private static final int[] NORTH_WEST = { -1, -1 };
private static final int[] SOUTH_WEST = { -1, 1 };
private final WalkerMetric metric;
private final int[][] neighborMap;
public AStarSquareEuclideanWalker(Zone zone, WalkerMetric metric) {
super(zone);
this.metric = metric;
// If we exposed this list of coordinates to the user, they could define their own movement
// criteria, including whether to favor the diagonals or the non-diagonals.
switch (metric) {
case NO_DIAGONALS:
neighborMap = new int[][] { NORTH, EAST, SOUTH, WEST };
break;
case ONE_ONE_ONE:
case MANHATTAN:
// promote straight directions to avoid 'only-diagonals' effect
neighborMap = new int[][] { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
break;
default:
// promote diagonals over straight directions by putting them at the front of the array
neighborMap = new int[][] { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST, NORTH, EAST, SOUTH, WEST };
break;
}
}
@Override
public int[][] getNeighborMap(int x, int y) {
return neighborMap;
}
@Override
protected double gScore(CellPoint p1, CellPoint p2) {
return metricDistance(p1, p2);
}
@Override
protected double hScore(CellPoint p1, CellPoint p2) {
return metricDistance(p1, p2);
}
private double metricDistance(CellPoint p1, CellPoint p2) {
int a = p2.x - p1.x;
int b = p2.y - p1.y;
final double distance;
switch (metric) {
// case ONE_ONE_ONE:
// distance = Math.max(Math.abs(a),Math.abs(b));
// break;
case MANHATTAN:
case NO_DIAGONALS:
distance = Math.abs(a) + Math.abs(b);
break;
default:
case ONE_TWO_ONE:
case ONE_ONE_ONE:
distance = Math.sqrt(a * a + b * b);
break;
}
return distance;
}
@Override
protected int calculateDistance(List<CellPoint> path, int feetPerCell) {
if (path == null || path.size() == 0)
return 0;
final int feetDistance;
{
int numDiag = 0;
int numStrt = 0;
CellPoint previousPoint = null;
for (CellPoint point : path) {
if (previousPoint != null) {
int change = Math.abs(previousPoint.x - point.x) + Math.abs(previousPoint.y - point.y);
switch (change) {
case 1:
numStrt++;
break;
case 2:
numDiag++;
break;
default:
assert false : String.format("Illegal path, cells are not contiguous; change=%d", change);
return -1;
}
}
previousPoint = point;
}
final int cellDistance;
switch (metric) {
case MANHATTAN:
case NO_DIAGONALS:
cellDistance = (numStrt + numDiag * 2);
break;
case ONE_ONE_ONE:
cellDistance = (numStrt + numDiag);
break;
default:
case ONE_TWO_ONE:
cellDistance = (numStrt + numDiag + numDiag / 2);
break;
}
feetDistance = feetPerCell * cellDistance;
}
return feetDistance;
}
}