gtsocial-umbx

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

edge_crosser.go (8551B)


      1 // Copyright 2017 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 import (
     18 	"math"
     19 )
     20 
     21 // EdgeCrosser allows edges to be efficiently tested for intersection with a
     22 // given fixed edge AB. It is especially efficient when testing for
     23 // intersection with an edge chain connecting vertices v0, v1, v2, ...
     24 //
     25 // Example usage:
     26 //
     27 //	func CountIntersections(a, b Point, edges []Edge) int {
     28 //		count := 0
     29 //		crosser := NewEdgeCrosser(a, b)
     30 //		for _, edge := range edges {
     31 //			if crosser.CrossingSign(&edge.First, &edge.Second) != DoNotCross {
     32 //				count++
     33 //			}
     34 //		}
     35 //		return count
     36 //	}
     37 //
     38 type EdgeCrosser struct {
     39 	a   Point
     40 	b   Point
     41 	aXb Point
     42 
     43 	// To reduce the number of calls to expensiveSign, we compute an
     44 	// outward-facing tangent at A and B if necessary. If the plane
     45 	// perpendicular to one of these tangents separates AB from CD (i.e., one
     46 	// edge on each side) then there is no intersection.
     47 	aTangent Point // Outward-facing tangent at A.
     48 	bTangent Point // Outward-facing tangent at B.
     49 
     50 	// The fields below are updated for each vertex in the chain.
     51 	c   Point     // Previous vertex in the vertex chain.
     52 	acb Direction // The orientation of triangle ACB.
     53 }
     54 
     55 // NewEdgeCrosser returns an EdgeCrosser with the fixed edge AB.
     56 func NewEdgeCrosser(a, b Point) *EdgeCrosser {
     57 	norm := a.PointCross(b)
     58 	return &EdgeCrosser{
     59 		a:        a,
     60 		b:        b,
     61 		aXb:      Point{a.Cross(b.Vector)},
     62 		aTangent: Point{a.Cross(norm.Vector)},
     63 		bTangent: Point{norm.Cross(b.Vector)},
     64 	}
     65 }
     66 
     67 // CrossingSign reports whether the edge AB intersects the edge CD. If any two
     68 // vertices from different edges are the same, returns MaybeCross. If either edge
     69 // is degenerate (A == B or C == D), returns either DoNotCross or MaybeCross.
     70 //
     71 // Properties of CrossingSign:
     72 //
     73 //  (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d)
     74 //  (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d)
     75 //  (3) CrossingSign(a,b,c,d) == MaybeCross if a==c, a==d, b==c, b==d
     76 //  (3) CrossingSign(a,b,c,d) == DoNotCross or MaybeCross if a==b or c==d
     77 //
     78 // Note that if you want to check an edge against a chain of other edges,
     79 // it is slightly more efficient to use the single-argument version
     80 // ChainCrossingSign below.
     81 func (e *EdgeCrosser) CrossingSign(c, d Point) Crossing {
     82 	if c != e.c {
     83 		e.RestartAt(c)
     84 	}
     85 	return e.ChainCrossingSign(d)
     86 }
     87 
     88 // EdgeOrVertexCrossing reports whether if CrossingSign(c, d) > 0, or AB and
     89 // CD share a vertex and VertexCrossing(a, b, c, d) is true.
     90 //
     91 // This method extends the concept of a "crossing" to the case where AB
     92 // and CD have a vertex in common. The two edges may or may not cross,
     93 // according to the rules defined in VertexCrossing above. The rules
     94 // are designed so that point containment tests can be implemented simply
     95 // by counting edge crossings. Similarly, determining whether one edge
     96 // chain crosses another edge chain can be implemented by counting.
     97 func (e *EdgeCrosser) EdgeOrVertexCrossing(c, d Point) bool {
     98 	if c != e.c {
     99 		e.RestartAt(c)
    100 	}
    101 	return e.EdgeOrVertexChainCrossing(d)
    102 }
    103 
    104 // NewChainEdgeCrosser is a convenience constructor that uses AB as the fixed edge,
    105 // and C as the first vertex of the vertex chain (equivalent to calling RestartAt(c)).
    106 //
    107 // You don't need to use this or any of the chain functions unless you're trying to
    108 // squeeze out every last drop of performance. Essentially all you are saving is a test
    109 // whether the first vertex of the current edge is the same as the second vertex of the
    110 // previous edge.
    111 func NewChainEdgeCrosser(a, b, c Point) *EdgeCrosser {
    112 	e := NewEdgeCrosser(a, b)
    113 	e.RestartAt(c)
    114 	return e
    115 }
    116 
    117 // RestartAt sets the current point of the edge crosser to be c.
    118 // Call this method when your chain 'jumps' to a new place.
    119 // The argument must point to a value that persists until the next call.
    120 func (e *EdgeCrosser) RestartAt(c Point) {
    121 	e.c = c
    122 	e.acb = -triageSign(e.a, e.b, e.c)
    123 }
    124 
    125 // ChainCrossingSign is like CrossingSign, but uses the last vertex passed to one of
    126 // the crossing methods (or RestartAt) as the first vertex of the current edge.
    127 func (e *EdgeCrosser) ChainCrossingSign(d Point) Crossing {
    128 	// For there to be an edge crossing, the triangles ACB, CBD, BDA, DAC must
    129 	// all be oriented the same way (CW or CCW). We keep the orientation of ACB
    130 	// as part of our state. When each new point D arrives, we compute the
    131 	// orientation of BDA and check whether it matches ACB. This checks whether
    132 	// the points C and D are on opposite sides of the great circle through AB.
    133 
    134 	// Recall that triageSign is invariant with respect to rotating its
    135 	// arguments, i.e. ABD has the same orientation as BDA.
    136 	bda := triageSign(e.a, e.b, d)
    137 	if e.acb == -bda && bda != Indeterminate {
    138 		// The most common case -- triangles have opposite orientations. Save the
    139 		// current vertex D as the next vertex C, and also save the orientation of
    140 		// the new triangle ACB (which is opposite to the current triangle BDA).
    141 		e.c = d
    142 		e.acb = -bda
    143 		return DoNotCross
    144 	}
    145 	return e.crossingSign(d, bda)
    146 }
    147 
    148 // EdgeOrVertexChainCrossing is like EdgeOrVertexCrossing, but uses the last vertex
    149 // passed to one of the crossing methods (or RestartAt) as the first vertex of the current edge.
    150 func (e *EdgeCrosser) EdgeOrVertexChainCrossing(d Point) bool {
    151 	// We need to copy e.c since it is clobbered by ChainCrossingSign.
    152 	c := e.c
    153 	switch e.ChainCrossingSign(d) {
    154 	case DoNotCross:
    155 		return false
    156 	case Cross:
    157 		return true
    158 	}
    159 	return VertexCrossing(e.a, e.b, c, d)
    160 }
    161 
    162 // crossingSign handle the slow path of CrossingSign.
    163 func (e *EdgeCrosser) crossingSign(d Point, bda Direction) Crossing {
    164 	// Compute the actual result, and then save the current vertex D as the next
    165 	// vertex C, and save the orientation of the next triangle ACB (which is
    166 	// opposite to the current triangle BDA).
    167 	defer func() {
    168 		e.c = d
    169 		e.acb = -bda
    170 	}()
    171 
    172 	// At this point, a very common situation is that A,B,C,D are four points on
    173 	// a line such that AB does not overlap CD. (For example, this happens when
    174 	// a line or curve is sampled finely, or when geometry is constructed by
    175 	// computing the union of S2CellIds.) Most of the time, we can determine
    176 	// that AB and CD do not intersect using the two outward-facing
    177 	// tangents at A and B (parallel to AB) and testing whether AB and CD are on
    178 	// opposite sides of the plane perpendicular to one of these tangents. This
    179 	// is moderately expensive but still much cheaper than expensiveSign.
    180 
    181 	// The error in RobustCrossProd is insignificant. The maximum error in
    182 	// the call to CrossProd (i.e., the maximum norm of the error vector) is
    183 	// (0.5 + 1/sqrt(3)) * dblEpsilon. The maximum error in each call to
    184 	// DotProd below is dblEpsilon. (There is also a small relative error
    185 	// term that is insignificant because we are comparing the result against a
    186 	// constant that is very close to zero.)
    187 	maxError := (1.5 + 1/math.Sqrt(3)) * dblEpsilon
    188 	if (e.c.Dot(e.aTangent.Vector) > maxError && d.Dot(e.aTangent.Vector) > maxError) || (e.c.Dot(e.bTangent.Vector) > maxError && d.Dot(e.bTangent.Vector) > maxError) {
    189 		return DoNotCross
    190 	}
    191 
    192 	// Otherwise, eliminate the cases where two vertices from different edges are
    193 	// equal. (These cases could be handled in the code below, but we would rather
    194 	// avoid calling ExpensiveSign if possible.)
    195 	if e.a == e.c || e.a == d || e.b == e.c || e.b == d {
    196 		return MaybeCross
    197 	}
    198 
    199 	// Eliminate the cases where an input edge is degenerate. (Note that in
    200 	// most cases, if CD is degenerate then this method is not even called
    201 	// because acb and bda have different signs.)
    202 	if e.a == e.b || e.c == d {
    203 		return DoNotCross
    204 	}
    205 
    206 	// Otherwise it's time to break out the big guns.
    207 	if e.acb == Indeterminate {
    208 		e.acb = -expensiveSign(e.a, e.b, e.c)
    209 	}
    210 	if bda == Indeterminate {
    211 		bda = expensiveSign(e.a, e.b, d)
    212 	}
    213 
    214 	if bda != e.acb {
    215 		return DoNotCross
    216 	}
    217 
    218 	cbd := -RobustSign(e.c, d, e.b)
    219 	if cbd != e.acb {
    220 		return DoNotCross
    221 	}
    222 	dac := RobustSign(e.c, d, e.a)
    223 	if dac != e.acb {
    224 		return DoNotCross
    225 	}
    226 	return Cross
    227 }