Scurry.PolygonMap (Scurry v3.0.1)
View SourceUtility functions to work on a polygon map.
A polygon map is a set of a primary polygon - the main boundary that outlines the world - and a list of polygons that make holes in the main polygon.
See Polygon for details on how polygons are composed.
The use case is eg. making a map (the main polygon) with obstacles (the
holes), and use the Astar module to find the shortest path between
points in the map.
See Quickstart for a concrete end-to-end example of
defining a world and holes/obstacles, then using PolygonMap and Astar
modules to do path finding within this.
Summary
Types
The cost of a traversel between two nodes is a numeric value
Function that given two gnode/0 graph nodes, computes the cost. Eg. a euclidian 2D vector function like Scurry.Vector.distance/2. (gnode, gnode -> cost)
A vector is represented as a tuple {x, y}, it's x and y components, both number/0. These are used for vector math in Scurry.Vector and as 2D points in Scurry.Polygon and Scurry.PolygonMap.
Functions
Given a polygon map (world & holes) and list of vertices, makes the walk
graph for a A-star search.
Given a polygon map (main & holes), list of vertices and the initial graph
from create_graph/4, extend the graph with extra points.
Given a polygon map (world & holes), returns a list of vertices for a
walkmap for A-star search
Checks if there's a line-of-sight (LOS) from start to stop within the map.
Find the nearest point on the line that is inside the world polygion and outside all hole polygons.
Types
@type cost() :: number()
The cost of a traversel between two nodes is a numeric value
Function that given two gnode/0 graph nodes, computes the cost. Eg. a euclidian 2D vector function like Scurry.Vector.distance/2. (gnode, gnode -> cost)
@type gnode() :: any()
A graph node (spelled gnode since node is reserved) is any type that can be used as a key in a map/0 For instance a vector/0.
A graph is a map from gnode/0 to a list of gnode/0 and the traversal cost/0, %{gnode => [{gnode, cost}, ...]}
A line is represented by two vertices of type vector/0
@type polygon() :: [vector()]
A polygon is represented by a list of vector/0, each being a tuple of {x, y} coordinates, both number/0. To be properly defined, the vertices of the polygon must be non-closed and clockwise.
A vector is represented as a tuple {x, y}, it's x and y components, both number/0. These are used for vector math in Scurry.Vector and as 2D points in Scurry.Polygon and Scurry.PolygonMap.
Functions
Given a polygon map (world & holes) and list of vertices, makes the walk
graph for a A-star search.
Params
world(polygon/0) the polygon that defines the outer boundary.holes(list/0), a list ofpolygon/0that define holes/obstacles insideworld.vertices(list/0), the result ofget_vertices/2.cost_fun(cost_fun/0) a "cost" function.
The cost_fun is a function that indicates the of traversing between to
points. It defaults to Scurry.Vector.distance/2, which is suitable for
basic 2D map walking.
Returns
A graph/0, which is a map of all node to node reachable edges and their
cost. This graphs is one of the parameters for a call to Astar.search/4.
Todo
This should ideally take
line_of_sight/3as a function so users can customise which vertices can reach each other. But for now, users can make the graph themselves just as easily.
Given a polygon map (main & holes), list of vertices and the initial graph
from create_graph/4, extend the graph with extra points.
This is used to "temporarily" expand the created walk graph with a start and end-point. This is a minor performance optimisation that saves work by reusing the fixed nodes and extend it with the moveable points.
Params
graph(graph/0) the walk graph, as created bycreate_graph/4. This is used to return a new graph that's extended withpoints.world(polygon/0) the polygon that defines the outer boundary.holes(list/0), a list ofpolygon/0that define holes/obstacles insideworld.vertices(list/0) the list of vertices returned byget_vertices/4and used forcreate_graph/4to getgraph.points(list/0) a list of vectors (vector/0),[{x, y}, {x, y}...], to extend with. Eg. a stopcost_fun(cost_fun/0) a "cost" function.
world and holes need to be passed in in addition to graph, so the
function can determine line-of-sights between the existing graph and new
points from points.
The cost_fun is a function that indicates the of traversing between to
points. It defaults to Scurry.Vector.distance/2, which is suitable for
basic 2D map walking.
Returns
A tuple with the extended graph plus the combined list of vertices and new
points, {new_graph, new_vertices}. The new_graph is intended to be used
in calls to Astar.search/4, and new_vertices is solely provided as
informational, eg. for displaying the graphs visually.
Given a polygon map (world & holes), returns a list of vertices for a
walkmap for A-star search
Params
world(polygon/0), the polygon that defines the outer boundary.holes(list/0), a list ofpolygon/0that define holes/obstacles insideworld.
Returns
The walkmap as a list/0 of vector/0 that are the worlds's concave
vertices and the convex ones of the holes.
These are used when generating the walk map, since only the world's concave
and the holes' convex ones limit where you can traverse in a 2D map. Any
path has to walk around the holes' convex points and the world's concave.
See Quickstart for a concrete example.
Checks if there's a line-of-sight (LOS) from start to stop within the map.
Params
polygon, a list of{x, y}vertices. This is the main boundary map.holes, a list of lists of{x, y}vertices. These are holes withinpolygon.linea tuple of points ({{ax, ay}, {bx, by}}) describing a line.
Returns true if there's a line-of-sight and none of the main polygon or
holes obstruct the path. false otherwise.
As the map consists of a boundary polygon with holes, LOS implies a few things;
- If either
startorstopis outsidepolygon, the result will be false. Even if both are outside, that's not considered a valid LOS. - If the distance between
startandstopis tiny (< 0.1 arbitrarily), LOS is true. - Next, it checks that the line between
startandstophas no intersections withpolygonorholes. - Finally it checks if the middle of the line between
startandstopis insidepolygonand outside all holes - this ensures that corner-to-corner across a hole isn't considered a LOS.
Find the nearest point on the line that is inside the world polygion and outside all hole polygons.
The purpose of this function is to determine compute a suitable end for the
points parameter for extend_graph/6. Imagine a UI in which the users
clicks on a point to walk to. If that click is outside the world map or
inside a hole/obstacle, we can either refuse to compute a path, or we can use
nearest_point/3 to find the closest reachable point to the click.
Params
world(polygon/0) the polygon that defines the outer boundary.holes(list/0), a list ofpolygon/0that define holes/obstacles insideworld.point(vector/0) the point for which to find the nearst point.
Returns
A new point (vector/0) p={x, y} such that;
if
pointis outside the world polygon, the newpis the closest point on the edge of theworldpolygon.if
pointis inside the world polygon, but also inside any hole polygon, thepthe closest point on the edge of the hole it's in.