@@ -20,10 +20,6 @@ internal extension Double {
20
20
21
21
public struct S2 {
22
22
23
- enum Error : ErrorProtocol {
24
- case IllegalArgumentException
25
- }
26
-
27
23
// Together these flags define a cell orientation. If SWAP_MASK
28
24
// is true, then canonical traversal order is flipped around the
29
25
// diagonal (i.e. i and j are swapped with each other). If
@@ -68,8 +64,8 @@ public struct S2 {
68
64
69
65
- Returns: A bit mask containing some combination of {@link #SWAP_MASK} and {@link #INVERT_MASK}.
70
66
*/
71
- public static func posToOrientation( position: Int ) throws -> Int {
72
- guard 0 <= position && position < 4 else { throw Error . IllegalArgumentException }
67
+ public static func posToOrientation( position: Int ) -> Int {
68
+ precondition ( 0 <= position && position < 4 )
73
69
return posToOrientation [ position]
74
70
}
75
71
@@ -93,9 +89,9 @@ public struct S2 {
93
89
94
90
- Returns: The IJ-index where `0->(0,0), 1->(0,1), 2->(1,0), 3->(1,1)`.
95
91
*/
96
- public static func posToIJ( orientation: Int , position: Int ) throws -> Int {
97
- guard 0 <= orientation && orientation < 4 else { throw Error . IllegalArgumentException }
98
- guard 0 <= position && position < 4 else { throw Error . IllegalArgumentException }
92
+ public static func posToIJ( orientation: Int , position: Int ) -> Int {
93
+ precondition ( 0 <= orientation && orientation < 4 )
94
+ precondition ( 0 <= position && position < 4 )
99
95
return posToIJ [ orientation] [ position]
100
96
}
101
97
@@ -119,9 +115,9 @@ public struct S2 {
119
115
120
116
- Returns: The position of the subcell in the Hilbert traversal, in the range [0,3].
121
117
*/
122
- public static func toPos( orientation: Int , ijIndex: Int ) throws -> Int {
123
- guard 0 <= orientation && orientation < 4 else { throw Error . IllegalArgumentException }
124
- guard 0 <= ijIndex && ijIndex < 4 else { throw Error . IllegalArgumentException }
118
+ public static func toPos( orientation: Int , ijIndex: Int ) -> Int {
119
+ precondition ( 0 <= orientation && orientation < 4 )
120
+ precondition ( 0 <= ijIndex && ijIndex < 4 )
125
121
return ijToPos [ orientation] [ ijIndex]
126
122
}
127
123
@@ -310,7 +306,7 @@ public struct S2 {
310
306
let sa = b. angle ( to: c)
311
307
let sb = c. angle ( to: a)
312
308
let sc = a. angle ( to: b)
313
- let s = 0.5 * ( sa + sb + sc) ;
309
+ let s = 0.5 * ( sa + sb + sc)
314
310
if s >= 3e-4 {
315
311
// Consider whether Girard's formula might be more accurate.
316
312
let s2 = s * s
@@ -324,12 +320,7 @@ public struct S2 {
324
320
}
325
321
}
326
322
// Use l'Huilier's formula.
327
- return 4
328
- * atan(
329
- sqrt (
330
- max ( 0.0 ,
331
- tan ( 0.5 * s) * tan( 0.5 * ( s - sa) ) * tan( 0.5 * ( s - sb) )
332
- * tan( 0.5 * ( s - sc) ) ) ) )
323
+ return 4 * atan( sqrt ( max ( 0.0 , tan ( 0.5 * s) * tan( 0.5 * ( s - sa) ) * tan( 0.5 * ( s - sb) ) * tan( 0.5 * ( s - sc) ) ) ) )
333
324
}
334
325
335
326
/**
@@ -353,6 +344,77 @@ public struct S2 {
353
344
return area ( a: a, b: b, c: c) * Double( robustCCW ( a: a, b: b, c: c) )
354
345
}
355
346
347
+ // About centroids:
348
+ // ----------------
349
+ //
350
+ // There are several notions of the "centroid" of a triangle. First, there
351
+ // // is the planar centroid, which is simply the centroid of the ordinary
352
+ // (non-spherical) triangle defined by the three vertices. Second, there is
353
+ // the surface centroid, which is defined as the intersection of the three
354
+ // medians of the spherical triangle. It is possible to show that this
355
+ // point is simply the planar centroid projected to the surface of the
356
+ // sphere. Finally, there is the true centroid (mass centroid), which is
357
+ // defined as the area integral over the spherical triangle of (x,y,z)
358
+ // divided by the triangle area. This is the point that the triangle would
359
+ // rotate around if it was spinning in empty space.
360
+ //
361
+ // The best centroid for most purposes is the true centroid. Unlike the
362
+ // planar and surface centroids, the true centroid behaves linearly as
363
+ // regions are added or subtracted. That is, if you split a triangle into
364
+ // pieces and compute the average of their centroids (weighted by triangle
365
+ // area), the result equals the centroid of the original triangle. This is
366
+ // not true of the other centroids.
367
+ //
368
+ // Also note that the surface centroid may be nowhere near the intuitive
369
+ // "center" of a spherical triangle. For example, consider the triangle
370
+ // with vertices A=(1,eps,0), B=(0,0,1), C=(-1,eps,0) (a quarter-sphere).
371
+ // The surface centroid of this triangle is at S=(0, 2*eps, 1), which is
372
+ // within a distance of 2*eps of the vertex B. Note that the median from A
373
+ // (the segment connecting A to the midpoint of BC) passes through S, since
374
+ // this is the shortest path connecting the two endpoints. On the other
375
+ // hand, the true centroid is at M=(0, 0.5, 0.5), which when projected onto
376
+ // the surface is a much more reasonable interpretation of the "center" of
377
+ // this triangle.
378
+
379
+ /**
380
+ Return the centroid of the planar triangle ABC. This can be normalized to
381
+ unit length to obtain the "surface centroid" of the corresponding spherical
382
+ triangle, i.e. the intersection of the three medians. However, note that
383
+ for large spherical triangles the surface centroid may be nowhere near the
384
+ intuitive "center" (see example above).
385
+ */
386
+ public static func planarCentroid( a: S2Point , b: S2Point , c: S2Point ) -> S2Point {
387
+ return S2Point ( x: ( a. x + b. x + c. x) / 3.0 , y: ( a. y + b. y + c. y) / 3.0 , z: ( a. z + b. z + c. z) / 3.0 )
388
+ }
389
+
390
+ /**
391
+ Returns the true centroid of the spherical triangle ABC multiplied by the
392
+ signed area of spherical triangle ABC. The reasons for multiplying by the
393
+ signed area are (1) this is the quantity that needs to be summed to compute
394
+ the centroid of a union or difference of triangles, and (2) it's actually
395
+ easier to calculate this way.
396
+ */
397
+ public static func trueCentroid( a: S2Point , b: S2Point , c: S2Point ) -> S2Point {
398
+ // I couldn't find any references for computing the true centroid of a
399
+ // spherical triangle... I have a truly marvellous demonstration of this
400
+ // formula which this margin is too narrow to contain :)
401
+
402
+ // assert (isUnitLength(a) && isUnitLength(b) && isUnitLength(c));
403
+ let sina = b. crossProd ( c) . norm
404
+ let sinb = c. crossProd ( a) . norm
405
+ let sinc = a. crossProd ( b) . norm
406
+ let ra = ( sina == 0 ) ? 1 : ( asin ( sina) / sina)
407
+ let rb = ( sinb == 0 ) ? 1 : ( asin ( sinb) / sinb)
408
+ let rc = ( sinc == 0 ) ? 1 : ( asin ( sinc) / sinc)
409
+
410
+ // Now compute a point M such that M.X = rX * det(ABC) / 2 for X in A,B,C.
411
+ let x = S2Point ( x: a. x, y: b. x, z: c. x)
412
+ let y = S2Point ( x: a. y, y: b. y, z: c. y)
413
+ let z = S2Point ( x: a. z, y: b. z, z: c. z)
414
+ let r = S2Point ( x: ra, y: rb, z: rc)
415
+ return S2Point ( x: 0.5 * y. crossProd ( z) . dotProd ( r) , y: 0.5 * z. crossProd ( x) . dotProd ( r) , z: 0.5 * x. crossProd ( y) . dotProd ( r) )
416
+ }
417
+
356
418
/**
357
419
Return true if the points A, B, C are strictly counterclockwise. Return
358
420
false if the points are clockwise or colinear (i.e. if they are all
@@ -632,6 +694,16 @@ public struct S2 {
632
694
return ( robustCCW ( a: a, b: b, c: c) > 0 ) ? outAngle : - outAngle
633
695
}
634
696
697
+ /// Return true if two points are within the given distance of each other (mainly useful for testing).
698
+ public static func approxEquals( a: S2Point , b: S2Point , maxError: Double = 1e-15 ) -> Bool {
699
+ return a. angle ( to: b) <= maxError
700
+ }
701
+
702
+ /// Return true if two points are within the given distance of each other (mainly useful for testing).
703
+ public static func approxEquals( a: Double , b: Double , maxError: Double = 1e-15 ) -> Bool {
704
+ return abs ( a - b) <= maxError
705
+ }
706
+
635
707
// Don't instantiate
636
708
private init ( ) { }
637
709
0 commit comments