gtsocial-umbx

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

centroids.go (5964B)


      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/r3"
     21 )
     22 
     23 // There are several notions of the "centroid" of a triangle. First, there
     24 // is the planar centroid, which is simply the centroid of the ordinary
     25 // (non-spherical) triangle defined by the three vertices. Second, there is
     26 // the surface centroid, which is defined as the intersection of the three
     27 // medians of the spherical triangle. It is possible to show that this
     28 // point is simply the planar centroid projected to the surface of the
     29 // sphere. Finally, there is the true centroid (mass centroid), which is
     30 // defined as the surface integral over the spherical triangle of (x,y,z)
     31 // divided by the triangle area. This is the point that the triangle would
     32 // rotate around if it was spinning in empty space.
     33 //
     34 // The best centroid for most purposes is the true centroid. Unlike the
     35 // planar and surface centroids, the true centroid behaves linearly as
     36 // regions are added or subtracted. That is, if you split a triangle into
     37 // pieces and compute the average of their centroids (weighted by triangle
     38 // area), the result equals the centroid of the original triangle. This is
     39 // not true of the other centroids.
     40 //
     41 // Also note that the surface centroid may be nowhere near the intuitive
     42 // "center" of a spherical triangle. For example, consider the triangle
     43 // with vertices A=(1,eps,0), B=(0,0,1), C=(-1,eps,0) (a quarter-sphere).
     44 // The surface centroid of this triangle is at S=(0, 2*eps, 1), which is
     45 // within a distance of 2*eps of the vertex B. Note that the median from A
     46 // (the segment connecting A to the midpoint of BC) passes through S, since
     47 // this is the shortest path connecting the two endpoints. On the other
     48 // hand, the true centroid is at M=(0, 0.5, 0.5), which when projected onto
     49 // the surface is a much more reasonable interpretation of the "center" of
     50 // this triangle.
     51 //
     52 
     53 // TrueCentroid returns the true centroid of the spherical triangle ABC
     54 // multiplied by the signed area of spherical triangle ABC. The reasons for
     55 // multiplying by the signed area are (1) this is the quantity that needs to be
     56 // summed to compute the centroid of a union or difference of triangles, and
     57 // (2) it's actually easier to calculate this way. All points must have unit length.
     58 //
     59 // Note that the result of this function is defined to be Point(0, 0, 0) if
     60 // the triangle is degenerate.
     61 func TrueCentroid(a, b, c Point) Point {
     62 	// Use Distance to get accurate results for small triangles.
     63 	ra := float64(1)
     64 	if sa := float64(b.Distance(c)); sa != 0 {
     65 		ra = sa / math.Sin(sa)
     66 	}
     67 	rb := float64(1)
     68 	if sb := float64(c.Distance(a)); sb != 0 {
     69 		rb = sb / math.Sin(sb)
     70 	}
     71 	rc := float64(1)
     72 	if sc := float64(a.Distance(b)); sc != 0 {
     73 		rc = sc / math.Sin(sc)
     74 	}
     75 
     76 	// Now compute a point M such that:
     77 	//
     78 	//  [Ax Ay Az] [Mx]                       [ra]
     79 	//  [Bx By Bz] [My]  = 0.5 * det(A,B,C) * [rb]
     80 	//  [Cx Cy Cz] [Mz]                       [rc]
     81 	//
     82 	// To improve the numerical stability we subtract the first row (A) from the
     83 	// other two rows; this reduces the cancellation error when A, B, and C are
     84 	// very close together. Then we solve it using Cramer's rule.
     85 	//
     86 	// The result is the true centroid of the triangle multiplied by the
     87 	// triangle's area.
     88 	//
     89 	// This code still isn't as numerically stable as it could be.
     90 	// The biggest potential improvement is to compute B-A and C-A more
     91 	// accurately so that (B-A)x(C-A) is always inside triangle ABC.
     92 	x := r3.Vector{a.X, b.X - a.X, c.X - a.X}
     93 	y := r3.Vector{a.Y, b.Y - a.Y, c.Y - a.Y}
     94 	z := r3.Vector{a.Z, b.Z - a.Z, c.Z - a.Z}
     95 	r := r3.Vector{ra, rb - ra, rc - ra}
     96 
     97 	return Point{r3.Vector{y.Cross(z).Dot(r), z.Cross(x).Dot(r), x.Cross(y).Dot(r)}.Mul(0.5)}
     98 }
     99 
    100 // EdgeTrueCentroid returns the true centroid of the spherical geodesic edge AB
    101 // multiplied by the length of the edge AB. As with triangles, the true centroid
    102 // of a collection of line segments may be computed simply by summing the result
    103 // of this method for each segment.
    104 //
    105 // Note that the planar centroid of a line segment is simply 0.5 * (a + b),
    106 // while the surface centroid is (a + b).Normalize(). However neither of
    107 // these values is appropriate for computing the centroid of a collection of
    108 // edges (such as a polyline).
    109 //
    110 // Also note that the result of this function is defined to be Point(0, 0, 0)
    111 // if the edge is degenerate.
    112 func EdgeTrueCentroid(a, b Point) Point {
    113 	// The centroid (multiplied by length) is a vector toward the midpoint
    114 	// of the edge, whose length is twice the sine of half the angle between
    115 	// the two vertices. Defining theta to be this angle, we have:
    116 	vDiff := a.Sub(b.Vector) // Length == 2*sin(theta)
    117 	vSum := a.Add(b.Vector)  // Length == 2*cos(theta)
    118 	sin2 := vDiff.Norm2()
    119 	cos2 := vSum.Norm2()
    120 	if cos2 == 0 {
    121 		return Point{} // Ignore antipodal edges.
    122 	}
    123 	return Point{vSum.Mul(math.Sqrt(sin2 / cos2))} // Length == 2*sin(theta)
    124 }
    125 
    126 // PlanarCentroid returns the centroid of the planar triangle ABC. This can be
    127 // normalized to unit length to obtain the "surface centroid" of the corresponding
    128 // spherical triangle, i.e. the intersection of the three medians. However, note
    129 // that for large spherical triangles the surface centroid may be nowhere near
    130 // the intuitive "center".
    131 func PlanarCentroid(a, b, c Point) Point {
    132 	return Point{a.Add(b.Vector).Add(c.Vector).Mul(1. / 3)}
    133 }