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

com.github.mathiewz.slick.util.pathfinding.navmesh.Space Maven / Gradle / Ivy

Go to download

The main purpose of this libraryis to modernize and maintain the slick2D library.

The newest version!
package com.github.mathiewz.slick.util.pathfinding.navmesh;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * A quad space within a navigation mesh
 *
 * @author kevin
 */
public class Space {
    /** The x coordinate of the top corner of the space */
    private final float x;
    /** The y coordinate of the top corner of the space */
    private final float y;
    /** The width of the space */
    private final float width;
    /** The height of the space */
    private final float height;

    /** A map from spaces to the links that connect them to this space */
    private final HashMap links = new HashMap<>();
    /** A list of the links from this space to others */
    private final ArrayList linksList = new ArrayList<>();
    /** The cost to get to this node */
    private float cost;

    /**
     * Create a new space
     *
     * @param x
     *            The x coordinate of the top corner of the space
     * @param y
     *            The y coordinate of the top corner of the space
     * @param width
     *            The width of the space
     * @param height
     *            The height of the space
     */
    public Space(float x, float y, float width, float height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    /**
     * Get the width of the space
     *
     * @return The width of the space
     */
    public float getWidth() {
        return width;
    }

    /**
     * Get the height of the space
     *
     * @return The height of the space
     */
    public float getHeight() {
        return height;
    }

    /**
     * Get the x coordinate of the top corner of the space
     *
     * @return The x coordinate of the top corner of the space
     */
    public float getX() {
        return x;
    }

    /**
     * Get the y coordinate of the top corner of the space
     *
     * @return The y coordinate of the top corner of the space
     */
    public float getY() {
        return y;
    }

    /**
     * Link this space to another by creating a link and finding the point
     * at which the spaces link up
     *
     * @param other
     *            The other space to link to
     */
    public void link(Space other) {
        // aligned vertical edges
        if (inTolerance(x, other.x + other.width) || inTolerance(x + width, other.x)) {
            float linkx = x;
            if (x + width == other.x) {
                linkx = x + width;
            }

            float top = Math.max(y, other.y);
            float bottom = Math.min(y + height, other.y + other.height);
            float linky = top + (bottom - top) / 2;

            Link link = new Link(linkx, linky, other);
            links.put(other, link);
            linksList.add(link);
        }
        // aligned horizontal edges
        if (inTolerance(y, other.y + other.height) || inTolerance(y + height, other.y)) {
            float linky = y;
            if (y + height == other.y) {
                linky = y + height;
            }

            float left = Math.max(x, other.x);
            float right = Math.min(x + width, other.x + other.width);
            float linkx = left + (right - left) / 2;

            Link link = new Link(linkx, linky, other);
            links.put(other, link);
            linksList.add(link);
        }
    }

    /**
     * Check whether two locations are within tolerance distance. This is
     * used when finding aligned edges to remove float rounding errors
     *
     * @param a
     *            The first value
     * @param b
     *            The second value
     * @return True if the edges are close enough (tm)
     */
    private boolean inTolerance(float a, float b) {
        return a == b;
    }

    /**
     * Check if this space has an edge that is joined with another
     *
     * @param other
     *            The other space to check against
     * @return True if the spaces have a shared edge
     */
    public boolean hasJoinedEdge(Space other) {
        // aligned vertical edges
        if (inTolerance(x, other.x + other.width) || inTolerance(x + width, other.x)) {
            if (y >= other.y && y <= other.y + other.height) {
                return true;
            }
            if (y + height >= other.y && y + height <= other.y + other.height) {
                return true;
            }
            if (other.y >= y && other.y <= y + height) {
                return true;
            }
            if (other.y + other.height >= y && other.y + other.height <= y + height) {
                return true;
            }
        }
        // aligned horizontal edges
        if (inTolerance(y, other.y + other.height) || inTolerance(y + height, other.y)) {
            if (x >= other.x && x <= other.x + other.width) {
                return true;
            }
            if (x + width >= other.x && x + width <= other.x + other.width) {
                return true;
            }
            if (other.x >= x && other.x <= x + width) {
                return true;
            }
            if (other.x + other.width >= x && other.x + other.width <= x + width) {
                return true;
            }
        }

        return false;
    }

    /**
     * Merge this space with another
     *
     * @param other
     *            The other space to merge with
     * @return The result space created by joining the two
     */
    public Space merge(Space other) {
        float minx = Math.min(x, other.x);
        float miny = Math.min(y, other.y);

        float newwidth = width + other.width;
        float newheight = height + other.height;
        if (x == other.x) {
            newwidth = width;
        } else {
            newheight = height;
        }
        return new Space(minx, miny, newwidth, newheight);
    }

    /**
     * Check if the given space can be merged with this one. It must have
     * an adjacent edge and have the same height or width as this space.
     *
     * @param other
     *            The other space to be considered
     * @return True if the spaces can be joined together
     */
    public boolean canMerge(Space other) {
        if (!hasJoinedEdge(other)) {
            return false;
        }

        if (x == other.x && width == other.width) {
            return true;
        }
        if (y == other.y && height == other.height) {
            return true;
        }

        return false;
    }

    /**
     * Get the number of links
     *
     * @return The number of links from the space to others
     */
    public int getLinkCount() {
        return linksList.size();
    }

    /**
     * Get the link from this space to another at a particular index
     *
     * @param index
     *            The index of the link to retrieve
     * @return The link from this space to another
     */
    public Link getLink(int index) {
        return linksList.get(index);
    }

    /**
     * Check if this space contains a given point
     *
     * @param xp
     *            The x coordinate to check
     * @param yp
     *            The y coordinate to check
     * @return True if this space container the coordinate given
     */
    public boolean contains(float xp, float yp) {
        return xp >= x && xp < x + width && yp >= y && yp < y + height;
    }

    /**
     * Fill the spaces based on the cost from a given starting point
     *
     * @param target
     *            The target space we're heading for
     * @param sx
     *            The x coordinate of the starting point
     * @param sy
     *            The y coordinate of the starting point
     * @param cost
     *            The cost up to this point
     */
    public void fill(Space target, float sx, float sy, float cost) {
        if (cost >= this.cost) {
            return;
        }
        this.cost = cost;
        if (target == this) {
            return;
        }

        for (int i = 0; i < getLinkCount(); i++) {
            Link link = getLink(i);
            float extraCost = link.distance2(sx, sy);
            float nextCost = cost + extraCost;
            link.getTarget().fill(target, link.getX(), link.getY(), nextCost);
        }
    }

    /**
     * Clear the costing values across the whole map
     */
    public void clearCost() {
        cost = Float.MAX_VALUE;
    }

    /**
     * Get the cost to get to this node at the moment
     *
     * @return The cost to get to this node
     */
    public float getCost() {
        return cost;
    }

    /**
     * Pick the lowest cost route from this space to another on the path
     *
     * @param target
     *            The target space we're looking for
     * @param path
     *            The path to add the steps to
     * @return True if the path was found
     */
    public boolean pickLowestCost(Space target, NavPath path) {
        if (target == this) {
            return true;
        }
        if (links.size() == 0) {
            return false;
        }

        Link bestLink = null;
        for (int i = 0; i < getLinkCount(); i++) {
            Link link = getLink(i);
            if (bestLink == null || link.getTarget().getCost() < bestLink.getTarget().getCost()) {
                bestLink = link;
            }
        }

        path.push(bestLink);
        return bestLink.getTarget().pickLowestCost(target, path);
    }

    /**
     * Get the string representation of this instance
     *
     * @return The string representation of this instance
     */
    @Override
    public String toString() {
        return "[Space " + x + "," + y + " " + width + "," + height + "]";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy