gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

contains_point_query.go (6328B)


      1 // Copyright 2018 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package s2
     16 
     17 // VertexModel defines whether shapes are considered to contain their vertices.
     18 // Note that these definitions differ from the ones used by BooleanOperation.
     19 //
     20 // Note that points other than vertices are never contained by polylines.
     21 // If you want need this behavior, use ClosestEdgeQuery's IsDistanceLess
     22 // with a suitable distance threshold instead.
     23 type VertexModel int
     24 
     25 const (
     26 	// VertexModelOpen means no shapes contain their vertices (not even
     27 	// points). Therefore Contains(Point) returns true if and only if the
     28 	// point is in the interior of some polygon.
     29 	VertexModelOpen VertexModel = iota
     30 
     31 	// VertexModelSemiOpen means that polygon point containment is defined
     32 	// such that if several polygons tile the region around a vertex, then
     33 	// exactly one of those polygons contains that vertex. Points and
     34 	// polylines still do not contain any vertices.
     35 	VertexModelSemiOpen
     36 
     37 	// VertexModelClosed means all shapes contain their vertices (including
     38 	// points and polylines).
     39 	VertexModelClosed
     40 )
     41 
     42 // ContainsPointQuery determines whether one or more shapes in a ShapeIndex
     43 // contain a given Point. The ShapeIndex may contain any number of points,
     44 // polylines, and/or polygons (possibly overlapping). Shape boundaries may be
     45 // modeled as Open, SemiOpen, or Closed (this affects whether or not shapes are
     46 // considered to contain their vertices).
     47 //
     48 // This type is not safe for concurrent use.
     49 //
     50 // However, note that if you need to do a large number of point containment
     51 // tests, it is more efficient to re-use the query rather than creating a new
     52 // one each time.
     53 type ContainsPointQuery struct {
     54 	model VertexModel
     55 	index *ShapeIndex
     56 	iter  *ShapeIndexIterator
     57 }
     58 
     59 // NewContainsPointQuery creates a new instance of the ContainsPointQuery for the index
     60 // and given vertex model choice.
     61 func NewContainsPointQuery(index *ShapeIndex, model VertexModel) *ContainsPointQuery {
     62 	return &ContainsPointQuery{
     63 		index: index,
     64 		model: model,
     65 		iter:  index.Iterator(),
     66 	}
     67 }
     68 
     69 // Contains reports whether any shape in the queries index contains the point p
     70 // under the queries vertex model (Open, SemiOpen, or Closed).
     71 func (q *ContainsPointQuery) Contains(p Point) bool {
     72 	if !q.iter.LocatePoint(p) {
     73 		return false
     74 	}
     75 
     76 	cell := q.iter.IndexCell()
     77 	for _, clipped := range cell.shapes {
     78 		if q.shapeContains(clipped, q.iter.Center(), p) {
     79 			return true
     80 		}
     81 	}
     82 	return false
     83 }
     84 
     85 // shapeContains reports whether the clippedShape from the iterator's center position contains
     86 // the given point.
     87 func (q *ContainsPointQuery) shapeContains(clipped *clippedShape, center, p Point) bool {
     88 	inside := clipped.containsCenter
     89 	numEdges := clipped.numEdges()
     90 	if numEdges <= 0 {
     91 		return inside
     92 	}
     93 
     94 	shape := q.index.Shape(clipped.shapeID)
     95 	if shape.Dimension() != 2 {
     96 		// Points and polylines can be ignored unless the vertex model is Closed.
     97 		if q.model != VertexModelClosed {
     98 			return false
     99 		}
    100 
    101 		// Otherwise, the point is contained if and only if it matches a vertex.
    102 		for _, edgeID := range clipped.edges {
    103 			edge := shape.Edge(edgeID)
    104 			if edge.V0 == p || edge.V1 == p {
    105 				return true
    106 			}
    107 		}
    108 		return false
    109 	}
    110 
    111 	// Test containment by drawing a line segment from the cell center to the
    112 	// given point and counting edge crossings.
    113 	crosser := NewEdgeCrosser(center, p)
    114 	for _, edgeID := range clipped.edges {
    115 		edge := shape.Edge(edgeID)
    116 		sign := crosser.CrossingSign(edge.V0, edge.V1)
    117 		if sign == DoNotCross {
    118 			continue
    119 		}
    120 		if sign == MaybeCross {
    121 			// For the Open and Closed models, check whether p is a vertex.
    122 			if q.model != VertexModelSemiOpen && (edge.V0 == p || edge.V1 == p) {
    123 				return (q.model == VertexModelClosed)
    124 			}
    125 			// C++ plays fast and loose with the int <-> bool conversions here.
    126 			if VertexCrossing(crosser.a, crosser.b, edge.V0, edge.V1) {
    127 				sign = Cross
    128 			} else {
    129 				sign = DoNotCross
    130 			}
    131 		}
    132 		inside = inside != (sign == Cross)
    133 	}
    134 
    135 	return inside
    136 }
    137 
    138 // ShapeContains reports whether the given shape contains the point under this
    139 // queries vertex model (Open, SemiOpen, or Closed).
    140 //
    141 // This requires the shape belongs to this queries index.
    142 func (q *ContainsPointQuery) ShapeContains(shape Shape, p Point) bool {
    143 	if !q.iter.LocatePoint(p) {
    144 		return false
    145 	}
    146 
    147 	clipped := q.iter.IndexCell().findByShapeID(q.index.idForShape(shape))
    148 	if clipped == nil {
    149 		return false
    150 	}
    151 	return q.shapeContains(clipped, q.iter.Center(), p)
    152 }
    153 
    154 // shapeVisitorFunc is a type of function that can be called against shaped in an index.
    155 type shapeVisitorFunc func(shape Shape) bool
    156 
    157 // visitContainingShapes visits all shapes in the given index that contain the
    158 // given point p, terminating early if the given visitor function returns false,
    159 // in which case visitContainingShapes returns false. Each shape is
    160 // visited at most once.
    161 func (q *ContainsPointQuery) visitContainingShapes(p Point, f shapeVisitorFunc) bool {
    162 	// This function returns false only if the algorithm terminates early
    163 	// because the visitor function returned false.
    164 	if !q.iter.LocatePoint(p) {
    165 		return true
    166 	}
    167 
    168 	cell := q.iter.IndexCell()
    169 	for _, clipped := range cell.shapes {
    170 		if q.shapeContains(clipped, q.iter.Center(), p) &&
    171 			!f(q.index.Shape(clipped.shapeID)) {
    172 			return false
    173 		}
    174 	}
    175 	return true
    176 }
    177 
    178 // ContainingShapes returns a slice of all shapes that contain the given point.
    179 func (q *ContainsPointQuery) ContainingShapes(p Point) []Shape {
    180 	var shapes []Shape
    181 	q.visitContainingShapes(p, func(shape Shape) bool {
    182 		shapes = append(shapes, shape)
    183 		return true
    184 	})
    185 	return shapes
    186 }
    187 
    188 // TODO(roberts): Remaining methods from C++
    189 // type edgeVisitorFunc func(shape ShapeEdge) bool
    190 // func (q *ContainsPointQuery) visitIncidentEdges(p Point, v edgeVisitorFunc) bool