All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.sahits.game.openpatrician.engine.sea.AStarGraphProvider Maven / Gradle / Ivy

package ch.sahits.game.openpatrician.engine.sea;

import ch.sahits.game.openpatrician.engine.sea.model.GraphAStar;
import ch.sahits.game.openpatrician.event.data.GraphInitialisationComplete;
import ch.sahits.game.openpatrician.event.data.HeuristicGraphInitialisationComplete;
import ch.sahits.game.openpatrician.model.city.ICity;
import ch.sahits.game.openpatrician.utilities.annotation.ClassCategory;
import ch.sahits.game.openpatrician.utilities.annotation.EClassCategory;
import ch.sahits.game.openpatrician.utilities.annotation.MapType;
import com.carrotsearch.hppc.ObjectDoubleMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import javafx.geometry.Point2D;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

/**
 * Factory class to provide the graph for the AStar path finding calculation
 * @author Andi Hotz, (c) Sahits GmbH, 2016
 *         Created on Jan 01, 2016
 */
@ClassCategory(EClassCategory.SINGLETON_BEAN)
@Component
@Lazy
public class AStarGraphProvider extends BaseGraphCalulationService {
    @Autowired
    private AStarHeuristicProvider heuristicProvider;
    private final Logger logger = LogManager.getLogger(getClass());


    @Autowired
    @Qualifier("serverThreadPool")
    private Executor serverThreadPool;
    @Autowired
    @Qualifier("serverClientEventBus")
    private AsyncEventBus clientServerEventBus;

    private final Object lock = new Object();


    private int width;
    private int height;

    private GraphAStar graph;
    @MapType(key = Point2D.class, value = Map.class)
    private Map> heuristic;


    @PostConstruct
    private void init(){
        clientServerEventBus.register(this);
        try {
            initImage();
        } catch (IOException e) {
            logger.error("Failed to create black and white image from the map", e);
        }
    }
    @Subscribe
    public void initGraph(HeuristicGraphInitialisationComplete event) {
        serverThreadPool.execute(this::createGraph);
    }

    @VisibleForTesting
    void createGraph() {
        long timestamp = System.currentTimeMillis();
        synchronized (lock) {
            heuristic = heuristicProvider.getHeuristic();
            while(heuristic.size() == 0) {
                Thread.yield();
                heuristic = heuristicProvider.getHeuristic();
            }
            graph = new GraphAStar<>(heuristic);
            List cities = map.getCities();
            for (int x = 0; x < width; x += CHECK_DISTANCE) {   // TODO: andi 1/3/16 split to differnt threads 
                for (int y = 0; y < height; y += CHECK_DISTANCE) {
                    // add edge from x,y -> x+dist,y  and back
                    Point2D p = getPoint(x, y);
                    if (isOnSea(p)) {
                        if (!graph.containsNode(p)) {
                            graph.addNodeInternal(p);
                        }
                        if (x + CHECK_DISTANCE <= width) { // destination point is still on the map
                            Point2D p2 = getPoint(x + CHECK_DISTANCE, y);
                            addEdges(p, p2, false, false);
                        }
                        // add edge from x,y -> x,y+dist and back
                        if (y + CHECK_DISTANCE <= height) {  // destination point is still on the map
                            Point2D p2 = getPoint(x, y + CHECK_DISTANCE);
                            addEdges(p, p2, false, false);
                        }
                        // add edge from x,y -> x+dist,y+dist and back
                        if (x + CHECK_DISTANCE <= width && y + CHECK_DISTANCE <= height) {
                            Point2D p2 = getPoint(x + CHECK_DISTANCE, y + CHECK_DISTANCE);
                            addEdges(p, p2, false, false);
                        }
                        // add edge from x,y -> x+dist,y-dist and back
                        if (x + CHECK_DISTANCE <= width && y - CHECK_DISTANCE >= 0) {
                            Point2D p2 = getPoint(x + CHECK_DISTANCE, y - CHECK_DISTANCE);
                            addEdges(p, p2, false, false);
                        }
                        // check if a city is near ( nearest = new ArrayList<>();
            for (Point2D node : graph) {
                double distance = node.distance(newPoint);
                if (distance <= DIAG_CHECK_DISTANCE) {
                    nearest.add(node);
                }
            }
            graph.addNode(newPoint, true);
            for (Point2D point : nearest) {
                addEdges(newPoint, point, isCity, false);
            }
        }
    }
    @VisibleForTesting
    void addSourcePointInternal(Point2D source, boolean isCity) {
        synchronized (lock) {
            if (!heuristic.containsKey(source)) {
                heuristicProvider.addSourceNodeToHeuristic(source);
            }
            if (graph.containsNode(source)) {
                return;
            }
            List nearest = new ArrayList<>();
            for (Point2D node : graph) {
                double distance = node.distance(source);
                if (distance <= DIAG_CHECK_DISTANCE) {
                    nearest.add(node);
                }
            }
            graph.addNode(source, false);
            for (Point2D point : nearest) {
                addEdges(source, point, isCity, false);
            }
        }
    }
    @Override
    protected double calculateWeight(Point2D from, Point2D to) {
        if (from.equals(to)) {
            return 0;
        }

        double distance = from.distance(to);
        try {
            int landPixels = imageService.countLandPixels(to, CHECK_DISTANCE*2, getSegments(from, to));
            int tangentialLandPixels = imageService.countLandPixels(to, (int) Math.round(DIAG_CHECK_DISTANCE), getTangentialSegments(from, to));
            double recastLandPixels =   landPixels/ (9.0 * 3);
            double recastTangentialPixels = tangentialLandPixels / (15.0);
            return distance + recastLandPixels + recastTangentialPixels;
        } catch (IOException e) {
            logger.error("Failed to count the land pixels near "+to, e);
        }
        return distance;
    }

    private void addEdges(Point2D from, Point2D to, boolean isCity, boolean initial) {
        if (isCity || isOnSea(to)) {
            if (!graph.containsNode(to)) {
                if (initial) {
                    graph.addNodeInternal(to);
                } else {
                    graph.addNode(to, true);
                }
            }
            heuristicProvider.getHeuristic();
            double weight = calculateWeight(from, to);
            if (initial) {
                graph.addEdgeInternal(from, to, weight);
            } else {
                graph.addEdge(from, to, weight);
            }
            weight = calculateWeight(to, from);
            if (initial) {
                graph.addEdgeInternal(to, from, weight);
            } else {
                graph.addEdge(to, from, weight);
            }
        }
    }


    @VisibleForTesting
    void initImage() throws IOException {
        width = imageService.getWidth();
        height = imageService.getHeight();
    }

    public GraphAStar getGraph() {
        synchronized (lock) {
            return graph;
        }
    }



}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy