gtsocial-umbx

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

point_measures.go (6420B)


      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 import (
     18 	"math"
     19 
     20 	"github.com/golang/geo/s1"
     21 )
     22 
     23 // PointArea returns the area of triangle ABC. This method combines two different
     24 // algorithms to get accurate results for both large and small triangles.
     25 // The maximum error is about 5e-15 (about 0.25 square meters on the Earth's
     26 // surface), the same as GirardArea below, but unlike that method it is
     27 // also accurate for small triangles. Example: when the true area is 100
     28 // square meters, PointArea yields an error about 1 trillion times smaller than
     29 // GirardArea.
     30 //
     31 // All points should be unit length, and no two points should be antipodal.
     32 // The area is always positive.
     33 func PointArea(a, b, c Point) float64 {
     34 	// This method is based on l'Huilier's theorem,
     35 	//
     36 	//   tan(E/4) = sqrt(tan(s/2) tan((s-a)/2) tan((s-b)/2) tan((s-c)/2))
     37 	//
     38 	// where E is the spherical excess of the triangle (i.e. its area),
     39 	//       a, b, c are the side lengths, and
     40 	//       s is the semiperimeter (a + b + c) / 2.
     41 	//
     42 	// The only significant source of error using l'Huilier's method is the
     43 	// cancellation error of the terms (s-a), (s-b), (s-c). This leads to a
     44 	// *relative* error of about 1e-16 * s / min(s-a, s-b, s-c). This compares
     45 	// to a relative error of about 1e-15 / E using Girard's formula, where E is
     46 	// the true area of the triangle. Girard's formula can be even worse than
     47 	// this for very small triangles, e.g. a triangle with a true area of 1e-30
     48 	// might evaluate to 1e-5.
     49 	//
     50 	// So, we prefer l'Huilier's formula unless dmin < s * (0.1 * E), where
     51 	// dmin = min(s-a, s-b, s-c). This basically includes all triangles
     52 	// except for extremely long and skinny ones.
     53 	//
     54 	// Since we don't know E, we would like a conservative upper bound on
     55 	// the triangle area in terms of s and dmin. It's possible to show that
     56 	// E <= k1 * s * sqrt(s * dmin), where k1 = 2*sqrt(3)/Pi (about 1).
     57 	// Using this, it's easy to show that we should always use l'Huilier's
     58 	// method if dmin >= k2 * s^5, where k2 is about 1e-2. Furthermore,
     59 	// if dmin < k2 * s^5, the triangle area is at most k3 * s^4, where
     60 	// k3 is about 0.1. Since the best case error using Girard's formula
     61 	// is about 1e-15, this means that we shouldn't even consider it unless
     62 	// s >= 3e-4 or so.
     63 	sa := float64(b.Angle(c.Vector))
     64 	sb := float64(c.Angle(a.Vector))
     65 	sc := float64(a.Angle(b.Vector))
     66 	s := 0.5 * (sa + sb + sc)
     67 	if s >= 3e-4 {
     68 		// Consider whether Girard's formula might be more accurate.
     69 		dmin := s - math.Max(sa, math.Max(sb, sc))
     70 		if dmin < 1e-2*s*s*s*s*s {
     71 			// This triangle is skinny enough to use Girard's formula.
     72 			area := GirardArea(a, b, c)
     73 			if dmin < s*0.1*area {
     74 				return area
     75 			}
     76 		}
     77 	}
     78 
     79 	// Use l'Huilier's formula.
     80 	return 4 * math.Atan(math.Sqrt(math.Max(0.0, math.Tan(0.5*s)*math.Tan(0.5*(s-sa))*
     81 		math.Tan(0.5*(s-sb))*math.Tan(0.5*(s-sc)))))
     82 }
     83 
     84 // GirardArea returns the area of the triangle computed using Girard's formula.
     85 // All points should be unit length, and no two points should be antipodal.
     86 //
     87 // This method is about twice as fast as PointArea() but has poor relative
     88 // accuracy for small triangles. The maximum error is about 5e-15 (about
     89 // 0.25 square meters on the Earth's surface) and the average error is about
     90 // 1e-15. These bounds apply to triangles of any size, even as the maximum
     91 // edge length of the triangle approaches 180 degrees. But note that for
     92 // such triangles, tiny perturbations of the input points can change the
     93 // true mathematical area dramatically.
     94 func GirardArea(a, b, c Point) float64 {
     95 	// This is equivalent to the usual Girard's formula but is slightly more
     96 	// accurate, faster to compute, and handles a == b == c without a special
     97 	// case. PointCross is necessary to get good accuracy when two of
     98 	// the input points are very close together.
     99 	ab := a.PointCross(b)
    100 	bc := b.PointCross(c)
    101 	ac := a.PointCross(c)
    102 
    103 	area := float64(ab.Angle(ac.Vector) - ab.Angle(bc.Vector) + bc.Angle(ac.Vector))
    104 	if area < 0 {
    105 		area = 0
    106 	}
    107 	return area
    108 }
    109 
    110 // SignedArea returns a positive value for counterclockwise triangles and a negative
    111 // value otherwise (similar to PointArea).
    112 func SignedArea(a, b, c Point) float64 {
    113 	return float64(RobustSign(a, b, c)) * PointArea(a, b, c)
    114 }
    115 
    116 // Angle returns the interior angle at the vertex B in the triangle ABC. The
    117 // return value is always in the range [0, pi]. All points should be
    118 // normalized. Ensures that Angle(a,b,c) == Angle(c,b,a) for all a,b,c.
    119 //
    120 // The angle is undefined if A or C is diametrically opposite from B, and
    121 // becomes numerically unstable as the length of edge AB or BC approaches
    122 // 180 degrees.
    123 func Angle(a, b, c Point) s1.Angle {
    124 	// PointCross is necessary to get good accuracy when two of the input
    125 	// points are very close together.
    126 	return a.PointCross(b).Angle(c.PointCross(b).Vector)
    127 }
    128 
    129 // TurnAngle returns the exterior angle at vertex B in the triangle ABC. The
    130 // return value is positive if ABC is counterclockwise and negative otherwise.
    131 // If you imagine an ant walking from A to B to C, this is the angle that the
    132 // ant turns at vertex B (positive = left = CCW, negative = right = CW).
    133 // This quantity is also known as the "geodesic curvature" at B.
    134 //
    135 // Ensures that TurnAngle(a,b,c) == -TurnAngle(c,b,a) for all distinct
    136 // a,b,c. The result is undefined if (a == b || b == c), but is either
    137 // -Pi or Pi if (a == c). All points should be normalized.
    138 func TurnAngle(a, b, c Point) s1.Angle {
    139 	// We use PointCross to get good accuracy when two points are very
    140 	// close together, and RobustSign to ensure that the sign is correct for
    141 	// turns that are close to 180 degrees.
    142 	angle := a.PointCross(b).Angle(b.PointCross(c).Vector)
    143 
    144 	// Don't return RobustSign * angle because it is legal to have (a == c).
    145 	if RobustSign(a, b, c) == CounterClockwise {
    146 		return angle
    147 	}
    148 	return -angle
    149 }