Documentation ¶
Index ¶
- Constants
- Variables
- func Area(s HasSignedArea) float64
- func CircularIndex(i, n int) int
- func Equal(a, b float64) bool
- func GreaterThan(a, b float64) bool
- func HandleTriangulatePanicRecover(r interface{}) error
- func IsCCW(s HasSignedArea) bool
- func IsCW(s HasSignedArea) bool
- func IterateGraph(root *QueryNode) chan *QueryNode
- func IterateTrapezoids(root *QueryNode) chan *Trapezoid
- func LessThan(a, b float64) bool
- type Direction
- type DirectionalPoint
- type GraphIterator
- type HasSignedArea
- type Point
- type PointSet
- type PointStack
- type Polygon
- type PolygonList
- type QueryGraph
- func (graph *QueryGraph) AddPolygon(poly Polygon, nondeterministic ...bool)
- func (g *QueryGraph) AddPolygons(list PolygonList)
- func (graph *QueryGraph) AddSegment(segment *Segment)
- func (g *QueryGraph) ContainsPoint(point *Point) bool
- func (graph *QueryGraph) FindPoint(dp DirectionalPoint) *QueryNode
- func (g *QueryGraph) IterateGraph() chan *QueryNode
- func (g *QueryGraph) IterateTrapezoids() chan *Trapezoid
- func (graph *QueryGraph) PrintAllTrapezoids()
- func (graph *QueryGraph) SplitTrapezoidHorizontally(node *QueryNode, point *Point)
- type QueryNode
- type QueryNodeInner
- type Segment
- func (s *Segment) Bottom() *Point
- func (s *Segment) IsHorizontal() bool
- func (s *Segment) IsLeftOf(p *Point) bool
- func (s *Segment) IsRightOf(p *Point) bool
- func (s *Segment) IsVertical() bool
- func (s *Segment) PointsDown() bool
- func (s *Segment) SolveForX(y float64) float64
- func (s *Segment) Top() *Point
- func (s *Segment) XDirection() XDirection
- type SinkNode
- type Trapezoid
- func (t *Trapezoid) BottomIntersectsSegment(segment *Segment) bool
- func (t *Trapezoid) CanMergeWith(other *Trapezoid) bool
- func (t *Trapezoid) DbgName() string
- func (t *Trapezoid) HasPoint(p *Point) bool
- func (t *Trapezoid) IsDegenerateOnSide(side YDirection) bool
- func (t *Trapezoid) IsInside() bool
- func (bottomTrapezoid *Trapezoid) NonzeroOverlapWithTrapezoidAbove(topTrapezoid *Trapezoid) bool
- func (t *Trapezoid) SegmentForSide(side XDirection) *Segment
- func (t *Trapezoid) SplitBySegment(segment *Segment) (left, right *Trapezoid)
- func (t *Trapezoid) String() string
- type TrapezoidNeighborList
- type TrapezoidSet
- type Triangle
- type TriangleList
- type TriangulateError
- type Vector
- type XDirection
- type XNode
- type YDirection
- type YNode
Constants ¶
const ( Down = iota Up )
const Epsilon = 1e-7
Variables ¶
var DefaultDirection = Direction{X: Left, Y: Down}
This is an arbitrary direction for when you don't really care (e.g. tests)
Functions ¶
func Area ¶
func Area(s HasSignedArea) float64
func CircularIndex ¶
Often we want to treat an array as a circular buffer. This gives the modular index given length n, but unlike the raw modulo operator, it only gives positive values
func Equal ¶
To compensate for imprecision in floats, equality is tolerance based. If we don't account for this, we'll end up shaving off absurdly thin triangles on nearly horizontal segments.
func GreaterThan ¶
func HandleTriangulatePanicRecover ¶
func HandleTriangulatePanicRecover(r interface{}) error
func IsCCW ¶
func IsCCW(s HasSignedArea) bool
func IsCW ¶
func IsCW(s HasSignedArea) bool
func IterateGraph ¶
func IterateTrapezoids ¶
Types ¶
type Direction ¶
type Direction struct { X XDirection Y YDirection }
type DirectionalPoint ¶
A point with a unit vector describing a direction.
func DefaultDirectionalPoint ¶
func DefaultDirectionalPoint(x, y float64) DirectionalPoint
Another convenience function, same concept as PointingRight
type GraphIterator ¶
type GraphIterator struct {
// contains filtered or unexported fields
}
A graph iterator lets you loop over the nodes in a graph exactly once. Traversal order is not defined. Behavior is also undefined if you modify the graph during iteration.
func NewGraphIterator ¶
func NewGraphIterator(root *QueryNode) *GraphIterator
func (*GraphIterator) MakeChan ¶
func (iter *GraphIterator) MakeChan() chan *QueryNode
Create a channel using a go routine to iterate over the subgraph. This provides a nicer API for looping, and allows the graph juggling to happen in another thread when possible.
func (*GraphIterator) Next ¶
func (iter *GraphIterator) Next() *QueryNode
type HasSignedArea ¶
type HasSignedArea interface { // Enclosed area of the structure, positive if counterclockwise, negative if clockwise. SignedArea() float64 }
Several properties can be derived from any structure that can compute its signed area.
type Point ¶
func (*Point) Below ¶
A common convention in our geometry is that if two points have the same Y value, the one with the smallex X value is "lower". This simulates a slightly rotated coordinate system, allowing us to assume Y values are never equal.
func (*Point) PointingAt ¶
func (p *Point) PointingAt(other *Point) DirectionalPoint
Create a directional point pointing at another point
func (*Point) PointingRight ¶
func (p *Point) PointingRight() DirectionalPoint
Convenience function setting the convention for finding points when you don't care about the direction.
type PointStack ¶
type PointStack []*Point
func (*PointStack) Empty ¶
func (s *PointStack) Empty() bool
func (*PointStack) Peek ¶
func (s *PointStack) Peek() *Point
func (*PointStack) Pop ¶
func (s *PointStack) Pop() *Point
func (*PointStack) Push ¶
func (s *PointStack) Push(p *Point)
type Polygon ¶
type Polygon struct {
Points []*Point
}
func (Polygon) ContainsPointByEvenOdd ¶
Even/odd point-in-polygon. This is provided primarily for testing of the Seidel algorithm. If you are checking many points inside the same large polygon, it can be more effficient to trapezoidize it and use the resulting QueryGraph.
Note that this is winding direction agnostic, so it will give a different answer from the Seidel algorithm if you add counter-clockwise holes, or clockwise outer polygons.
func (Polygon) CrossingCount ¶
Crossing count helper for even odd rule
func (*Polygon) SignedArea ¶
type PolygonList ¶
type PolygonList []Polygon
A list of polygons which must not have crossing segments. For the purposes of Seidel triangulation, holes must run clockwise, and the outer polygons must run counter-clockwise.
func ConvertToMonotones ¶
func ConvertToMonotones(list PolygonList) PolygonList
Use a query graph to split a set of polygons into monotone polygons.
func (PolygonList) ContainsPointByEvenOdd ¶
func (l PolygonList) ContainsPointByEvenOdd(p *Point) bool
func (PolygonList) CrossingCount ¶
func (l PolygonList) CrossingCount(p *Point) int
func (PolygonList) Triangulate ¶
func (list PolygonList) Triangulate() TriangleList
type QueryGraph ¶
type QueryGraph struct {
Root *QueryNode
}
func NewQueryGraph ¶
func NewQueryGraph(segment *Segment) *QueryGraph
Create a new graph from a single segment, and return the root node.
func (*QueryGraph) AddPolygon ¶
func (graph *QueryGraph) AddPolygon(poly Polygon, nondeterministic ...bool)
Add a polygon to the graph. If the polygon winds clockwise, this will end up producing a hole. Otherwise, it will be filled. The polygon must not intersect any existing segments in the graph.
By default, this process is pseudorandom, but deterministic. This is because predictable results are easier to debug. However, this raises the potential for adversarial inputs. If you are using untrusted input, you should pass "true" for proper randomization.
func (*QueryGraph) AddPolygons ¶
func (g *QueryGraph) AddPolygons(list PolygonList)
func (*QueryGraph) AddSegment ¶
func (graph *QueryGraph) AddSegment(segment *Segment)
func (*QueryGraph) ContainsPoint ¶
func (g *QueryGraph) ContainsPoint(point *Point) bool
Fast test for point-in-polygon using the trapezoid graph. Output is not defined for points exactly on the edge of the graph.
func (*QueryGraph) FindPoint ¶
func (graph *QueryGraph) FindPoint(dp DirectionalPoint) *QueryNode
func (*QueryGraph) IterateGraph ¶
func (g *QueryGraph) IterateGraph() chan *QueryNode
func (*QueryGraph) IterateTrapezoids ¶
func (g *QueryGraph) IterateTrapezoids() chan *Trapezoid
func (*QueryGraph) PrintAllTrapezoids ¶
func (graph *QueryGraph) PrintAllTrapezoids()
func (*QueryGraph) SplitTrapezoidHorizontally ¶
func (graph *QueryGraph) SplitTrapezoidHorizontally(node *QueryNode, point *Point)
Split a trapezoid horizontally, and replace its sink with a y node. node.Inner must be a sink
type QueryNode ¶
type QueryNode struct {
Inner QueryNodeInner
}
func (*QueryNode) ChildNodes ¶
func (*QueryNode) FindPoint ¶
func (n *QueryNode) FindPoint(dp DirectionalPoint) *QueryNode
type QueryNodeInner ¶
type QueryNodeInner interface { // Traverse the graph to find the sink whose trapezoid contains the point. The // direction argument is required to disambiguate when the point is an XNode // segment's endpoint. FindPoint(DirectionalPoint) *QueryNode // Child nodes is useful for iterating over a graph ChildNodes() []*QueryNode // contains filtered or unexported methods }
Query nodes are polymorphic, and we need to be able to replace the content with a different node type in O(1) time. Therefore, we use this interface to provide a union between the different types of query node.
type Segment ¶
Note that all points involved with the triangulation are pointers. This means they can be used as keys. We should never modify a point value from the original polygon, since some applications require exact equality, and we cannot tolerate loss of precision.
func (*Segment) IsHorizontal ¶
func (*Segment) IsLeftOf ¶
Is the line segment left of p. This assumes that P is vertically between the start and end of the segment
func (*Segment) IsVertical ¶
func (*Segment) PointsDown ¶
A segment points down if its start point is above its endpoint
func (*Segment) XDirection ¶
func (s *Segment) XDirection() XDirection
Determine which direction the segment points from top to bottom
o / <- Left o o \ <- Right o
type SinkNode ¶
type SinkNode struct { Trapezoid *Trapezoid // Before a sink has been merged, it will always have a single parent, which // this points to. After a merge, we no longer need to know the parent, and // this will be nil. InitialParent *QueryNode }
func (SinkNode) ChildNodes ¶
func (SinkNode) FindPoint ¶
func (node SinkNode) FindPoint(_ DirectionalPoint) *QueryNode
type Trapezoid ¶
type Trapezoid struct {
Left, Right *Segment
// The top and bottom are points, although geometrically, you can think of
// them as the y values of those points. There are two reasons that points
// must be used instead of y values:
//
// 1. A critical assumption of the algorithm is that no two points lie on the
// same horizontal. This is simulated by lexicographic ordering, but it means
// that _every_ Y comparison must have an X value involved to break ties.
//
// 2. The time will come when we ask every trapezoid "what points on your
// boundary are vertices of the polygon"? Because of the unique Y value
// assumption, the answer is _always_ two points. Those two points are the top
// and bottom fields. Note that in some cases, these will be an endpoint of a
// segment, and in some cases, they'll lie on the top or bottom of the
// trapezoid, away from the left and right sides.
Top, Bottom *Point
TrapezoidsAbove, TrapezoidsBelow TrapezoidNeighborList
Sink *QueryNode
}
func (*Trapezoid) BottomIntersectsSegment ¶
Check if a segment crosses the bottom edge of the trapezoid.
func (*Trapezoid) CanMergeWith ¶
func (*Trapezoid) HasPoint ¶
Check if the point is any of the (up to) six points involved with the trapezoid. If it is, then it's already a line segment in the graph.
func (*Trapezoid) IsDegenerateOnSide ¶
func (t *Trapezoid) IsDegenerateOnSide(side YDirection) bool
Check if the trapezoid has a degenerate side (is it a triangle). If either side is nil, then it's never degenerate. Otherwise, this holds when the corresponding segment endpoints are equal IFF the corresponding side of the trapezoid is that segment's start or end.
func (*Trapezoid) NonzeroOverlapWithTrapezoidAbove ¶
This is what decides if two trapezoids are neighbors.
func (*Trapezoid) SegmentForSide ¶
func (t *Trapezoid) SegmentForSide(side XDirection) *Segment
func (*Trapezoid) SplitBySegment ¶
Split a trapezoid vertically with a segment, returning the two trapezoids. It is assumed that the segment fully passes through the trapezoid. The resulting left and right trapezoids will not yet be in the query graph, and they will still point to the original trapezoid's sink. This must be fixed after trapezoids with agreeing edges are merged.
type TrapezoidNeighborList ¶
type TrapezoidNeighborList [3]*Trapezoid
Trapezoids can have up to two neighbors above and below them in the stable state, but while splitting, they can have up to three below. This should never be the case after splitting is complete.
func (*TrapezoidNeighborList) Add ¶
func (tl *TrapezoidNeighborList) Add(t *Trapezoid)
Append a trapezoid to the list, if it isn't already there
func (*TrapezoidNeighborList) AnyNeighbor ¶
func (tl *TrapezoidNeighborList) AnyNeighbor() *Trapezoid
func (*TrapezoidNeighborList) Remove ¶
func (tl *TrapezoidNeighborList) Remove(t *Trapezoid)
func (*TrapezoidNeighborList) ReplaceOrAdd ¶
func (tl *TrapezoidNeighborList) ReplaceOrAdd(orig *Trapezoid, replacement *Trapezoid)
Replace a trapezoid with another, or append it if the original isn't there
func (*TrapezoidNeighborList) String ¶
func (tl *TrapezoidNeighborList) String() string
type TrapezoidSet ¶
type TrapezoidSet map[*Trapezoid]struct{}
type Triangle ¶
type Triangle struct {
A, B, C *Point
}
func TriangulateMonotone ¶
func (*Triangle) SignedArea ¶
type TriangleList ¶
type TriangleList []*Triangle
func (TriangleList) ToPolygonList ¶
func (triangles TriangleList) ToPolygonList() PolygonList
Helper mostly used in tests. Converts the triangles into generic polygons.
type TriangulateError ¶
type TriangulateError error
type XDirection ¶
type XDirection int
const ( Left XDirection = iota Right )
func (XDirection) Opposite ¶
func (dir XDirection) Opposite() XDirection
type XNode ¶
An X node
func (XNode) ChildNodes ¶
func (XNode) FindPoint ¶
func (node XNode) FindPoint(dp DirectionalPoint) *QueryNode
type YDirection ¶
type YDirection int
func (YDirection) Opposite ¶
func (dir YDirection) Opposite() YDirection