1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package s2
16
17 import (
18 "math"
19 "reflect"
20 "testing"
21
22 "github.com/golang/geo/s1"
23 )
24
25 func perturbAtDistance(distance s1.Angle, a0, b0 Point) Point {
26 x := InterpolateAtDistance(distance, a0, b0)
27 if oneIn(2) {
28 if oneIn(2) {
29 x.X = math.Nextafter(x.X, 1)
30 } else {
31 x.X = math.Nextafter(x.X, -1)
32 }
33 if oneIn(2) {
34 x.Y = math.Nextafter(x.Y, 1)
35 } else {
36 x.Y = math.Nextafter(x.Y, -1)
37 }
38 if oneIn(2) {
39 x.Z = math.Nextafter(x.Z, 1)
40 } else {
41 x.Z = math.Nextafter(x.Z, -1)
42
43 }
44 x = Point{x.Normalize()}
45 }
46 return x
47 }
48
49
50
51
52 func generatePerturbedSubEdges(a, b Point, count int) []Edge {
53 var edges []Edge
54
55 a = Point{a.Normalize()}
56 b = Point{b.Normalize()}
57
58 length0 := a.Distance(b)
59 for i := 0; i < count; i++ {
60 length := length0 * s1.Angle(math.Pow(1e-15, randomFloat64()))
61 offset := (length0 - length) * s1.Angle(randomFloat64())
62 edges = append(edges, Edge{
63 perturbAtDistance(offset, a, b),
64 perturbAtDistance(offset+length, a, b),
65 })
66 }
67 return edges
68 }
69
70
71
72 func generateCapEdges(centerCap Cap, maxLength s1.Angle, count int) []Edge {
73 var edges []Edge
74 for i := 0; i < count; i++ {
75 center := samplePointFromCap(centerCap)
76 edgeCap := CapFromCenterAngle(center, 0.5*maxLength)
77 p1 := samplePointFromCap(edgeCap)
78
79 p2 := Point{(center.Mul(2 * p1.Dot(center.Vector)).Sub(p1.Vector)).Normalize()}
80 edges = append(edges, Edge{p1, p2})
81 }
82 return edges
83 }
84
85 func testCrossingEdgeQueryAllCrossings(t *testing.T, edges []Edge) {
86 s := &edgeVectorShape{}
87 for _, edge := range edges {
88 s.Add(edge.V0, edge.V1)
89 }
90
91
92 index := NewShapeIndex()
93 index.maxEdgesPerCell = 1
94 index.Add(s)
95
96
97
98
99 var numCandidates, numNearbyPairs int
100
101 for _, edge := range edges {
102 a := edge.V0
103 b := edge.V1
104
105 query := NewCrossingEdgeQuery(index)
106 candidates := query.candidates(a, b, s)
107
108
109 edgeMap := query.candidatesEdgeMap(a, b)
110 if len(edgeMap) != 1 {
111 t.Errorf("there should be only one shape in this map, got %d", len(edgeMap))
112
113
114 continue
115 }
116 for k, v := range edgeMap {
117 if Shape(s) != k {
118 t.Errorf("element(%v) of edgeMap should be this shape", k)
119 }
120 if !reflect.DeepEqual(candidates, v) {
121 t.Errorf("edgeMap candidates for this shape = %v, want %v", v, candidates)
122 }
123 }
124 if len(candidates) == 0 {
125 t.Errorf("candidates should not be empty")
126 }
127
128
129
130 for k, v := range candidates {
131 if k < len(candidates)-1 {
132 if v > candidates[k+1] {
133 t.Errorf("candidates was not sorted. candidates[%d] = %v > candidates[%d] = %v", k, v, k+1, candidates[k+1])
134 }
135 }
136 }
137 if got := candidates[0]; got < 0 {
138 t.Errorf("the first element should not have a negative edge id")
139 }
140 if got, want := candidates[len(candidates)-1], s.NumEdges(); got >= want {
141 t.Errorf("candidates[%d] = %v, want < %v", len(candidates)-1, got, want)
142 }
143
144 numCandidates += len(candidates)
145
146 var expectedCrossings, expectedInteriorCrossings []int
147 var missingCandidates []int
148 for i := 0; i < s.NumEdges(); i++ {
149 edge := s.Edge(i)
150 c := edge.V0
151 d := edge.V1
152 sign := CrossingSign(a, b, c, d)
153 if sign != DoNotCross {
154 expectedCrossings = append(expectedCrossings, i)
155 if sign == Cross {
156 expectedInteriorCrossings = append(expectedInteriorCrossings, i)
157 }
158 numNearbyPairs++
159
160
161 found := false
162 for _, v := range candidates {
163 if v == i {
164 found = true
165 }
166 }
167
168 if !found {
169 missingCandidates = append(missingCandidates, i)
170 }
171 } else {
172 maxDist := MaxDiagMetric.Value(MaxLevel)
173 if DistanceFromSegment(a, c, d).Radians() < maxDist ||
174 DistanceFromSegment(b, c, d).Radians() < maxDist ||
175 DistanceFromSegment(c, a, b).Radians() < maxDist ||
176 DistanceFromSegment(d, a, b).Radians() < maxDist {
177 numNearbyPairs++
178 }
179 }
180 }
181
182 if len(missingCandidates) != 0 {
183 t.Errorf("missingCandidates should have been empty")
184 }
185
186
187 actualCrossings := query.Crossings(a, b, s, CrossingTypeAll)
188 if !reflect.DeepEqual(expectedCrossings, actualCrossings) {
189 t.Errorf("query.Crossings(%v, %v, ...) = %v, want %v", a, b, actualCrossings, expectedCrossings)
190 }
191
192
193 if edgeMap := query.CrossingsEdgeMap(a, b, CrossingTypeAll); len(edgeMap) != 0 {
194 if len(edgeMap) != 1 {
195 t.Errorf("query.CrossingsEdgeMap(%v, %v) has %d entried, want 1", a, b, len(edgeMap))
196 } else {
197 for k, v := range edgeMap {
198 if s != k {
199 t.Errorf("query.CrossingsEdgeMap(%v, %v, ...) shape = %v, want %v", a, b, k, s)
200 }
201 if !reflect.DeepEqual(expectedCrossings, v) {
202 t.Errorf("query.CrossingsEdgeMap(%v, %v) = %v, want %v", a, b, v, expectedCrossings)
203 }
204 }
205 }
206 }
207
208
209 actualCrossings = query.Crossings(a, b, s, CrossingTypeInterior)
210
211
212
213 if !reflect.DeepEqual(expectedInteriorCrossings, actualCrossings) && (len(actualCrossings) != 0 && len(expectedInteriorCrossings) != 0) {
214 t.Errorf("query.Crossings(%v, %v, CrossingTypeInterior) = %v, want %v", a, b, actualCrossings, expectedInteriorCrossings)
215 }
216 }
217
218
219
220 if numCandidates > 3*numNearbyPairs {
221 t.Errorf("number of candidates should not be dramatically larger than number of nearby pairs. got %v, want < %v", numCandidates, 3*numNearbyPairs)
222 }
223
224 }
225
226 func TestCrossingEdgeQueryCrossingCandidatesPerturbedCubeEdges(t *testing.T) {
227
228
229
230
231
232
233
234 for iter := 0; iter < 10; iter++ {
235 face := randomUniformInt(6)
236 scale := math.Pow(1e-15, randomFloat64())
237 u := scale*2*float64(randomUniformInt(2)) - 1
238 v := scale*2*float64(randomUniformInt(2)) - 1
239
240 a := Point{faceUVToXYZ(face, u, v)}
241 b := Point{a.Sub(unitNorm(face).Mul(2))}
242
243
244 edges := generatePerturbedSubEdges(a, b, 30)
245 testCrossingEdgeQueryAllCrossings(t, edges)
246 }
247 }
248
249
250
251
252 func TestCrossingEdgeQueryCandidatesPerturbedCubeFaceAxes(t *testing.T) {
253 for iter := 0; iter < 5; iter++ {
254 face := randomUniformInt(6)
255 scale := math.Pow(1e-15, randomFloat64())
256 axis := uvwAxis(face, randomUniformInt(2))
257 a := Point{axis.Mul(scale).Add(unitNorm(face).Vector)}
258 b := Point{axis.Mul(scale).Sub(unitNorm(face).Vector)}
259 edges := generatePerturbedSubEdges(a, b, 30)
260 testCrossingEdgeQueryAllCrossings(t, edges)
261 }
262 }
263
264 func TestCrossingEdgeQueryCandidatesCapEdgesNearCubeVertex(t *testing.T) {
265
266
267 edges := generateCapEdges(CapFromCenterAngle(PointFromCoords(-1, -1, 1), s1.Angle(1e-3)), s1.Angle(1e-4), 1000)
268 testCrossingEdgeQueryAllCrossings(t, edges)
269 }
270
271 func TestCrossingEdgeQueryCandidatesDegenerateEdgeOnCellVertexIsItsOwnCandidate(t *testing.T) {
272 for iter := 0; iter < 100; iter++ {
273 cell := CellFromCellID(randomCellID())
274 edges := []Edge{{cell.Vertex(0), cell.Vertex(0)}}
275 testCrossingEdgeQueryAllCrossings(t, edges)
276 }
277 }
278
279 func TestCrossingEdgeQueryCandidatesCollinearEdgesOnCellBoundaries(t *testing.T) {
280 const numEdgeIntervals = 8
281 for level := 0; level <= MaxLevel; level++ {
282 var edges []Edge
283 cell := CellFromCellID(randomCellIDForLevel(level))
284 i := randomUniformInt(4)
285 p1 := cell.Vertex(i % 4)
286 p2 := cell.Vertex((i + 1) % 4)
287 delta := p2.Sub(p1.Vector).Mul(1 / numEdgeIntervals)
288 for i := 0; i <= numEdgeIntervals; i++ {
289 for j := 0; j < i; j++ {
290 edges = append(edges, Edge{
291 Point{p1.Add(delta.Mul(float64(i))).Normalize()},
292 Point{p1.Add(delta.Mul(float64(j))).Normalize()},
293 })
294 }
295 }
296 testCrossingEdgeQueryAllCrossings(t, edges)
297 }
298 }
299
300 func TestCrossingEdgeQueryCrossingsPolylineCrossings(t *testing.T) {
301 index := NewShapeIndex()
302
303 index.Add(makePolyline("0:0, 2:1, 0:2, 2:3, 0:4, 2:5, 0:6"))
304 index.Add(makePolyline("1:0, 3:1, 1:2, 3:3, 1:4, 3:5, 1:6"))
305 index.Add(makePolyline("2:0, 4:1, 2:2, 4:3, 2:4, 4:5, 2:6"))
306 index.Begin()
307
308 var tests = []struct {
309 a0, a1 Point
310 }{
311 {parsePoint("1:0"), parsePoint("1:4")},
312 {parsePoint("5:5"), parsePoint("6:6")},
313 }
314
315 for _, test := range tests {
316 query := NewCrossingEdgeQuery(index)
317
318 edgeMap := query.CrossingsEdgeMap(test.a0, test.a1, CrossingTypeAll)
319 if len(edgeMap) == 0 {
320 continue
321 }
322
323 for shape, edges := range edgeMap {
324 polyline := shape.(*Polyline)
325 if len(edges) == 0 {
326 t.Errorf("shapes with no crossings should have been filtered out")
327 }
328
329 for _, edge := range edges {
330 b0 := (*polyline)[edge]
331 b1 := (*polyline)[edge+1]
332 if got := CrossingSign(test.a0, test.a1, b0, b1); got == DoNotCross {
333 t.Errorf("CrossingSign(%v, %v, %v, %v) = %v, want MaybeCross or Cross", test.a0, test.a1, b0, b1, got)
334 }
335 }
336 }
337
338
339 for _, shape := range index.shapes {
340 polyline := shape.(*Polyline)
341 edges := edgeMap[shape]
342 for e := 0; e < len(*polyline)-1; e++ {
343 if got := CrossingSign(test.a0, test.a1, (*polyline)[e], (*polyline)[e+1]); got != DoNotCross {
344
345
346 count := 0
347 for _, edge := range edges {
348 if edge == e {
349 count++
350 }
351 }
352
353 if count != 1 {
354 t.Errorf("edge %v should appear once in the set of edges, got %v", e, count)
355 }
356 }
357 }
358 }
359 }
360 }
361
362 func TestUniqueInts(t *testing.T) {
363 tests := []struct {
364 have []int
365 want []int
366 }{
367 {
368 have: nil,
369 want: nil,
370 },
371 {
372 have: []int{},
373 want: nil,
374 },
375 {
376 have: []int{1},
377 want: []int{1},
378 },
379 {
380 have: []int{3, 2, 1},
381 want: []int{1, 2, 3},
382 },
383 {
384 have: []int{4, 4, 4},
385 want: []int{4},
386 },
387 {
388 have: []int{1, 2, 3, 4, 2, 3, 5, 4, 6, 1, 2, 3, 4, 5, 7, 8, 1, 3, 1, 2, 3, 9, 3, 2, 1},
389 want: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
390 },
391 }
392
393 for _, test := range tests {
394 if got := uniqueInts(test.have); !reflect.DeepEqual(got, test.want) {
395 t.Errorf("uniqueInts(%v) = %v, want %v", test.have, got, test.want)
396 }
397 }
398 }
399
View as plain text