/* * Copyright 2016 Nathan Howard * * This file is part of OpenGrave * * OpenGrave is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenGrave is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenGrave. If not, see <http://www.gnu.org/licenses/>. */ package com.opengrave.common.pathing; import java.util.ArrayList; import com.opengrave.common.world.MovableObject; public class PathFinderPolygonAStar implements PathFinderPolygon { Path path = null; double size = 0.4f; private NavigationMesh mesh; private PathingNode start; private PathingNode end; boolean foundEnd = false, impossible = false; BinaryHeap<PathingNode> openList = new BinaryHeap<PathingNode>(); ArrayList<PathingNode> closedList = new ArrayList<PathingNode>(); public PathFinderPolygonAStar(NavigationMesh mesh, Point end) { this.mesh = mesh; ArrayList<PathingArea> endAreas = mesh.getPolygonsAt(end); if (endAreas.size() == 0) { impossible = true; } this.end = new PathingNode(end, null, Double.NaN, 0); } @Override public Path getPath() { return path; } @Override public void makePath(Point start) { if (impossible) { return; } ArrayList<PathingArea> startAreas = mesh.getPolygonsAt(start); if (startAreas.size() == 0) { return; } this.start = new PathingNode(start, null, 0, start.getDistance(end.getPoint())); int loopcount = 0; openList.add(this.start); Path path = new Path(); while (loopcount < 1000 && openList.size() > 0) { PathingNode point = openList.takeBest(); ArrayList<PathingArea> polyList = mesh.getPolygonsAt(point.getPoint()); if (mesh.lineOfSight(point.getPoint(), end.getPoint(), size)) { // This point can have a direct line to endpoint // Don't bail right away, check if there's any shorter distance for a few more rounds. if (loopcount < 990) { loopcount = 990; } double newScore = point.getGScore() + point.getPoint().getDistance(end.getPoint()); if (Double.isNaN(end.gScore) || end.gScore > newScore) { end.parent = point; end.gScore = newScore; end.hScore = 0; } // end.parent = point; foundEnd = true; // break; } for (PathingArea mainpoly : polyList) { // if size > 0.0 then this should be singular, but let's not bet on it addAllPointInPoly(point, mainpoly); // Check for paths to other points of same poly for (PathingEdge edge : mesh.getEdges(mainpoly)) { PathingArea poly = edge.getNeighbour(mainpoly); addAllPointInPoly(point, poly); } } closedList.add(point); } if (foundEnd) { addNode(path, end); this.path = path; return; } } private void addNode(Path path, PathingNode node) { if (node.parent != null) { addNode(path, node.parent); } path.addPoint(node.getPoint()); } public void addAllPointInPoly(PathingNode parent, PathingArea poly) { Polygon minigon = poly.getInnerPoly(size); for (Point p : minigon.getPoints()) { addPoint(p, parent); } } public void addPoint(Point point, PathingNode parent) { // First - check lineOfSight between points if (!mesh.lineOfSight(point, parent.getPoint(), size)) { return; } double gScore = parent.getGScore() + point.getDistance(parent.getPoint()); // Distance from start to new point, including detours double hScore = point.getDistance(end.getPoint()); // Direct distance to end. PathingNode tempNode = new PathingNode(point, parent, gScore, hScore); int idx = openList.indexOf(tempNode); if (idx != -1) { // It's in the open list PathingNode node = openList.get(idx); if (node.gScore > gScore) { node.parent = parent; node.gScore = gScore; openList.integrityChanged(node); } return; } idx = closedList.indexOf(tempNode); if (idx != -1) { // Is this redundant? We'll have to see PathingNode node = closedList.get(idx); if (node.gScore > gScore) { node.parent = parent; node.gScore = gScore; } return; } openList.add(tempNode); } @Override public boolean isComplete(MovableObject obj) { return end.getPoint().equals(new Point(obj.getLocation())); } }