View Source Scurry.Polygon (Scurry v2.0.0)
Functions related to polygons and lines relevant for 2D map pathfinding.
This provides functions for;
- line of sight between two points
- classify polygon vertices as concave or vertex
- intersections of lines and polygons
- checking if points are inside/outside polygons
- finding nearest point on a polygon and distances
Polygons are
- A list of vertices,
[{x1, y1}, {x2,y2}, ...]
. - They must not be closed, ie. last vertex should not be equal to the first.
- They must be in clockwise order in screen coordinates, otherwise convex/concave classification will be inversed as it traverses the egdes.
order-of-vertices
Order of verticesThey must be in clockwise order in screen coordinates, otherwise convex/concave classification will be inversed as it traverses the egdes.
Here's a crude drawing of an example of the M shaped polygon used for many tests/docs.
polygon = [{0, 0}, {10, 0}, {20, 0}, {20, 20}, {10, 10}, {0, 20}]
Link to this section Summary
Functions
Check if a vertex is concave, convex or neither.
Split a polygon into concave and convex vertices.
Get first intersections of a line with a polygon.
Get all intersections of a line with a polygon including their type.
Checks if a line intersects a polygon.
Check if the polygon is clockwise within screen coordinates.
Check if a vertex is concave or not.
Check if a vertex is convex or not.
Check if a point is inside a polygon or not.
The opposite of is_inside?/3
, provided for code readability.
Get last intersections of a line with a polygon.
Find the edge of a polygon nearest a given point
Find the point on the edge of a polygon nearest a given point.
Link to this section Functions
Check if a vertex is concave, convex or neither.
Whehter a vertex is concave or convex is defined by it pointing out - it's inner angle is less than 180 means convex and more than 180 means concave.
When testing a vertex, keep this in mind and negate appropriately depending on whether it's the boundary polygon or a hole polygon being tested.
params
Params
polygon
, a list of{x, y}
tuples outlining a polygon. This must be non-closed.at
, a position withinpolygon
to check.
Return
:convex
for a convex vertice.:concave
for a concave vertice.:neither
for a vertice that's a straight edge, ie. 180 degrees.
examples
Examples
# A vaguely M shaped polygon
iex> Polygon.classify_vertex([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 0)
:convex
iex> Polygon.classify_vertex([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 1)
:neither
iex> Polygon.classify_vertex([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 4)
:concave
Split a polygon into concave and convex vertices.
When doing pathfinding, there will typically be a outer polygon bounding the "world" and multiple inner polygons describing "holes". The path can only be within the outer polygon and has to "walk around" the holes.
Classifying the polygons into concave and convex gives the walkable graph.
- The outer polygon's concave (pointing into the world) vertices should be used.
- The holes' convex (point out of the hole, into the world) vertices should be used.
In code, this looks like
{concave, _convex} = Polygon.classify_vertices(world)
convex = Enum.reduce(holes, [], fn points, acc ->
{_, convex} = Polygon.classify_vertices(points)
acc ++ convex
end)
vertices = concave ++ convex
params
Params
polygon
, a list of{x, y}
tuples outlining a polygon. This must be non-closed.
Returns {list of concave vertices, list of convex}
.
Three points that fall on the same line ([{0, 0}, {1, 0}, {2, 0}]
) does not
match neither the concave/convex definition (angle gt/lt 180 degrees) this
will discard these via classify_vertex/2
.
examples
Examples
# A vaguely M shaped polygon
iex> Polygon.classify_vertices([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}])
{[{1, 0.5}], [{0, 0}, {2, 0}, {2, 1}, {0, 1}]}
Get first intersections of a line with a polygon.
The "opposite" of last_intersection/2
.
params
Params
polygon
a list of points ([{x, y}, {x, y}, ...]
) describing a polygon.line
a tuple of points ({{ax, ay}, {bx, by}}
) describing a line. The first tuple ({ax, ay}
) is considered the head of the line and "first" in this context means nearest to that point.
Returns a {x, y}
tuples indicating where the line first intersects, or nil
if there's no intersection.
Get all intersections of a line with a polygon including their type.
This function basically calls Geo.line_segment_intersection/2
for every segment
of the polygon
against the line
and filters the results to only include
the list of intersection points.
params
Params
polygon
a list of points ([{x, y}, {x, y}, ...]
) describing a polygon. This must be non-closed.line
a tuple of points ({{ax, ay}, {bx, by}}
) describing a line.opts
:allow_points
(defaultfalse
) whether aon_point
intersection should be considered an intersection or not. This varies from use cases. Eg. when building a polygon, points will be connected and thus intersect iftrue
. This may not be the desired result, sofalse
won't consider points intersections.
Returns a list of {x, y}
tuples indicating where the line intersects, or
[]
if there's no intersections.
examples
Examples
iex> polygon = [{0, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}]
[{0, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}]
iex> line = {{1, -1}, {1, 3}}
{{1, -1}, {1, 3}}
iex> Polygon.intersections(polygon, line)
[{1.0, 0.0}]
iex> Polygon.intersections(polygon, line, allow_points: true)
[{1.0, 0.0}, {1.0, 0.5}]
Checks if a line intersects a polygon.
This is a bare-minimum function, and for most cases using intersections/2
will be a better choice.
params
Params
polygon
a list of points ([{x, y}, {x, y}, ...]
) describing a polygon.line
a tuple of points ({{ax, ay}, {bx, by}}
) describing a line.
Returns true
or false
wether the line intersects the polygon or not.
Check if the polygon is clockwise within screen coordinates.
examples
Examples
iex> Polygon.is_clockwise?([{0, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}])
true
iex> Polygon.is_clockwise?([{0, 0}, {0, 1}, {1, 0.5}, {2, 1}, {2, 0}])
false
Check if a vertex is concave or not.
params
Params
polygon
, a list of{x, y}
tuples outlining a polygon. This must be non-closed.at
, a position withinpolygon
to check.
Return true
or false
.
Three points that fall on the same line ([{0, 0}, {1, 0}, {2, 0}]
) does not
match neither the concave/convex definition (angle gt/lt 180 degrees). This
will return false for such a vertex.
examples
Examples
# A vaguely M shaped polygon
iex> Polygon.is_concave?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 0)
false
iex> Polygon.is_concave?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 1)
false
iex> Polygon.is_concave?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 2)
false
iex> Polygon.is_concave?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 3)
false
iex> Polygon.is_concave?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 4)
true
iex> Polygon.is_concave?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 5)
false
Check if a vertex is convex or not.
params
Params
polygon
, a list of{x, y}
tuples outlining a polygon. This must be non-closed.at
, a position withinpolygon
to check.
Return true
or false
.
Three points that fall on the same line ([{0, 0}, {1, 0}, {2, 0}]
) does not
match neither the concave/convex definition (angle gt/lt 180 degrees). This
will return false for such a vertex.
examples
Examples
# A vaguely M shaped polygon
iex> Polygon.is_convex?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 0)
true
iex> Polygon.is_convex?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 1)
false
iex> Polygon.is_convex?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 2)
true
iex> Polygon.is_convex?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 3)
true
iex> Polygon.is_convex?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 4)
false
iex> Polygon.is_convex?([{0, 0}, {1, 0}, {2, 0}, {2, 1}, {1, 0.5}, {0, 1}], 5)
true
Check if a point is inside a polygon or not.
The opposite of is_inside?/3
, provided for code readability.
Get last intersections of a line with a polygon.
The "opposite" of first_intersection/2
.
params
Params
polygon
a list of points ([{x, y}, {x, y}, ...]
) describing a polygon.line
a tuple of points ({{ax, ay}, {bx, by}}
) describing a line. The second tuple ({bx, by}
) is considered the end of the line and "last" in this context means nearest to that point.
Returns a {x, y}
tuples indicating where the line last intersects, or nil
if there's no intersection.
Find the edge of a polygon nearest a given point
Given a point
that's inside or outside a given polygon
, this checks each
segment of the polygon, and returns the nearest one.
params
Params
polygon
, a list of{x, y}
vertices,[{x1, y2}, {x2, y2}, ...]
. This must be non-closed.point
a tuple{x, y}
describing a point
Returns the {{x1, y1}, {x2, y2}}
segment that is closest to the point.
Find the point on the edge of a polygon nearest a given point.
Given a point
that's inside or outside a given polygon
, this uses
nearest_edge/2
to find the closest edge and then computes the point on the
edge nearest the given point
.
params
Params
polygon
, a list of{x, y}
vertices,[{x1, y2}, {x2, y2}, ...]
. This must be non-closed.point
a tuple{x, y}
describing a point
Returns the {x, y}
on an edge of the polygon that is nearest point
.