diff options
| author | Felix Hanley <felix@userspace.com.au> | 2017-03-19 15:19:42 +0000 |
|---|---|---|
| committer | Felix Hanley <felix@userspace.com.au> | 2017-03-19 15:19:42 +0000 |
| commit | 8a541d499b6f117cd3a81e475ee779ba60fc0637 (patch) | |
| tree | 7b3b5326235725ab93056b5ff4637d987fb0a7b6 /vendor/github.com/golang/geo/s1 | |
| parent | fe847b2d01060044274d20d2c35ae01a684d4ee3 (diff) | |
| download | crjw-maps-8a541d499b6f117cd3a81e475ee779ba60fc0637.tar.gz crjw-maps-8a541d499b6f117cd3a81e475ee779ba60fc0637.tar.bz2 | |
Diffstat (limited to 'vendor/github.com/golang/geo/s1')
| -rw-r--r-- | vendor/github.com/golang/geo/s1/LICENSE | 202 | ||||
| -rw-r--r-- | vendor/github.com/golang/geo/s1/angle_test.go | 169 | ||||
| -rw-r--r-- | vendor/github.com/golang/geo/s1/chordangle.go | 12 | ||||
| -rw-r--r-- | vendor/github.com/golang/geo/s1/chordangle_test.go | 226 | ||||
| -rw-r--r-- | vendor/github.com/golang/geo/s1/interval_test.go | 457 |
5 files changed, 861 insertions, 205 deletions
diff --git a/vendor/github.com/golang/geo/s1/LICENSE b/vendor/github.com/golang/geo/s1/LICENSE deleted file mode 100644 index d645695..0000000 --- a/vendor/github.com/golang/geo/s1/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/golang/geo/s1/angle_test.go b/vendor/github.com/golang/geo/s1/angle_test.go new file mode 100644 index 0000000..2dd2e58 --- /dev/null +++ b/vendor/github.com/golang/geo/s1/angle_test.go @@ -0,0 +1,169 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package s1 + +import ( + "math" + "testing" +) + +// float64Eq reports whether the two values are within the default epsilon. +func float64Eq(x, y float64) bool { + return float64Near(x, y, epsilon) +} + +// float64Near reports whether the two values are within the specified epsilon. +func float64Near(x, y, eps float64) bool { + return math.Abs(x-y) <= eps +} + +func TestEmptyValue(t *testing.T) { + var a Angle + if rad := a.Radians(); rad != 0 { + t.Errorf("Empty value of Angle was %v, want 0", rad) + } +} + +func TestPiRadiansExactly180Degrees(t *testing.T) { + if rad := (math.Pi * Radian).Radians(); rad != math.Pi { + t.Errorf("(π * Radian).Radians() was %v, want π", rad) + } + if deg := (math.Pi * Radian).Degrees(); deg != 180 { + t.Errorf("(π * Radian).Degrees() was %v, want 180", deg) + } + if rad := (180 * Degree).Radians(); rad != math.Pi { + t.Errorf("(180 * Degree).Radians() was %v, want π", rad) + } + if deg := (180 * Degree).Degrees(); deg != 180 { + t.Errorf("(180 * Degree).Degrees() was %v, want 180", deg) + } + + if deg := (math.Pi / 2 * Radian).Degrees(); deg != 90 { + t.Errorf("(π/2 * Radian).Degrees() was %v, want 90", deg) + } + + // Check negative angles. + if deg := (-math.Pi / 2 * Radian).Degrees(); deg != -90 { + t.Errorf("(-π/2 * Radian).Degrees() was %v, want -90", deg) + } + if rad := (-45 * Degree).Radians(); rad != -math.Pi/4 { + t.Errorf("(-45 * Degree).Radians() was %v, want -π/4", rad) + } +} + +func TestE5E6E7Representation(t *testing.T) { + // NOTE(dsymonds): This first test gives a variance in the 16th decimal place. I should track that down. + exp, act := (-45 * Degree).Radians(), (-4500000 * E5).Radians() + if math.Abs(exp-act) > 1e-15 { + t.Errorf("(-4500000 * E5).Radians() was %v, want %v", act, exp) + } + if exp, act := (-60 * Degree).Radians(), (-60000000 * E6).Radians(); exp != act { + t.Errorf("(-60000000 * E6).Radians() was %v, want %v", act, exp) + } + if exp, act := (75 * Degree).Radians(), (750000000 * E7).Radians(); exp != act { + t.Errorf("(-750000000 * E7).Radians() was %v, want %v", act, exp) + } + + if exp, act := int32(-17256123), (-172.56123 * Degree).E5(); exp != act { + t.Errorf("(-172.56123°).E5() was %v, want %v", act, exp) + } + if exp, act := int32(12345678), (12.345678 * Degree).E6(); exp != act { + t.Errorf("(12.345678°).E6() was %v, want %v", act, exp) + } + if exp, act := int32(-123456789), (-12.3456789 * Degree).E7(); exp != act { + t.Errorf("(-12.3456789°).E7() was %v, want %v", act, exp) + } + + roundingTests := []struct { + have Angle + want int32 + }{ + {0.500000001, 1}, + {-0.500000001, -1}, + {0.499999999, 0}, + {-0.499999999, 0}, + } + for _, test := range roundingTests { + if act := (test.have * 1e-5 * Degree).E5(); test.want != act { + t.Errorf("(%v°).E5() was %v, want %v", test.have, act, test.want) + } + if act := (test.have * 1e-6 * Degree).E6(); test.want != act { + t.Errorf("(%v°).E6() was %v, want %v", test.have, act, test.want) + } + if act := (test.have * 1e-7 * Degree).E7(); test.want != act { + t.Errorf("(%v°).E7() was %v, want %v", test.have, act, test.want) + } + } +} + +func TestNormalizeCorrectlyCanonicalizesAngles(t *testing.T) { + tests := []struct { + in, want float64 // both in degrees + }{ + {360, 0}, + {-180, 180}, + {180, 180}, + {540, 180}, + {-270, 90}, + } + for _, test := range tests { + deg := (Angle(test.in) * Degree).Normalized().Degrees() + if deg != test.want { + t.Errorf("Normalized %.0f° = %v, want %v", test.in, deg, test.want) + } + } +} + +func TestAngleString(t *testing.T) { + if s, exp := (180 * Degree).String(), "180.0000000"; s != exp { + t.Errorf("(180°).String() = %q, want %q", s, exp) + } +} + +func TestDegreesVsRadians(t *testing.T) { + // This test tests the exactness of specific values between degrees and radians. + for k := -8; k <= 8; k++ { + if got, want := Angle(45*k)*Degree, Angle((float64(k)*math.Pi)/4)*Radian; got != want { + t.Errorf("45°*%d != (%d*π)/4 radians (%f vs %f)", k, k, got, want) + } + + if got, want := (Angle(45*k) * Degree).Degrees(), float64(45*k); got != want { + t.Errorf("Angle(45°*%d).Degrees() != 45*%d, (%f vs %f)", k, k, got, want) + } + } + + for k := uint64(0); k < 30; k++ { + m := 1 << k + n := float64(m) + for _, test := range []struct{ deg, rad float64 }{ + {180, 1}, + {60, 3}, + {36, 5}, + {20, 9}, + {4, 45}, + } { + if got, want := Angle(test.deg/n)*Degree, Angle(math.Pi/(test.rad*n))*Radian; got != want { + t.Errorf("%v°/%d != π/%v*%d rad (%f vs %f)", test.deg, m, test.rad, m, got, want) + } + } + } + + // We also spot check a non-identity. + if got := (60 * Degree).Degrees(); float64Eq(got, 60) { + t.Errorf("Angle(60).Degrees() == 60, but should not (%f vs %f)", got, 60.0) + } +} diff --git a/vendor/github.com/golang/geo/s1/chordangle.go b/vendor/github.com/golang/geo/s1/chordangle.go index 5f5832a..7fe06bb 100644 --- a/vendor/github.com/golang/geo/s1/chordangle.go +++ b/vendor/github.com/golang/geo/s1/chordangle.go @@ -164,8 +164,8 @@ func (c ChordAngle) Add(other ChordAngle) ChordAngle { return ChordAngle(math.Min(4.0, x+y+2*math.Sqrt(x*y))) } -// Sub subtracts the other ChordAngle from this one and returns the resulting value. -// This method assumes the ChordAngles are not special. +// Sub subtracts the other ChordAngle from this one and returns the resulting +// value. This method assumes the ChordAngles are not special. func (c ChordAngle) Sub(other ChordAngle) ChordAngle { if other == 0 { return c @@ -181,12 +181,18 @@ func (c ChordAngle) Sub(other ChordAngle) ChordAngle { // Sin returns the sine of this chord angle. This method is more efficient // than converting to Angle and performing the computation. func (c ChordAngle) Sin() float64 { + return math.Sqrt(c.Sin2()) +} + +// Sin2 returns the square of the sine of this chord angle. +// It is more efficient than Sin. +func (c ChordAngle) Sin2() float64 { // Let a be the (non-squared) chord length, and let A be the corresponding // half-angle (a = 2*sin(A)). The formula below can be derived from: // sin(2*A) = 2 * sin(A) * cos(A) // cos^2(A) = 1 - sin^2(A) // This is much faster than converting to an angle and computing its sine. - return math.Sqrt(float64(c * (1 - 0.25*c))) + return float64(c * (1 - 0.25*c)) } // Cos returns the cosine of this chord angle. This method is more efficient diff --git a/vendor/github.com/golang/geo/s1/chordangle_test.go b/vendor/github.com/golang/geo/s1/chordangle_test.go new file mode 100644 index 0000000..11c585d --- /dev/null +++ b/vendor/github.com/golang/geo/s1/chordangle_test.go @@ -0,0 +1,226 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package s1 + +import ( + "math" + "testing" +) + +func TestChordAngleBasics(t *testing.T) { + var zeroChord ChordAngle + tests := []struct { + a, b ChordAngle + lessThan bool + equal bool + }{ + {NegativeChordAngle, NegativeChordAngle, false, true}, + {NegativeChordAngle, zeroChord, true, false}, + {NegativeChordAngle, StraightChordAngle, true, false}, + {NegativeChordAngle, InfChordAngle(), true, false}, + + {zeroChord, zeroChord, false, true}, + {zeroChord, StraightChordAngle, true, false}, + {zeroChord, InfChordAngle(), true, false}, + + {StraightChordAngle, StraightChordAngle, false, true}, + {StraightChordAngle, InfChordAngle(), true, false}, + + {InfChordAngle(), InfChordAngle(), false, true}, + {InfChordAngle(), InfChordAngle(), false, true}, + } + + for _, test := range tests { + if got := test.a < test.b; got != test.lessThan { + t.Errorf("%v should be less than %v", test.a, test.b) + } + if got := test.a == test.b; got != test.equal { + t.Errorf("%v should be equal to %v", test.a, test.b) + } + } +} + +func TestChordAngleIsFunctions(t *testing.T) { + var zeroChord ChordAngle + tests := []struct { + have ChordAngle + isNegative bool + isZero bool + isInf bool + isSpecial bool + }{ + {zeroChord, false, true, false, false}, + {NegativeChordAngle, true, false, false, true}, + {zeroChord, false, true, false, false}, + {StraightChordAngle, false, false, false, false}, + {InfChordAngle(), false, false, true, true}, + } + + for _, test := range tests { + if got := test.have < 0; got != test.isNegative { + t.Errorf("%v.isNegative() = %t, want %t", test.have, got, test.isNegative) + } + if got := test.have == 0; got != test.isZero { + t.Errorf("%v.isZero() = %t, want %t", test.have, got, test.isZero) + } + if got := test.have.isInf(); got != test.isInf { + t.Errorf("%v.isInf() = %t, want %t", test.have, got, test.isInf) + } + if got := test.have.isSpecial(); got != test.isSpecial { + t.Errorf("%v.isSpecial() = %t, want %t", test.have, got, test.isSpecial) + } + } +} + +func TestChordAngleFromAngle(t *testing.T) { + for _, angle := range []float64{0, 1, -1, math.Pi} { + if got := ChordAngleFromAngle(Angle(angle)).Angle().Radians(); got != angle { + t.Errorf("ChordAngleFromAngle(Angle(%v)) = %v, want %v", angle, got, angle) + } + } + + if got := ChordAngleFromAngle(Angle(math.Pi)); got != StraightChordAngle { + t.Errorf("a ChordAngle from an Angle of π = %v, want %v", got, StraightChordAngle) + } + + if InfAngle() != ChordAngleFromAngle(InfAngle()).Angle() { + t.Errorf("converting infinite Angle to ChordAngle should yield infinite Angle") + } +} + +func TestChordAngleArithmetic(t *testing.T) { + var ( + zero ChordAngle + degree30 = ChordAngleFromAngle(30 * Degree) + degree60 = ChordAngleFromAngle(60 * Degree) + degree90 = ChordAngleFromAngle(90 * Degree) + degree120 = ChordAngleFromAngle(120 * Degree) + degree180 = StraightChordAngle + ) + + addTests := []struct { + a, b ChordAngle + want ChordAngle + }{ + {zero, zero, zero}, + {degree60, zero, degree60}, + {zero, degree60, degree60}, + {degree30, degree60, degree90}, + {degree60, degree30, degree90}, + {degree180, zero, degree180}, + {degree60, degree30, degree90}, + {degree90, degree90, degree180}, + {degree120, degree90, degree180}, + {degree120, degree120, degree180}, + {degree30, degree180, degree180}, + {degree180, degree180, degree180}, + } + + subTests := []struct { + a, b ChordAngle + want ChordAngle + }{ + {zero, zero, zero}, + {degree60, degree60, zero}, + {degree180, degree180, zero}, + {zero, degree60, zero}, + {degree30, degree90, zero}, + {degree90, degree30, degree60}, + {degree90, degree60, degree30}, + {degree180, zero, degree180}, + } + + for _, test := range addTests { + if got := float64(test.a.Add(test.b)); !float64Eq(got, float64(test.want)) { + t.Errorf("%v.Add(%v) = %0.24f, want %0.24f", test.a.Angle().Degrees(), test.b.Angle().Degrees(), got, test.want) + } + } + for _, test := range subTests { + if got := float64(test.a.Sub(test.b)); !float64Eq(got, float64(test.want)) { + t.Errorf("%v.Sub(%v) = %0.24f, want %0.24f", test.a.Angle().Degrees(), test.b.Angle().Degrees(), got, test.want) + } + } +} + +func TestChordAngleTrigonometry(t *testing.T) { + // Because of the way the math works out, the 9/10th's case has slightly more + // difference than all the other computations, so this gets a more generous + // epsilon to deal with that. + const epsilon = 1e-14 + const iters = 40 + for iter := 0; iter <= iters; iter++ { + radians := math.Pi * float64(iter) / float64(iters) + angle := ChordAngleFromAngle(Angle(radians)) + if !float64Near(math.Sin(radians), angle.Sin(), epsilon) { + t.Errorf("(%d/%d)*π. %v.Sin() = %v, want %v", iter, iters, angle, angle.Sin(), math.Sin(radians)) + } + if !float64Near(math.Cos(radians), angle.Cos(), epsilon) { + t.Errorf("(%d/%d)*π. %v.Cos() = %v, want %v", iter, iters, angle, angle.Cos(), math.Cos(radians)) + } + // Since tan(x) is unbounded near pi/4, we map the result back to an + // angle before comparing. The assertion is that the result is equal to + // the tangent of a nearby angle. + if !float64Near(math.Atan(math.Tan(radians)), math.Atan(angle.Tan()), 1e-14) { + t.Errorf("(%d/%d)*π. %v.Tan() = %v, want %v", iter, iters, angle, angle.Tan(), math.Tan(radians)) + } + } + + // Unlike Angle, ChordAngle can represent 90 and 180 degrees exactly. + angle90 := ChordAngleFromSquaredLength(2) + angle180 := ChordAngleFromSquaredLength(4) + if !float64Eq(1, angle90.Sin()) { + t.Errorf("%v.Sin() = %v, want 1", angle90, angle90.Sin()) + } + if !float64Eq(0, angle90.Cos()) { + t.Errorf("%v.Cos() = %v, want 0", angle90, angle90.Cos()) + } + if !math.IsInf(angle90.Tan(), 0) { + t.Errorf("%v.Tan() should be infinite, but was not.", angle90) + } + if !float64Eq(0, angle180.Sin()) { + t.Errorf("%v.Sin() = %v, want 0", angle180, angle180.Sin()) + } + if !float64Eq(-1, angle180.Cos()) { + t.Errorf("%v.Cos() = %v, want -1", angle180, angle180.Cos()) + } + if !float64Eq(0, angle180.Tan()) { + t.Errorf("%v.Tan() = %v, want 0", angle180, angle180.Tan()) + } +} + +func TestChordAngleExpanded(t *testing.T) { + var zero ChordAngle + + tests := []struct { + have ChordAngle + add float64 + want ChordAngle + }{ + {NegativeChordAngle, 5, NegativeChordAngle.Expanded(5)}, + {InfChordAngle(), -5, InfChordAngle()}, + {StraightChordAngle, 5, ChordAngleFromSquaredLength(5)}, + {zero, -5, zero}, + {ChordAngleFromSquaredLength(1.25), 0.25, ChordAngleFromSquaredLength(1.5)}, + {ChordAngleFromSquaredLength(0.75), 0.25, ChordAngleFromSquaredLength(1)}, + } + + for _, test := range tests { + if got := test.have.Expanded(test.add); got != test.want { + t.Errorf("%v.Expanded(%v) = %v, want %v", test.have, test.add, got, test.want) + } + } +} diff --git a/vendor/github.com/golang/geo/s1/interval_test.go b/vendor/github.com/golang/geo/s1/interval_test.go new file mode 100644 index 0000000..9ec6ab3 --- /dev/null +++ b/vendor/github.com/golang/geo/s1/interval_test.go @@ -0,0 +1,457 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package s1 + +import ( + "math" + "testing" +) + +// Some standard intervals for use throughout the tests. +var ( + empty = EmptyInterval() + full = FullInterval() + // Single-point intervals: + zero = IntervalFromEndpoints(0, 0) + pi2 = IntervalFromEndpoints(math.Pi/2, math.Pi/2) + pi = IntervalFromEndpoints(math.Pi, math.Pi) + mipi = IntervalFromEndpoints(-math.Pi, -math.Pi) // same as pi after normalization + mipi2 = IntervalFromEndpoints(-math.Pi/2, -math.Pi/2) + // Single quadrants: + quad1 = IntervalFromEndpoints(0, math.Pi/2) + quad2 = IntervalFromEndpoints(math.Pi/2, -math.Pi) // equivalent to (pi/2, pi) + quad3 = IntervalFromEndpoints(math.Pi, -math.Pi/2) + quad4 = IntervalFromEndpoints(-math.Pi/2, 0) + // Quadrant pairs: + quad12 = IntervalFromEndpoints(0, -math.Pi) + quad23 = IntervalFromEndpoints(math.Pi/2, -math.Pi/2) + quad34 = IntervalFromEndpoints(-math.Pi, 0) + quad41 = IntervalFromEndpoints(-math.Pi/2, math.Pi/2) + // Quadrant triples: + quad123 = IntervalFromEndpoints(0, -math.Pi/2) + quad234 = IntervalFromEndpoints(math.Pi/2, 0) + quad341 = IntervalFromEndpoints(math.Pi, math.Pi/2) + quad412 = IntervalFromEndpoints(-math.Pi/2, -math.Pi) + // Small intervals around the midpoints between quadrants, + // such that the center of each interval is offset slightly CCW from the midpoint. + mid12 = IntervalFromEndpoints(math.Pi/2-0.01, math.Pi/2+0.02) + mid23 = IntervalFromEndpoints(math.Pi-0.01, -math.Pi+0.02) + mid34 = IntervalFromEndpoints(-math.Pi/2-0.01, -math.Pi/2+0.02) + mid41 = IntervalFromEndpoints(-0.01, 0.02) +) + +func TestConstructors(t *testing.T) { + // Check that [-π,-π] is normalized to [π,π]. + if mipi.Lo != math.Pi { + t.Errorf("mipi.Lo = %v, want π", mipi.Lo) + } + if mipi.Hi != math.Pi { + t.Errorf("mipi.Hi = %v, want π", mipi.Lo) + } + + var i Interval + if !i.IsValid() { + t.Errorf("Zero value Interval is not valid") + } +} + +func TestIntervalFromPointPair(t *testing.T) { + tests := []struct { + a, b float64 + want Interval + }{ + {-math.Pi, math.Pi, pi}, + {math.Pi, -math.Pi, pi}, + {mid34.Hi, mid34.Lo, mid34}, + {mid23.Lo, mid23.Hi, mid23}, + } + for _, test := range tests { + got := IntervalFromPointPair(test.a, test.b) + if got != test.want { + t.Errorf("IntervalFromPointPair(%f, %f) = %v, want %v", test.a, test.b, got, test.want) + } + } +} + +func TestSimplePredicates(t *testing.T) { + if !zero.IsValid() || zero.IsEmpty() || zero.IsFull() { + t.Errorf("Zero interval is invalid or empty or full") + } + if !empty.IsValid() || !empty.IsEmpty() || empty.IsFull() { + t.Errorf("Empty interval is invalid or not empty or full") + } + if !empty.IsInverted() { + t.Errorf("Empty interval is not inverted") + } + if !full.IsValid() || full.IsEmpty() || !full.IsFull() { + t.Errorf("Full interval is invalid or empty or not full") + } + if !pi.IsValid() || pi.IsEmpty() || pi.IsInverted() { + t.Errorf("pi is invalid or empty or inverted") + } + if !mipi.IsValid() || mipi.IsEmpty() || mipi.IsInverted() { + t.Errorf("mipi is invalid or empty or inverted") + } +} + +func TestAlmostFullOrEmpty(t *testing.T) { + // Test that rounding errors don't cause intervals that are almost empty or + // full to be considered empty or full. The following value is the greatest + // representable value less than Pi. + almostPi := math.Pi - 2*dblEpsilon + + i := Interval{-almostPi, math.Pi} + if i.IsFull() { + t.Errorf("%v.IsFull should not be true", i) + } + + i = Interval{-math.Pi, almostPi} + if i.IsFull() { + t.Errorf("%v.IsFull should not be true", i) + } + + i = Interval{math.Pi, -almostPi} + if i.IsEmpty() { + t.Errorf("%v.IsEmpty should not be true", i) + } + + i = Interval{almostPi, -math.Pi} + if i.IsEmpty() { + t.Errorf("%v.IsEmpty should not be true", i) + } +} + +func TestCenter(t *testing.T) { + tests := []struct { + interval Interval + want float64 + }{ + {quad12, math.Pi / 2}, + {IntervalFromEndpoints(3.1, 2.9), 3 - math.Pi}, + {IntervalFromEndpoints(-2.9, -3.1), math.Pi - 3}, + {IntervalFromEndpoints(2.1, -2.1), math.Pi}, + {pi, math.Pi}, + {mipi, math.Pi}, + // TODO(dsymonds): The C++ test for quad23 uses fabs. Why? + {quad23, math.Pi}, + // TODO(dsymonds): The C++ test for quad123 uses EXPECT_DOUBLE_EQ. Why? + {quad123, 0.75 * math.Pi}, + } + for _, test := range tests { + got := test.interval.Center() + // TODO(dsymonds): Some are inaccurate in the 16th decimal place. Track it down. + if math.Abs(got-test.want) > 1e-15 { + t.Errorf("%v.Center() = %v, want %v", test.interval, got, test.want) + } + } +} + +func TestLength(t *testing.T) { + tests := []struct { + interval Interval + want float64 + }{ + {quad12, math.Pi}, + {pi, 0}, + {mipi, 0}, + // TODO(dsymonds): The C++ test for quad123 uses DOUBLE_EQ. Why? + {quad123, 1.5 * math.Pi}, + // TODO(dsymonds): The C++ test for quad23 uses fabs. Why? + {quad23, math.Pi}, + {full, 2 * math.Pi}, + } + for _, test := range tests { + if l := test.interval.Length(); l != test.want { + t.Errorf("%v.Length() got %v, want %v", test.interval, l, test.want) + } + } + if l := empty.Length(); l >= 0 { + t.Errorf("empty interval has non-negative length %v", l) + } +} + +func TestContains(t *testing.T) { + tests := []struct { + interval Interval + in, out []float64 // points that should be inside/outside the interval + iIn, iOut []float64 // points that should be inside/outside the interior + }{ + {empty, nil, []float64{0, math.Pi, -math.Pi}, nil, []float64{math.Pi, -math.Pi}}, + {full, []float64{0, math.Pi, -math.Pi}, nil, []float64{math.Pi, -math.Pi}, nil}, + {quad12, []float64{0, math.Pi, -math.Pi}, nil, + []float64{math.Pi / 2}, []float64{0, math.Pi, -math.Pi}}, + {quad23, []float64{math.Pi / 2, -math.Pi / 2, math.Pi, -math.Pi}, []float64{0}, + []float64{math.Pi, -math.Pi}, []float64{math.Pi / 2, -math.Pi / 2, 0}}, + {pi, []float64{math.Pi, -math.Pi}, []float64{0}, nil, []float64{math.Pi, -math.Pi}}, + {mipi, []float64{math.Pi, -math.Pi}, []float64{0}, nil, []float64{math.Pi, -math.Pi}}, + {zero, []float64{0}, nil, nil, []float64{0}}, + } + for _, test := range tests { + for _, p := range test.in { + if !test.interval.Contains(p) { + t.Errorf("%v should contain %v", test.interval, p) + } + } + for _, p := range test.out { + if test.interval.Contains(p) { + t.Errorf("%v should not contain %v", test.interval, p) + } + } + for _, p := range test.iIn { + if !test.interval.InteriorContains(p) { + t.Errorf("interior of %v should contain %v", test.interval, p) + } + } + for _, p := range test.iOut { + if test.interval.InteriorContains(p) { + t.Errorf("interior %v should not contain %v", test.interval, p) + } + } + } +} + +func TestIntervalOperations(t *testing.T) { + quad12eps := IntervalFromEndpoints(quad12.Lo, mid23.Hi) + quad2hi := IntervalFromEndpoints(mid23.Lo, quad12.Hi) + quad412eps := IntervalFromEndpoints(mid34.Lo, quad12.Hi) + quadeps12 := IntervalFromEndpoints(mid41.Lo, quad12.Hi) + quad1lo := IntervalFromEndpoints(quad12.Lo, mid41.Hi) + quad2lo := IntervalFromEndpoints(quad23.Lo, mid12.Hi) + quad3hi := IntervalFromEndpoints(mid34.Lo, quad23.Hi) + quadeps23 := IntervalFromEndpoints(mid12.Lo, quad23.Hi) + quad23eps := IntervalFromEndpoints(quad23.Lo, mid34.Hi) + quadeps123 := IntervalFromEndpoints(mid41.Lo, quad23.Hi) + + // This massive list of test cases is ported directly from the C++ test case. + tests := []struct { + x, y Interval + xContainsY, xInteriorContainsY bool + xIntersectsY, xInteriorIntersectsY bool + wantUnion, wantIntersection Interval + }{ + // 0 + {empty, empty, true, true, false, false, empty, empty}, + {empty, full, false, false, false, false, full, empty}, + {empty, zero, false, false, false, false, zero, empty}, + {empty, pi, false, false, false, false, pi, empty}, + {empty, mipi, false, false, false, false, mipi, empty}, + + // 5 + {full, empty, true, true, false, false, full, empty}, + {full, full, true, true, true, true, full, full}, + {full, zero, true, true, true, true, full, zero}, + {full, pi, true, true, true, true, full, pi}, + {full, mipi, true, true, true, true, full, mipi}, + {full, quad12, true, true, true, true, full, quad12}, + {full, quad23, true, true, true, true, full, quad23}, + + // 12 + {zero, empty, true, true, false, false, zero, empty}, + {zero, full, false, false, true, false, full, zero}, + {zero, zero, true, false, true, false, zero, zero}, + {zero, pi, false, false, false, false, IntervalFromEndpoints(0, math.Pi), empty}, + {zero, pi2, false, false, false, false, quad1, empty}, + {zero, mipi, false, false, false, false, quad12, empty}, + {zero, mipi2, false, false, false, false, quad4, empty}, + {zero, quad12, false, false, true, false, quad12, zero}, + {zero, quad23, false, false, false, false, quad123, empty}, + + // 21 + {pi2, empty, true, true, false, false, pi2, empty}, + {pi2, full, false, false, true, false, full, pi2}, + {pi2, zero, false, false, false, false, quad1, empty}, + {pi2, pi, false, false, false, false, IntervalFromEndpoints(math.Pi/2, math.Pi), empty}, + {pi2, pi2, true, false, true, false, pi2, pi2}, + {pi2, mipi, false, false, false, false, quad2, empty}, + {pi2, mipi2, false, false, false, false, quad23, empty}, + {pi2, quad12, false, false, true, false, quad12, pi2}, + {pi2, quad23, false, false, true, false, quad23, pi2}, + + // 30 + {pi, empty, true, true, false, false, pi, empty}, + {pi, full, false, false, true, false, full, pi}, + {pi, zero, false, false, false, false, IntervalFromEndpoints(math.Pi, 0), empty}, + {pi, pi, true, false, true, false, pi, pi}, + {pi, pi2, false, false, false, false, IntervalFromEndpoints(math.Pi/2, math.Pi), empty}, + {pi, mipi, true, false, true, false, pi, pi}, + {pi, mipi2, false, false, false, false, quad3, empty}, + {pi, quad12, false, false, true, false, IntervalFromEndpoints(0, math.Pi), pi}, + {pi, quad23, false, false, true, false, quad23, pi}, + + // 39 + {mipi, empty, true, true, false, false, mipi, empty}, + {mipi, full, false, false, true, false, full, mipi}, + {mipi, zero, false, false, false, false, quad34, empty}, + {mipi, pi, true, false, true, false, mipi, mipi}, + {mipi, pi2, false, false, false, false, quad2, empty}, + {mipi, mipi, true, false, true, false, mipi, mipi}, + {mipi, mipi2, false, false, false, false, IntervalFromEndpoints(-math.Pi, -math.Pi/2), empty}, + {mipi, quad12, false, false, true, false, quad12, mipi}, + {mipi, quad23, false, false, true, false, quad23, mipi}, + + // 48 + {quad12, empty, true, true, false, false, quad12, empty}, + {quad12, full, false, false, true, true, full, quad12}, + {quad12, zero, true, false, true, false, quad12, zero}, + {quad12, pi, true, false, true, false, quad12, pi}, + {quad12, mipi, true, false, true, false, quad12, mipi}, + {quad12, quad12, true, false, true, true, quad12, quad12}, + {quad12, quad23, false, false, true, true, quad123, quad2}, + {quad12, quad34, false, false, true, false, full, quad12}, + + // 56 + {quad23, empty, true, true, false, false, quad23, empty}, + {quad23, full, false, false, true, true, full, quad23}, + {quad23, zero, false, false, false, false, quad234, empty}, + {quad23, pi, true, true, true, true, quad23, pi}, + {quad23, mipi, true, true, true, true, quad23, mipi}, + {quad23, quad12, false, false, true, true, quad123, quad2}, + {quad23, quad23, true, false, true, true, quad23, quad23}, + {quad23, quad34, false, false, true, true, quad234, IntervalFromEndpoints(-math.Pi, -math.Pi/2)}, + + // 64 + {quad1, quad23, false, false, true, false, quad123, IntervalFromEndpoints(math.Pi/2, math.Pi/2)}, + {quad2, quad3, false, false, true, false, quad23, mipi}, + {quad3, quad2, false, false, true, false, quad23, pi}, + {quad2, pi, true, false, true, false, quad2, pi}, + {quad2, mipi, true, false, true, false, quad2, mipi}, + {quad3, pi, true, false, true, false, quad3, pi}, + {quad3, mipi, true, false, true, false, quad3, mipi}, + + // 71 + {quad12, mid12, true, true, true, true, quad12, mid12}, + {mid12, quad12, false, false, true, true, quad12, mid12}, + + // 73 + {quad12, mid23, false, false, true, true, quad12eps, quad2hi}, + {mid23, quad12, false, false, true, true, quad12eps, quad2hi}, + + // This test checks that the union of two disjoint intervals is the smallest + // interval that contains both of them. Note that the center of "mid34" + // slightly CCW of -Pi/2 so that there is no ambiguity about the result. + // 75 + {quad12, mid34, false, false, false, false, quad412eps, empty}, + {mid34, quad12, false, false, false, false, quad412eps, empty}, + + // 77 + {quad12, mid41, false, false, true, true, quadeps12, quad1lo}, + {mid41, quad12, false, false, true, true, quadeps12, quad1lo}, + + // 79 + {quad23, mid12, false, false, true, true, quadeps23, quad2lo}, + {mid12, quad23, false, false, true, true, quadeps23, quad2lo}, + {quad23, mid23, true, true, true, true, quad23, mid23}, + {mid23, quad23, false, false, true, true, quad23, mid23}, + {quad23, mid34, false, false, true, true, quad23eps, quad3hi}, + {mid34, quad23, false, false, true, true, quad23eps, quad3hi}, + {quad23, mid41, false, false, false, false, quadeps123, empty}, + {mid41, quad23, false, false, false, false, quadeps123, empty}, + } + should := func(b bool) string { + if b { + return "should" + } + return "should not" + } + for _, test := range tests { + if test.x.ContainsInterval(test.y) != test.xContainsY { + t.Errorf("%v %s contain %v", test.x, should(test.xContainsY), test.y) + } + if test.x.InteriorContainsInterval(test.y) != test.xInteriorContainsY { + t.Errorf("interior of %v %s contain %v", test.x, should(test.xInteriorContainsY), test.y) + } + if test.x.Intersects(test.y) != test.xIntersectsY { + t.Errorf("%v %s intersect %v", test.x, should(test.xIntersectsY), test.y) + } + if test.x.InteriorIntersects(test.y) != test.xInteriorIntersectsY { + t.Errorf("interior of %v %s intersect %v", test.x, should(test.xInteriorIntersectsY), test.y) + } + if u := test.x.Union(test.y); u != test.wantUnion { + t.Errorf("%v ∪ %v was %v, want %v", test.x, test.y, u, test.wantUnion) + } + if u := test.x.Intersection(test.y); u != test.wantIntersection { + t.Errorf("%v ∩ %v was %v, want %v", test.x, test.y, u, test.wantIntersection) + } + } +} + +func TestAddPoint(t *testing.T) { + tests := []struct { + interval Interval + points []float64 + want Interval + }{ + {empty, []float64{0}, zero}, + {empty, []float64{math.Pi}, pi}, + {empty, []float64{-math.Pi}, mipi}, + {empty, []float64{math.Pi, -math.Pi}, pi}, + {empty, []float64{-math.Pi, math.Pi}, mipi}, + {empty, []float64{mid12.Lo, mid12.Hi}, mid12}, + {empty, []float64{mid23.Lo, mid23.Hi}, mid23}, + + {quad1, []float64{-0.9 * math.Pi, -math.Pi / 2}, quad123}, + {full, []float64{0}, full}, + {full, []float64{math.Pi}, full}, + {full, []float64{-math.Pi}, full}, + } + for _, test := range tests { + got := test.interval + for _, point := range test.points { + got = got.AddPoint(point) + } + want := test.want + if math.Abs(got.Lo-want.Lo) > 1e-15 || math.Abs(got.Hi-want.Hi) > 1e-15 { + t.Errorf("%v.AddPoint(%v) = %v, want %v", test.interval, test.points, got, want) + } + } +} + +func TestExpanded(t *testing.T) { + tests := []struct { + interval Interval + margin float64 + want Interval + }{ + {empty, 1, empty}, + {full, 1, full}, + {zero, 1, Interval{-1, 1}}, + {mipi, 0.01, Interval{math.Pi - 0.01, -math.Pi + 0.01}}, + {pi, 27, full}, + {pi, math.Pi / 2, quad23}, + {pi2, math.Pi / 2, quad12}, + {mipi2, math.Pi / 2, quad34}, + + {empty, -1, empty}, + {full, -1, full}, + {quad123, -27, empty}, + {quad234, -27, empty}, + {quad123, -math.Pi / 2, quad2}, + {quad341, -math.Pi / 2, quad4}, + {quad412, -math.Pi / 2, quad1}, + } + for _, test := range tests { + if got, want := test.interval.Expanded(test.margin), test.want; math.Abs(got.Lo-want.Lo) > 1e-15 || math.Abs(got.Hi-want.Hi) > 1e-15 { + t.Errorf("%v.Expanded(%v) = %v, want %v", test.interval, test.margin, got, want) + } + } +} + +func TestIntervalString(t *testing.T) { + if s, exp := pi.String(), "[3.1415927, 3.1415927]"; s != exp { + t.Errorf("pi.String() = %q, want %q", s, exp) + } +} |
