Introduction
Be aware of:
- Floating point errors
- Nearly-parallel lines
- Very thin/long rectangles
- Points very close together
- Special cases
- Coincident points
- Intersecting lines
- Degenerate cases
- Zero area triangles
This course simplifies this by using integer cartesian grids and avoiding division, square roots, trig etc.
Sidenote - basic matplotlib example:
import matplotlib.pyplot as plt
vertices = [(0,0), (1,2), (2,4)]
vx, vy = zip(*vertices) # Unzip the array into x and y components
axes = plt.axes()
axes.plot(vx, vy, color="blue", marker="o")
plt.show()
Points, Vectors & Lines
Definitions
Point:
- Position in space
-
tuple - Uppercase, non-bold, non-italic letters
Vector:
- Difference between two points
-
tuple - The vector from
to is - Lowercase, bold, italic letters
- Position vector
- Vector from origin to some given point
Basic Operations
Points:
- Subtract to get displacement vector
- Linear/affine sum:
-
- Indivisible, two operation operand
- Points on the line between A and B
-
-
-
- The linear sum is the equation of the line: use this instead of
as it does not work with vertical lines
Vectors:
- Addition, subtractions
- Multiplication by scalar
- Addition to a point to get another point
- Magnitude:
- Dot product
- Signed triangle area
Vector Operations
Dot Product:
-
-
when the angle is acute: angle in or -
when they are perpendicular
Signed Area/Turn Direction
The area of parallelogram is:
This is equivalent to the magnitude of the cross product where
The area of triangle is half of this.
The area is positive if
Checking if
- Check if signed area 0
- To check if it is on the line segment (i.e. Between
and ): -
and - Where
is the distance between the points and
-
Turn Direction:
- Just check the sign of the signed area (unless the area is zero)
Line Segment Intersection:
- Two line segments
and intersect iff and are on opposite sides of the line and vice-versa - True if
isCCW(A,B,D) != isCCW(A,B,C) && isCCW(C,D,A) != isCCW(C,D,B)(where the first argument is the origin)
Degenerate cases:
- One point on the line
- Sharing a point
- All four points collinear
Simple Polygons
By convention, vertices are in counter-clockwise order.
Convex Polygons:
- Every interior angle less than 180 degrees
- Strictly less than for strictly convex polygon
- Every counter-clockwise traversal turns left at every vertex (or goes straight)
Point Inclusion Test
Convex Polygon
For a convex polygon, a point is in a polygon (PiP) if it is on the left side of every edge for an anticlockwise traversal of the polygon:
all([isCCW(vertex[i], vertex[(i + 1) % len(vertex)], P) for i in range(len(vertex))])
Simple Polygon
- Create a semi-infinite horizontal line starting from
going either left or right - The point is inside the polygon iff it intersects the polygon an odd number of times
- The line only needs to extend to past the maximum
value for the polygon’s edges
Special cases:
- The line goes through a vertex
- On the ‘inside’: count 0 or 2 times
- Otherwise: count 1 time
- Goes along an edge
The correct way to solve this is to:
- Ignore horizontal edges
- If edge directed upwards, count the start vertex as an intersection
- If edge directed down, count the end vertex as an intersection
The hacky solution: all vertices are on an integer grid, so simply move
Improvements:
- Find the rectangle that encompasses the polygon
- If its not in the rectangle, then it can’t be inside
Convex Hull Problem
For a finite set of points, the convex hull is the smallest convex polygon enclosing all points. The convex hull:
- Is unique
- Contains every point in the set
- has
, , and as vertices of the convex hull
Naïve solution:
- Construct all possible edges using pairs of points
- For every other point, check if it lies on the left side of the edge. If so, it belongs to the convex hull
-
Gift Wrap Algorithm
- Start from the point with the minimum
coordinate (or max , or min/max ) - Choose the left-most one if multiple points have the same minimum
- Select the ‘rightmost’ point from the perspective of the current point
- Pick any point
- For all points
- Check if
is to the right of - If so,
- Check if
- The current
is part of the convex hull. Repeat until a closed loop is formed
- Pick any point
- Worst case
- Normal case
, where is the number of vertices in the convex hull
Graham-Scan Algorithm
An
- Point
will have the minimum value, and so is guaranteed to be on the convex hull - Create a line centered around point
and pointing right - Rotate the line anticlockwise. When a vertex is found, add them to the list - we are essentially sorting the vertices by angle
- Connect the vertices in the sorted list to create the simple closed path
- Not all vertices will be on the convex hull, so the concavities need to be filled in
Sorting without calculating angles
We don’t need to know the angle: only the ordering. This can be done using isCCW, Python’s built in sort function (which will be from functools import cmp\_to\_key or by defining a SortKey class with a custom __lt__ operator and converting the point to that class in the sort function’s key lambda.
Consider edge cases:
-
or are the anchor - Several points colinear with
(i.e. on a straight line) - Don’t worry about them
Concavities:
-
and always on the convex hull -
may not be
Initialize a stack with
- Check if the next point,
, is on the left edge of the line connecting and - If so, add the point to the stack
- Otherwise, pop from the stack until this is true
Complexity:
- Step 1: find the point with the minimum
Y value: - Step 2: sort operation:
- Step 3: initialize hull
- Step 4: generate the convex hull
- Not
as although the nested while loop may run several times, it can only remove previously added elements, so you can only ever pop elements - Hence, it is
- Not