1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
|
// Copyright 2016 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 s2
import (
"math"
"sort"
"sync"
"sync/atomic"
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
)
// CellRelation describes the possible relationships between a target cell
// and the cells of the ShapeIndex. If the target is an index cell or is
// contained by an index cell, it is Indexed. If the target is subdivided
// into one or more index cells, it is Subdivided. Otherwise it is Disjoint.
type CellRelation int
// The possible CellRelations for a ShapeIndex.
const (
Indexed CellRelation = iota
Subdivided
Disjoint
)
const (
// cellPadding defines the total error when clipping an edge which comes
// from two sources:
// (1) Clipping the original spherical edge to a cube face (the face edge).
// The maximum error in this step is faceClipErrorUVCoord.
// (2) Clipping the face edge to the u- or v-coordinate of a cell boundary.
// The maximum error in this step is edgeClipErrorUVCoord.
// Finally, since we encounter the same errors when clipping query edges, we
// double the total error so that we only need to pad edges during indexing
// and not at query time.
cellPadding = 2.0 * (faceClipErrorUVCoord + edgeClipErrorUVCoord)
// cellSizeToLongEdgeRatio defines the cell size relative to the length of an
// edge at which it is first considered to be long. Long edges do not
// contribute toward the decision to subdivide a cell further. For example,
// a value of 2.0 means that the cell must be at least twice the size of the
// edge in order for that edge to be counted. There are two reasons for not
// counting long edges: (1) such edges typically need to be propagated to
// several children, which increases time and memory costs without much benefit,
// and (2) in pathological cases, many long edges close together could force
// subdivision to continue all the way to the leaf cell level.
cellSizeToLongEdgeRatio = 1.0
)
// clippedShape represents the part of a shape that intersects a Cell.
// It consists of the set of edge IDs that intersect that cell and a boolean
// indicating whether the center of the cell is inside the shape (for shapes
// that have an interior).
//
// Note that the edges themselves are not clipped; we always use the original
// edges for intersection tests so that the results will be the same as the
// original shape.
type clippedShape struct {
// shapeID is the index of the shape this clipped shape is a part of.
shapeID int32
// containsCenter indicates if the center of the CellID this shape has been
// clipped to falls inside this shape. This is false for shapes that do not
// have an interior.
containsCenter bool
// edges is the ordered set of ShapeIndex original edge IDs. Edges
// are stored in increasing order of edge ID.
edges []int
}
// newClippedShape returns a new clipped shape for the given shapeID and number of expected edges.
func newClippedShape(id int32, numEdges int) *clippedShape {
return &clippedShape{
shapeID: id,
edges: make([]int, numEdges),
}
}
// numEdges returns the number of edges that intersect the CellID of the Cell this was clipped to.
func (c *clippedShape) numEdges() int {
return len(c.edges)
}
// containsEdge reports if this clipped shape contains the given edge ID.
func (c *clippedShape) containsEdge(id int) bool {
// Linear search is fast because the number of edges per shape is typically
// very small (less than 10).
for _, e := range c.edges {
if e == id {
return true
}
}
return false
}
// ShapeIndexCell stores the index contents for a particular CellID.
type ShapeIndexCell struct {
shapes []*clippedShape
}
// NewShapeIndexCell creates a new cell that is sized to hold the given number of shapes.
func NewShapeIndexCell(numShapes int) *ShapeIndexCell {
return &ShapeIndexCell{
shapes: make([]*clippedShape, numShapes),
}
}
// numEdges reports the total number of edges in all clipped shapes in this cell.
func (s *ShapeIndexCell) numEdges() int {
var e int
for _, cs := range s.shapes {
e += cs.numEdges()
}
return e
}
// add adds the given clipped shape to this index cell.
func (s *ShapeIndexCell) add(c *clippedShape) {
// C++ uses a set, so it's ordered and unique. We don't currently catch
// the case when a duplicate value is added.
s.shapes = append(s.shapes, c)
}
// findByShapeID returns the clipped shape that contains the given shapeID,
// or nil if none of the clipped shapes contain it.
func (s *ShapeIndexCell) findByShapeID(shapeID int32) *clippedShape {
// Linear search is fine because the number of shapes per cell is typically
// very small (most often 1), and is large only for pathological inputs
// (e.g. very deeply nested loops).
for _, clipped := range s.shapes {
if clipped.shapeID == shapeID {
return clipped
}
}
return nil
}
// faceEdge and clippedEdge store temporary edge data while the index is being
// updated.
//
// While it would be possible to combine all the edge information into one
// structure, there are two good reasons for separating it:
//
// - Memory usage. Separating the two means that we only need to
// store one copy of the per-face data no matter how many times an edge is
// subdivided, and it also lets us delay computing bounding boxes until
// they are needed for processing each face (when the dataset spans
// multiple faces).
//
// - Performance. UpdateEdges is significantly faster on large polygons when
// the data is separated, because it often only needs to access the data in
// clippedEdge and this data is cached more successfully.
// faceEdge represents an edge that has been projected onto a given face,
type faceEdge struct {
shapeID int32 // The ID of shape that this edge belongs to
edgeID int // Edge ID within that shape
maxLevel int // Not desirable to subdivide this edge beyond this level
hasInterior bool // Belongs to a shape that has a dimension of 2
a, b r2.Point // The edge endpoints, clipped to a given face
edge Edge // The original edge.
}
// clippedEdge represents the portion of that edge that has been clipped to a given Cell.
type clippedEdge struct {
faceEdge *faceEdge // The original unclipped edge
bound r2.Rect // Bounding box for the clipped portion
}
// ShapeIndexIteratorPos defines the set of possible iterator starting positions. By
// default iterators are unpositioned, since this avoids an extra seek in this
// situation where one of the seek methods (such as Locate) is immediately called.
type ShapeIndexIteratorPos int
const (
// IteratorBegin specifies the iterator should be positioned at the beginning of the index.
IteratorBegin ShapeIndexIteratorPos = iota
// IteratorEnd specifies the iterator should be positioned at the end of the index.
IteratorEnd
)
// ShapeIndexIterator is an iterator that provides low-level access to
// the cells of the index. Cells are returned in increasing order of CellID.
//
// for it := index.Iterator(); !it.Done(); it.Next() {
// fmt.Print(it.CellID())
// }
//
type ShapeIndexIterator struct {
index *ShapeIndex
position int
id CellID
cell *ShapeIndexCell
}
// NewShapeIndexIterator creates a new iterator for the given index. If a starting
// position is specified, the iterator is positioned at the given spot.
func NewShapeIndexIterator(index *ShapeIndex, pos ...ShapeIndexIteratorPos) *ShapeIndexIterator {
s := &ShapeIndexIterator{
index: index,
}
if len(pos) > 0 {
if len(pos) > 1 {
panic("too many ShapeIndexIteratorPos arguments")
}
switch pos[0] {
case IteratorBegin:
s.Begin()
case IteratorEnd:
s.End()
default:
panic("unknown ShapeIndexIteratorPos value")
}
}
return s
}
// CellID returns the CellID of the current index cell.
// If s.Done() is true, a value larger than any valid CellID is returned.
func (s *ShapeIndexIterator) CellID() CellID {
return s.id
}
// IndexCell returns the current index cell.
func (s *ShapeIndexIterator) IndexCell() *ShapeIndexCell {
// TODO(roberts): C++ has this call a virtual method to allow subclasses
// of ShapeIndexIterator to do other work before returning the cell. Do
// we need such a thing?
return s.cell
}
// Center returns the Point at the center of the current position of the iterator.
func (s *ShapeIndexIterator) Center() Point {
return s.CellID().Point()
}
// Begin positions the iterator at the beginning of the index.
func (s *ShapeIndexIterator) Begin() {
if !s.index.IsFresh() {
s.index.maybeApplyUpdates()
}
s.position = 0
s.refresh()
}
// Next positions the iterator at the next index cell.
func (s *ShapeIndexIterator) Next() {
s.position++
s.refresh()
}
// Prev advances the iterator to the previous cell in the index and returns true to
// indicate it was not yet at the beginning of the index. If the iterator is at the
// first cell the call does nothing and returns false.
func (s *ShapeIndexIterator) Prev() bool {
if s.position <= 0 {
return false
}
s.position--
s.refresh()
return true
}
// End positions the iterator at the end of the index.
func (s *ShapeIndexIterator) End() {
s.position = len(s.index.cells)
s.refresh()
}
// Done reports if the iterator is positioned at or after the last index cell.
func (s *ShapeIndexIterator) Done() bool {
return s.id == SentinelCellID
}
// refresh updates the stored internal iterator values.
func (s *ShapeIndexIterator) refresh() {
if s.position < len(s.index.cells) {
s.id = s.index.cells[s.position]
s.cell = s.index.cellMap[s.CellID()]
} else {
s.id = SentinelCellID
s.cell = nil
}
}
// seek positions the iterator at the first cell whose ID >= target, or at the
// end of the index if no such cell exists.
func (s *ShapeIndexIterator) seek(target CellID) {
s.position = sort.Search(len(s.index.cells), func(i int) bool {
return s.index.cells[i] >= target
})
s.refresh()
}
// LocatePoint positions the iterator at the cell that contains the given Point.
// If no such cell exists, the iterator position is unspecified, and false is returned.
// The cell at the matched position is guaranteed to contain all edges that might
// intersect the line segment between target and the cell's center.
func (s *ShapeIndexIterator) LocatePoint(p Point) bool {
// Let I = cellMap.LowerBound(T), where T is the leaf cell containing
// point P. Then if T is contained by an index cell, then the
// containing cell is either I or I'. We test for containment by comparing
// the ranges of leaf cells spanned by T, I, and I'.
target := cellIDFromPoint(p)
s.seek(target)
if !s.Done() && s.CellID().RangeMin() <= target {
return true
}
if s.Prev() && s.CellID().RangeMax() >= target {
return true
}
return false
}
// LocateCellID attempts to position the iterator at the first matching index cell
// in the index that has some relation to the given CellID. Let T be the target CellID.
// If T is contained by (or equal to) some index cell I, then the iterator is positioned
// at I and returns Indexed. Otherwise if T contains one or more (smaller) index cells,
// then the iterator is positioned at the first such cell I and return Subdivided.
// Otherwise Disjoint is returned and the iterator position is undefined.
func (s *ShapeIndexIterator) LocateCellID(target CellID) CellRelation {
// Let T be the target, let I = cellMap.LowerBound(T.RangeMin()), and
// let I' be the predecessor of I. If T contains any index cells, then T
// contains I. Similarly, if T is contained by an index cell, then the
// containing cell is either I or I'. We test for containment by comparing
// the ranges of leaf cells spanned by T, I, and I'.
s.seek(target.RangeMin())
if !s.Done() {
if s.CellID() >= target && s.CellID().RangeMin() <= target {
return Indexed
}
if s.CellID() <= target.RangeMax() {
return Subdivided
}
}
if s.Prev() && s.CellID().RangeMax() >= target {
return Indexed
}
return Disjoint
}
// tracker keeps track of which shapes in a given set contain a particular point
// (the focus). It provides an efficient way to move the focus from one point
// to another and incrementally update the set of shapes which contain it. We use
// this to compute which shapes contain the center of every CellID in the index,
// by advancing the focus from one cell center to the next.
//
// Initially the focus is at the start of the CellID space-filling curve. We then
// visit all the cells that are being added to the ShapeIndex in increasing order
// of CellID. For each cell, we draw two edges: one from the entry vertex to the
// center, and another from the center to the exit vertex (where entry and exit
// refer to the points where the space-filling curve enters and exits the cell).
// By counting edge crossings we can incrementally compute which shapes contain
// the cell center. Note that the same set of shapes will always contain the exit
// point of one cell and the entry point of the next cell in the index, because
// either (a) these two points are actually the same, or (b) the intervening
// cells in CellID order are all empty, and therefore there are no edge crossings
// if we follow this path from one cell to the other.
//
// In C++, this is S2ShapeIndex::InteriorTracker.
type tracker struct {
isActive bool
a Point
b Point
nextCellID CellID
crosser *EdgeCrosser
shapeIDs []int32
// Shape ids saved by saveAndClearStateBefore. The state is never saved
// recursively so we don't need to worry about maintaining a stack.
savedIDs []int32
}
// newTracker returns a new tracker with the appropriate defaults.
func newTracker() *tracker {
// As shapes are added, we compute which ones contain the start of the
// CellID space-filling curve by drawing an edge from OriginPoint to this
// point and counting how many shape edges cross this edge.
t := &tracker{
isActive: false,
b: trackerOrigin(),
nextCellID: CellIDFromFace(0).ChildBeginAtLevel(maxLevel),
}
t.drawTo(Point{faceUVToXYZ(0, -1, -1).Normalize()}) // CellID curve start
return t
}
// trackerOrigin returns the initial focus point when the tracker is created
// (corresponding to the start of the CellID space-filling curve).
func trackerOrigin() Point {
// The start of the S2CellId space-filling curve.
return Point{faceUVToXYZ(0, -1, -1).Normalize()}
}
// focus returns the current focus point of the tracker.
func (t *tracker) focus() Point { return t.b }
// addShape adds a shape whose interior should be tracked. containsOrigin indicates
// whether the current focus point is inside the shape. Alternatively, if
// the focus point is in the process of being moved (via moveTo/drawTo), you
// can also specify containsOrigin at the old focus point and call testEdge
// for every edge of the shape that might cross the current drawTo line.
// This updates the state to correspond to the new focus point.
//
// This requires shape.HasInterior
func (t *tracker) addShape(shapeID int32, containsFocus bool) {
t.isActive = true
if containsFocus {
t.toggleShape(shapeID)
}
}
// moveTo moves the focus of the tracker to the given point. This method should
// only be used when it is known that there are no edge crossings between the old
// and new focus locations; otherwise use drawTo.
func (t *tracker) moveTo(b Point) { t.b = b }
// drawTo moves the focus of the tracker to the given point. After this method is
// called, testEdge should be called with all edges that may cross the line
// segment between the old and new focus locations.
func (t *tracker) drawTo(b Point) {
t.a = t.b
t.b = b
// TODO: the edge crosser may need an in-place Init method if this gets expensive
t.crosser = NewEdgeCrosser(t.a, t.b)
}
// testEdge checks if the given edge crosses the current edge, and if so, then
// toggle the state of the given shapeID.
// This requires shape to have an interior.
func (t *tracker) testEdge(shapeID int32, edge Edge) {
if t.crosser.EdgeOrVertexCrossing(edge.V0, edge.V1) {
t.toggleShape(shapeID)
}
}
// setNextCellID is used to indicate that the last argument to moveTo or drawTo
// was the entry vertex of the given CellID, i.e. the tracker is positioned at the
// start of this cell. By using this method together with atCellID, the caller
// can avoid calling moveTo in cases where the exit vertex of the previous cell
// is the same as the entry vertex of the current cell.
func (t *tracker) setNextCellID(nextCellID CellID) {
t.nextCellID = nextCellID.RangeMin()
}
// atCellID reports if the focus is already at the entry vertex of the given
// CellID (provided that the caller calls setNextCellID as each cell is processed).
func (t *tracker) atCellID(cellid CellID) bool {
return cellid.RangeMin() == t.nextCellID
}
// toggleShape adds or removes the given shapeID from the set of IDs it is tracking.
func (t *tracker) toggleShape(shapeID int32) {
// Most shapeIDs slices are small, so special case the common steps.
// If there is nothing here, add it.
if len(t.shapeIDs) == 0 {
t.shapeIDs = append(t.shapeIDs, shapeID)
return
}
// If it's the first element, drop it from the slice.
if t.shapeIDs[0] == shapeID {
t.shapeIDs = t.shapeIDs[1:]
return
}
for i, s := range t.shapeIDs {
if s < shapeID {
continue
}
// If it's in the set, cut it out.
if s == shapeID {
copy(t.shapeIDs[i:], t.shapeIDs[i+1:]) // overwrite the ith element
t.shapeIDs = t.shapeIDs[:len(t.shapeIDs)-1]
return
}
// We've got to a point in the slice where we should be inserted.
// (the given shapeID is now less than the current positions id.)
t.shapeIDs = append(t.shapeIDs[0:i],
append([]int32{shapeID}, t.shapeIDs[i:len(t.shapeIDs)]...)...)
return
}
// We got to the end and didn't find it, so add it to the list.
t.shapeIDs = append(t.shapeIDs, shapeID)
}
// saveAndClearStateBefore makes an internal copy of the state for shape ids below
// the given limit, and then clear the state for those shapes. This is used during
// incremental updates to track the state of added and removed shapes separately.
func (t *tracker) saveAndClearStateBefore(limitShapeID int32) {
limit := t.lowerBound(limitShapeID)
t.savedIDs = append([]int32(nil), t.shapeIDs[:limit]...)
t.shapeIDs = t.shapeIDs[limit:]
}
// restoreStateBefore restores the state previously saved by saveAndClearStateBefore.
// This only affects the state for shapeIDs below "limitShapeID".
func (t *tracker) restoreStateBefore(limitShapeID int32) {
limit := t.lowerBound(limitShapeID)
t.shapeIDs = append(append([]int32(nil), t.savedIDs...), t.shapeIDs[limit:]...)
t.savedIDs = nil
}
// lowerBound returns the shapeID of the first entry x where x >= shapeID.
func (t *tracker) lowerBound(shapeID int32) int32 {
panic("not implemented")
}
// removedShape represents a set of edges from the given shape that is queued for removal.
type removedShape struct {
shapeID int32
hasInterior bool
containsTrackerOrigin bool
edges []Edge
}
// There are three basic states the index can be in.
const (
stale int32 = iota // There are pending updates.
updating // Updates are currently being applied.
fresh // There are no pending updates.
)
// ShapeIndex indexes a set of Shapes, where a Shape is some collection of edges
// that optionally defines an interior. It can be used to represent a set of
// points, a set of polylines, or a set of polygons. For Shapes that have
// interiors, the index makes it very fast to determine which Shape(s) contain
// a given point or region.
//
// The index can be updated incrementally by adding or removing shapes. It is
// designed to handle up to hundreds of millions of edges. All data structures
// are designed to be small, so the index is compact; generally it is smaller
// than the underlying data being indexed. The index is also fast to construct.
//
// Polygon, Loop, and Polyline implement Shape which allows these objects to
// be indexed easily. You can find useful query methods in CrossingEdgeQuery
// and ClosestEdgeQuery (Not yet implemented in Go).
//
// Example showing how to build an index of Polylines:
//
// index := NewShapeIndex()
// for _, polyline := range polylines {
// index.Add(polyline);
// }
// // Now you can use a CrossingEdgeQuery or ClosestEdgeQuery here.
//
type ShapeIndex struct {
// shapes is a map of shape ID to shape.
shapes map[int32]Shape
// The maximum number of edges per cell.
// TODO(roberts): Update the comments when the usage of this is implemented.
maxEdgesPerCell int
// nextID tracks the next ID to hand out. IDs are not reused when shapes
// are removed from the index.
nextID int32
// cellMap is a map from CellID to the set of clipped shapes that intersect that
// cell. The cell IDs cover a set of non-overlapping regions on the sphere.
// In C++, this is a BTree, so the cells are ordered naturally by the data structure.
cellMap map[CellID]*ShapeIndexCell
// Track the ordered list of cell IDs.
cells []CellID
// The current status of the index; accessed atomically.
status int32
// Additions and removals are queued and processed on the first subsequent
// query. There are several reasons to do this:
//
// - It is significantly more efficient to process updates in batches if
// the amount of entities added grows.
// - Often the index will never be queried, in which case we can save both
// the time and memory required to build it. Examples:
// + Loops that are created simply to pass to an Polygon. (We don't
// need the Loop index, because Polygon builds its own index.)
// + Applications that load a database of geometry and then query only
// a small fraction of it.
//
// The main drawback is that we need to go to some extra work to ensure that
// some methods are still thread-safe. Note that the goal is *not* to
// make this thread-safe in general, but simply to hide the fact that
// we defer some of the indexing work until query time.
//
// This mutex protects all of following fields in the index.
mu sync.RWMutex
// pendingAdditionsPos is the index of the first entry that has not been processed
// via applyUpdatesInternal.
pendingAdditionsPos int32
// The set of shapes that have been queued for removal but not processed yet by
// applyUpdatesInternal.
pendingRemovals []*removedShape
}
// NewShapeIndex creates a new ShapeIndex.
func NewShapeIndex() *ShapeIndex {
return &ShapeIndex{
maxEdgesPerCell: 10,
shapes: make(map[int32]Shape),
cellMap: make(map[CellID]*ShapeIndexCell),
cells: nil,
status: fresh,
}
}
// Iterator returns an iterator for this index.
func (s *ShapeIndex) Iterator() *ShapeIndexIterator {
s.maybeApplyUpdates()
return NewShapeIndexIterator(s, IteratorBegin)
}
// Begin positions the iterator at the first cell in the index.
func (s *ShapeIndex) Begin() *ShapeIndexIterator {
s.maybeApplyUpdates()
return NewShapeIndexIterator(s, IteratorBegin)
}
// End positions the iterator at the last cell in the index.
func (s *ShapeIndex) End() *ShapeIndexIterator {
// TODO(roberts): It's possible that updates could happen to the index between
// the time this is called and the time the iterators position is used and this
// will be invalid or not the end. For now, things will be undefined if this
// happens. See about referencing the IsFresh to guard for this in the future.
s.maybeApplyUpdates()
return NewShapeIndexIterator(s, IteratorEnd)
}
// Len reports the number of Shapes in this index.
func (s *ShapeIndex) Len() int {
return len(s.shapes)
}
// Reset resets the index to its original state.
func (s *ShapeIndex) Reset() {
s.shapes = make(map[int32]Shape)
s.nextID = 0
s.cellMap = make(map[CellID]*ShapeIndexCell)
s.cells = nil
atomic.StoreInt32(&s.status, fresh)
}
// NumEdges returns the number of edges in this index.
func (s *ShapeIndex) NumEdges() int {
numEdges := 0
for _, shape := range s.shapes {
numEdges += shape.NumEdges()
}
return numEdges
}
// NumEdgesUpTo returns the number of edges in the given index, up to the given
// limit. If the limit is encountered, the current running total is returned,
// which may be more than the limit.
func (s *ShapeIndex) NumEdgesUpTo(limit int) int {
var numEdges int
// We choose to iterate over the shapes in order to match the counting
// up behavior in C++ and for test compatibility instead of using a
// more idiomatic range over the shape map.
for i := int32(0); i <= s.nextID; i++ {
s := s.Shape(i)
if s == nil {
continue
}
numEdges += s.NumEdges()
if numEdges >= limit {
break
}
}
return numEdges
}
// Shape returns the shape with the given ID, or nil if the shape has been removed from the index.
func (s *ShapeIndex) Shape(id int32) Shape { return s.shapes[id] }
// idForShape returns the id of the given shape in this index, or -1 if it is
// not in the index.
//
// TODO(roberts): Need to figure out an appropriate way to expose this on a Shape.
// C++ allows a given S2 type (Loop, Polygon, etc) to be part of multiple indexes.
// By having each type extend S2Shape which has an id element, they all inherit their
// own id field rather than having to track it themselves.
func (s *ShapeIndex) idForShape(shape Shape) int32 {
for k, v := range s.shapes {
if v == shape {
return k
}
}
return -1
}
// Add adds the given shape to the index and returns the assigned ID..
func (s *ShapeIndex) Add(shape Shape) int32 {
s.shapes[s.nextID] = shape
s.nextID++
atomic.StoreInt32(&s.status, stale)
return s.nextID - 1
}
// Remove removes the given shape from the index.
func (s *ShapeIndex) Remove(shape Shape) {
// The index updates itself lazily because it is much more efficient to
// process additions and removals in batches.
id := s.idForShape(shape)
// If the shape wasn't found, it's already been removed or was not in the index.
if s.shapes[id] == nil {
return
}
// Remove the shape from the shapes map.
delete(s.shapes, id)
// We are removing a shape that has not yet been added to the index,
// so there is nothing else to do.
if id >= s.pendingAdditionsPos {
return
}
numEdges := shape.NumEdges()
removed := &removedShape{
shapeID: id,
hasInterior: shape.Dimension() == 2,
containsTrackerOrigin: shape.ReferencePoint().Contained,
edges: make([]Edge, numEdges),
}
for e := 0; e < numEdges; e++ {
removed.edges[e] = shape.Edge(e)
}
s.pendingRemovals = append(s.pendingRemovals, removed)
atomic.StoreInt32(&s.status, stale)
}
// IsFresh reports if there are no pending updates that need to be applied.
// This can be useful to avoid building the index unnecessarily, or for
// choosing between two different algorithms depending on whether the index
// is available.
//
// The returned index status may be slightly out of date if the index was
// built in a different thread. This is fine for the intended use (as an
// efficiency hint), but it should not be used by internal methods.
func (s *ShapeIndex) IsFresh() bool {
return atomic.LoadInt32(&s.status) == fresh
}
// isFirstUpdate reports if this is the first update to the index.
func (s *ShapeIndex) isFirstUpdate() bool {
// Note that it is not sufficient to check whether cellMap is empty, since
// entries are added to it during the update process.
return s.pendingAdditionsPos == 0
}
// isShapeBeingRemoved reports if the shape with the given ID is currently slated for removal.
func (s *ShapeIndex) isShapeBeingRemoved(shapeID int32) bool {
// All shape ids being removed fall below the index position of shapes being added.
return shapeID < s.pendingAdditionsPos
}
// maybeApplyUpdates checks if the index pieces have changed, and if so, applies pending updates.
func (s *ShapeIndex) maybeApplyUpdates() {
// TODO(roberts): To avoid acquiring and releasing the mutex on every
// query, we should use atomic operations when testing whether the status
// is fresh and when updating the status to be fresh. This guarantees
// that any thread that sees a status of fresh will also see the
// corresponding index updates.
if atomic.LoadInt32(&s.status) != fresh {
s.mu.Lock()
s.applyUpdatesInternal()
atomic.StoreInt32(&s.status, fresh)
s.mu.Unlock()
}
}
// applyUpdatesInternal does the actual work of updating the index by applying all
// pending additions and removals. It does *not* update the indexes status.
func (s *ShapeIndex) applyUpdatesInternal() {
// TODO(roberts): Building the index can use up to 20x as much memory per
// edge as the final index memory size. If this causes issues, add in
// batched updating to limit the amount of items per batch to a
// configurable memory footprint overhead.
t := newTracker()
// allEdges maps a Face to a collection of faceEdges.
allEdges := make([][]faceEdge, 6)
for _, p := range s.pendingRemovals {
s.removeShapeInternal(p, allEdges, t)
}
for id := s.pendingAdditionsPos; id < int32(len(s.shapes)); id++ {
s.addShapeInternal(id, allEdges, t)
}
for face := 0; face < 6; face++ {
s.updateFaceEdges(face, allEdges[face], t)
}
s.pendingRemovals = s.pendingRemovals[:0]
s.pendingAdditionsPos = int32(len(s.shapes))
// It is the caller's responsibility to update the index status.
}
// addShapeInternal clips all edges of the given shape to the six cube faces,
// adds the clipped edges to the set of allEdges, and starts tracking its
// interior if necessary.
func (s *ShapeIndex) addShapeInternal(shapeID int32, allEdges [][]faceEdge, t *tracker) {
shape, ok := s.shapes[shapeID]
if !ok {
// This shape has already been removed.
return
}
faceEdge := faceEdge{
shapeID: shapeID,
hasInterior: shape.Dimension() == 2,
}
if faceEdge.hasInterior {
t.addShape(shapeID, containsBruteForce(shape, t.focus()))
}
numEdges := shape.NumEdges()
for e := 0; e < numEdges; e++ {
edge := shape.Edge(e)
faceEdge.edgeID = e
faceEdge.edge = edge
faceEdge.maxLevel = maxLevelForEdge(edge)
s.addFaceEdge(faceEdge, allEdges)
}
}
// addFaceEdge adds the given faceEdge into the collection of all edges.
func (s *ShapeIndex) addFaceEdge(fe faceEdge, allEdges [][]faceEdge) {
aFace := face(fe.edge.V0.Vector)
// See if both endpoints are on the same face, and are far enough from
// the edge of the face that they don't intersect any (padded) adjacent face.
if aFace == face(fe.edge.V1.Vector) {
x, y := validFaceXYZToUV(aFace, fe.edge.V0.Vector)
fe.a = r2.Point{x, y}
x, y = validFaceXYZToUV(aFace, fe.edge.V1.Vector)
fe.b = r2.Point{x, y}
maxUV := 1 - cellPadding
if math.Abs(fe.a.X) <= maxUV && math.Abs(fe.a.Y) <= maxUV &&
math.Abs(fe.b.X) <= maxUV && math.Abs(fe.b.Y) <= maxUV {
allEdges[aFace] = append(allEdges[aFace], fe)
return
}
}
// Otherwise, we simply clip the edge to all six faces.
for face := 0; face < 6; face++ {
if aClip, bClip, intersects := ClipToPaddedFace(fe.edge.V0, fe.edge.V1, face, cellPadding); intersects {
fe.a = aClip
fe.b = bClip
allEdges[face] = append(allEdges[face], fe)
}
}
}
// updateFaceEdges adds or removes the various edges from the index.
// An edge is added if shapes[id] is not nil, and removed otherwise.
func (s *ShapeIndex) updateFaceEdges(face int, faceEdges []faceEdge, t *tracker) {
numEdges := len(faceEdges)
if numEdges == 0 && len(t.shapeIDs) == 0 {
return
}
// Create the initial clippedEdge for each faceEdge. Additional clipped
// edges are created when edges are split between child cells. We create
// two arrays, one containing the edge data and another containing pointers
// to those edges, so that during the recursion we only need to copy
// pointers in order to propagate an edge to the correct child.
clippedEdges := make([]*clippedEdge, numEdges)
bound := r2.EmptyRect()
for e := 0; e < numEdges; e++ {
clipped := &clippedEdge{
faceEdge: &faceEdges[e],
}
clipped.bound = r2.RectFromPoints(faceEdges[e].a, faceEdges[e].b)
clippedEdges[e] = clipped
bound = bound.AddRect(clipped.bound)
}
// Construct the initial face cell containing all the edges, and then update
// all the edges in the index recursively.
faceID := CellIDFromFace(face)
pcell := PaddedCellFromCellID(faceID, cellPadding)
disjointFromIndex := s.isFirstUpdate()
if numEdges > 0 {
shrunkID := s.shrinkToFit(pcell, bound)
if shrunkID != pcell.id {
// All the edges are contained by some descendant of the face cell. We
// can save a lot of work by starting directly with that cell, but if we
// are in the interior of at least one shape then we need to create
// index entries for the cells we are skipping over.
s.skipCellRange(faceID.RangeMin(), shrunkID.RangeMin(), t, disjointFromIndex)
pcell = PaddedCellFromCellID(shrunkID, cellPadding)
s.updateEdges(pcell, clippedEdges, t, disjointFromIndex)
s.skipCellRange(shrunkID.RangeMax().Next(), faceID.RangeMax().Next(), t, disjointFromIndex)
return
}
}
// Otherwise (no edges, or no shrinking is possible), subdivide normally.
s.updateEdges(pcell, clippedEdges, t, disjointFromIndex)
}
// shrinkToFit shrinks the PaddedCell to fit within the given bounds.
func (s *ShapeIndex) shrinkToFit(pcell *PaddedCell, bound r2.Rect) CellID {
shrunkID := pcell.ShrinkToFit(bound)
if !s.isFirstUpdate() && shrunkID != pcell.CellID() {
// Don't shrink any smaller than the existing index cells, since we need
// to combine the new edges with those cells.
iter := s.Iterator()
if iter.LocateCellID(shrunkID) == Indexed {
shrunkID = iter.CellID()
}
}
return shrunkID
}
// skipCellRange skips over the cells in the given range, creating index cells if we are
// currently in the interior of at least one shape.
func (s *ShapeIndex) skipCellRange(begin, end CellID, t *tracker, disjointFromIndex bool) {
// If we aren't in the interior of a shape, then skipping over cells is easy.
if len(t.shapeIDs) == 0 {
return
}
// Otherwise generate the list of cell ids that we need to visit, and create
// an index entry for each one.
skipped := CellUnionFromRange(begin, end)
for _, cell := range skipped {
var clippedEdges []*clippedEdge
s.updateEdges(PaddedCellFromCellID(cell, cellPadding), clippedEdges, t, disjointFromIndex)
}
}
// updateEdges adds or removes the given edges whose bounding boxes intersect a
// given cell. disjointFromIndex is an optimization hint indicating that cellMap
// does not contain any entries that overlap the given cell.
func (s *ShapeIndex) updateEdges(pcell *PaddedCell, edges []*clippedEdge, t *tracker, disjointFromIndex bool) {
// This function is recursive with a maximum recursion depth of 30 (maxLevel).
// Incremental updates are handled as follows. All edges being added or
// removed are combined together in edges, and all shapes with interiors
// are tracked using tracker. We subdivide recursively as usual until we
// encounter an existing index cell. At this point we absorb the index
// cell as follows:
//
// - Edges and shapes that are being removed are deleted from edges and
// tracker.
// - All remaining edges and shapes from the index cell are added to
// edges and tracker.
// - Continue subdividing recursively, creating new index cells as needed.
// - When the recursion gets back to the cell that was absorbed, we
// restore edges and tracker to their previous state.
//
// Note that the only reason that we include removed shapes in the recursive
// subdivision process is so that we can find all of the index cells that
// contain those shapes efficiently, without maintaining an explicit list of
// index cells for each shape (which would be expensive in terms of memory).
indexCellAbsorbed := false
if !disjointFromIndex {
// There may be existing index cells contained inside pcell. If we
// encounter such a cell, we need to combine the edges being updated with
// the existing cell contents by absorbing the cell.
iter := s.Iterator()
r := iter.LocateCellID(pcell.id)
if r == Disjoint {
disjointFromIndex = true
} else if r == Indexed {
// Absorb the index cell by transferring its contents to edges and
// deleting it. We also start tracking the interior of any new shapes.
s.absorbIndexCell(pcell, iter, edges, t)
indexCellAbsorbed = true
disjointFromIndex = true
} else {
// DCHECK_EQ(SUBDIVIDED, r)
}
}
// If there are existing index cells below us, then we need to keep
// subdividing so that we can merge with those cells. Otherwise,
// makeIndexCell checks if the number of edges is small enough, and creates
// an index cell if possible (returning true when it does so).
if !disjointFromIndex || !s.makeIndexCell(pcell, edges, t) {
// TODO(roberts): If it turns out to have memory problems when there
// are 10M+ edges in the index, look into pre-allocating space so we
// are not always appending.
childEdges := [2][2][]*clippedEdge{} // [i][j]
// Compute the middle of the padded cell, defined as the rectangle in
// (u,v)-space that belongs to all four (padded) children. By comparing
// against the four boundaries of middle we can determine which children
// each edge needs to be propagated to.
middle := pcell.Middle()
// Build up a vector edges to be passed to each child cell. The (i,j)
// directions are left (i=0), right (i=1), lower (j=0), and upper (j=1).
// Note that the vast majority of edges are propagated to a single child.
for _, edge := range edges {
if edge.bound.X.Hi <= middle.X.Lo {
// Edge is entirely contained in the two left children.
a, b := s.clipVAxis(edge, middle.Y)
if a != nil {
childEdges[0][0] = append(childEdges[0][0], a)
}
if b != nil {
childEdges[0][1] = append(childEdges[0][1], b)
}
} else if edge.bound.X.Lo >= middle.X.Hi {
// Edge is entirely contained in the two right children.
a, b := s.clipVAxis(edge, middle.Y)
if a != nil {
childEdges[1][0] = append(childEdges[1][0], a)
}
if b != nil {
childEdges[1][1] = append(childEdges[1][1], b)
}
} else if edge.bound.Y.Hi <= middle.Y.Lo {
// Edge is entirely contained in the two lower children.
if a := s.clipUBound(edge, 1, middle.X.Hi); a != nil {
childEdges[0][0] = append(childEdges[0][0], a)
}
if b := s.clipUBound(edge, 0, middle.X.Lo); b != nil {
childEdges[1][0] = append(childEdges[1][0], b)
}
} else if edge.bound.Y.Lo >= middle.Y.Hi {
// Edge is entirely contained in the two upper children.
if a := s.clipUBound(edge, 1, middle.X.Hi); a != nil {
childEdges[0][1] = append(childEdges[0][1], a)
}
if b := s.clipUBound(edge, 0, middle.X.Lo); b != nil {
childEdges[1][1] = append(childEdges[1][1], b)
}
} else {
// The edge bound spans all four children. The edge
// itself intersects either three or four padded children.
left := s.clipUBound(edge, 1, middle.X.Hi)
a, b := s.clipVAxis(left, middle.Y)
if a != nil {
childEdges[0][0] = append(childEdges[0][0], a)
}
if b != nil {
childEdges[0][1] = append(childEdges[0][1], b)
}
right := s.clipUBound(edge, 0, middle.X.Lo)
a, b = s.clipVAxis(right, middle.Y)
if a != nil {
childEdges[1][0] = append(childEdges[1][0], a)
}
if b != nil {
childEdges[1][1] = append(childEdges[1][1], b)
}
}
}
// Now recursively update the edges in each child. We call the children in
// increasing order of CellID so that when the index is first constructed,
// all insertions into cellMap are at the end (which is much faster).
for pos := 0; pos < 4; pos++ {
i, j := pcell.ChildIJ(pos)
if len(childEdges[i][j]) > 0 || len(t.shapeIDs) > 0 {
s.updateEdges(PaddedCellFromParentIJ(pcell, i, j), childEdges[i][j],
t, disjointFromIndex)
}
}
}
if indexCellAbsorbed {
// Restore the state for any edges being removed that we are tracking.
t.restoreStateBefore(s.pendingAdditionsPos)
}
}
// makeIndexCell builds an indexCell from the given padded cell and set of edges and adds
// it to the index. If the cell or edges are empty, no cell is added.
func (s *ShapeIndex) makeIndexCell(p *PaddedCell, edges []*clippedEdge, t *tracker) bool {
// If the cell is empty, no index cell is needed. (In most cases this
// situation is detected before we get to this point, but this can happen
// when all shapes in a cell are removed.)
if len(edges) == 0 && len(t.shapeIDs) == 0 {
return true
}
// Count the number of edges that have not reached their maximum level yet.
// Return false if there are too many such edges.
count := 0
for _, ce := range edges {
if p.Level() < ce.faceEdge.maxLevel {
count++
}
if count > s.maxEdgesPerCell {
return false
}
}
// Possible optimization: Continue subdividing as long as exactly one child
// of the padded cell intersects the given edges. This can be done by finding
// the bounding box of all the edges and calling ShrinkToFit:
//
// cellID = p.ShrinkToFit(RectBound(edges));
//
// Currently this is not beneficial; it slows down construction by 4-25%
// (mainly computing the union of the bounding rectangles) and also slows
// down queries (since more recursive clipping is required to get down to
// the level of a spatial index cell). But it may be worth trying again
// once containsCenter is computed and all algorithms are modified to
// take advantage of it.
// We update the InteriorTracker as follows. For every Cell in the index
// we construct two edges: one edge from entry vertex of the cell to its
// center, and one from the cell center to its exit vertex. Here entry
// and exit refer the CellID ordering, i.e. the order in which points
// are encountered along the 2 space-filling curve. The exit vertex then
// becomes the entry vertex for the next cell in the index, unless there are
// one or more empty intervening cells, in which case the InteriorTracker
// state is unchanged because the intervening cells have no edges.
// Shift the InteriorTracker focus point to the center of the current cell.
if t.isActive && len(edges) != 0 {
if !t.atCellID(p.id) {
t.moveTo(p.EntryVertex())
}
t.drawTo(p.Center())
s.testAllEdges(edges, t)
}
// Allocate and fill a new index cell. To get the total number of shapes we
// need to merge the shapes associated with the intersecting edges together
// with the shapes that happen to contain the cell center.
cshapeIDs := t.shapeIDs
numShapes := s.countShapes(edges, cshapeIDs)
cell := NewShapeIndexCell(numShapes)
// To fill the index cell we merge the two sources of shapes: edge shapes
// (those that have at least one edge that intersects this cell), and
// containing shapes (those that contain the cell center). We keep track
// of the index of the next intersecting edge and the next containing shape
// as we go along. Both sets of shape ids are already sorted.
eNext := 0
cNextIdx := 0
for i := 0; i < numShapes; i++ {
var clipped *clippedShape
// advance to next value base + i
eshapeID := int32(s.Len())
cshapeID := eshapeID // Sentinels
if eNext != len(edges) {
eshapeID = edges[eNext].faceEdge.shapeID
}
if cNextIdx < len(cshapeIDs) {
cshapeID = cshapeIDs[cNextIdx]
}
eBegin := eNext
if cshapeID < eshapeID {
// The entire cell is in the shape interior.
clipped = newClippedShape(cshapeID, 0)
clipped.containsCenter = true
cNextIdx++
} else {
// Count the number of edges for this shape and allocate space for them.
for eNext < len(edges) && edges[eNext].faceEdge.shapeID == eshapeID {
eNext++
}
clipped = newClippedShape(eshapeID, eNext-eBegin)
for e := eBegin; e < eNext; e++ {
clipped.edges[e-eBegin] = edges[e].faceEdge.edgeID
}
if cshapeID == eshapeID {
clipped.containsCenter = true
cNextIdx++
}
}
cell.shapes[i] = clipped
}
// Add this cell to the map.
s.cellMap[p.id] = cell
s.cells = append(s.cells, p.id)
// Shift the tracker focus point to the exit vertex of this cell.
if t.isActive && len(edges) != 0 {
t.drawTo(p.ExitVertex())
s.testAllEdges(edges, t)
t.setNextCellID(p.id.Next())
}
return true
}
// updateBound updates the specified endpoint of the given clipped edge and returns the
// resulting clipped edge.
func (s *ShapeIndex) updateBound(edge *clippedEdge, uEnd int, u float64, vEnd int, v float64) *clippedEdge {
c := &clippedEdge{faceEdge: edge.faceEdge}
if uEnd == 0 {
c.bound.X.Lo = u
c.bound.X.Hi = edge.bound.X.Hi
} else {
c.bound.X.Lo = edge.bound.X.Lo
c.bound.X.Hi = u
}
if vEnd == 0 {
c.bound.Y.Lo = v
c.bound.Y.Hi = edge.bound.Y.Hi
} else {
c.bound.Y.Lo = edge.bound.Y.Lo
c.bound.Y.Hi = v
}
return c
}
// clipUBound clips the given endpoint (lo=0, hi=1) of the u-axis so that
// it does not extend past the given value of the given edge.
func (s *ShapeIndex) clipUBound(edge *clippedEdge, uEnd int, u float64) *clippedEdge {
// First check whether the edge actually requires any clipping. (Sometimes
// this method is called when clipping is not necessary, e.g. when one edge
// endpoint is in the overlap area between two padded child cells.)
if uEnd == 0 {
if edge.bound.X.Lo >= u {
return edge
}
} else {
if edge.bound.X.Hi <= u {
return edge
}
}
// We interpolate the new v-value from the endpoints of the original edge.
// This has two advantages: (1) we don't need to store the clipped endpoints
// at all, just their bounding box; and (2) it avoids the accumulation of
// roundoff errors due to repeated interpolations. The result needs to be
// clamped to ensure that it is in the appropriate range.
e := edge.faceEdge
v := edge.bound.Y.ClampPoint(interpolateFloat64(u, e.a.X, e.b.X, e.a.Y, e.b.Y))
// Determine which endpoint of the v-axis bound to update. If the edge
// slope is positive we update the same endpoint, otherwise we update the
// opposite endpoint.
var vEnd int
positiveSlope := (e.a.X > e.b.X) == (e.a.Y > e.b.Y)
if (uEnd == 1) == positiveSlope {
vEnd = 1
}
return s.updateBound(edge, uEnd, u, vEnd, v)
}
// clipVBound clips the given endpoint (lo=0, hi=1) of the v-axis so that
// it does not extend past the given value of the given edge.
func (s *ShapeIndex) clipVBound(edge *clippedEdge, vEnd int, v float64) *clippedEdge {
if vEnd == 0 {
if edge.bound.Y.Lo >= v {
return edge
}
} else {
if edge.bound.Y.Hi <= v {
return edge
}
}
// We interpolate the new v-value from the endpoints of the original edge.
// This has two advantages: (1) we don't need to store the clipped endpoints
// at all, just their bounding box; and (2) it avoids the accumulation of
// roundoff errors due to repeated interpolations. The result needs to be
// clamped to ensure that it is in the appropriate range.
e := edge.faceEdge
u := edge.bound.X.ClampPoint(interpolateFloat64(v, e.a.Y, e.b.Y, e.a.X, e.b.X))
// Determine which endpoint of the v-axis bound to update. If the edge
// slope is positive we update the same endpoint, otherwise we update the
// opposite endpoint.
var uEnd int
positiveSlope := (e.a.X > e.b.X) == (e.a.Y > e.b.Y)
if (vEnd == 1) == positiveSlope {
uEnd = 1
}
return s.updateBound(edge, uEnd, u, vEnd, v)
}
// cliupVAxis returns the given edge clipped to within the boundaries of the middle
// interval along the v-axis, and adds the result to its children.
func (s *ShapeIndex) clipVAxis(edge *clippedEdge, middle r1.Interval) (a, b *clippedEdge) {
if edge.bound.Y.Hi <= middle.Lo {
// Edge is entirely contained in the lower child.
return edge, nil
} else if edge.bound.Y.Lo >= middle.Hi {
// Edge is entirely contained in the upper child.
return nil, edge
}
// The edge bound spans both children.
return s.clipVBound(edge, 1, middle.Hi), s.clipVBound(edge, 0, middle.Lo)
}
// absorbIndexCell absorbs an index cell by transferring its contents to edges
// and/or "tracker", and then delete this cell from the index. If edges includes
// any edges that are being removed, this method also updates their
// InteriorTracker state to correspond to the exit vertex of this cell.
func (s *ShapeIndex) absorbIndexCell(p *PaddedCell, iter *ShapeIndexIterator, edges []*clippedEdge, t *tracker) {
// When we absorb a cell, we erase all the edges that are being removed.
// However when we are finished with this cell, we want to restore the state
// of those edges (since that is how we find all the index cells that need
// to be updated). The edges themselves are restored automatically when
// UpdateEdges returns from its recursive call, but the InteriorTracker
// state needs to be restored explicitly.
//
// Here we first update the InteriorTracker state for removed edges to
// correspond to the exit vertex of this cell, and then save the
// InteriorTracker state. This state will be restored by UpdateEdges when
// it is finished processing the contents of this cell.
if t.isActive && len(edges) != 0 && s.isShapeBeingRemoved(edges[0].faceEdge.shapeID) {
// We probably need to update the tracker. ("Probably" because
// it's possible that all shapes being removed do not have interiors.)
if !t.atCellID(p.id) {
t.moveTo(p.EntryVertex())
}
t.drawTo(p.ExitVertex())
t.setNextCellID(p.id.Next())
for _, edge := range edges {
fe := edge.faceEdge
if !s.isShapeBeingRemoved(fe.shapeID) {
break // All shapes being removed come first.
}
if fe.hasInterior {
t.testEdge(fe.shapeID, fe.edge)
}
}
}
// Save the state of the edges being removed, so that it can be restored
// when we are finished processing this cell and its children. We don't
// need to save the state of the edges being added because they aren't being
// removed from "edges" and will therefore be updated normally as we visit
// this cell and its children.
t.saveAndClearStateBefore(s.pendingAdditionsPos)
// Create a faceEdge for each edge in this cell that isn't being removed.
var faceEdges []*faceEdge
trackerMoved := false
cell := iter.IndexCell()
for _, clipped := range cell.shapes {
shapeID := clipped.shapeID
shape := s.Shape(shapeID)
if shape == nil {
continue // This shape is being removed.
}
numClipped := clipped.numEdges()
// If this shape has an interior, start tracking whether we are inside the
// shape. updateEdges wants to know whether the entry vertex of this
// cell is inside the shape, but we only know whether the center of the
// cell is inside the shape, so we need to test all the edges against the
// line segment from the cell center to the entry vertex.
edge := &faceEdge{
shapeID: shapeID,
hasInterior: shape.Dimension() == 2,
}
if edge.hasInterior {
t.addShape(shapeID, clipped.containsCenter)
// There might not be any edges in this entire cell (i.e., it might be
// in the interior of all shapes), so we delay updating the tracker
// until we see the first edge.
if !trackerMoved && numClipped > 0 {
t.moveTo(p.Center())
t.drawTo(p.EntryVertex())
t.setNextCellID(p.id)
trackerMoved = true
}
}
for i := 0; i < numClipped; i++ {
edgeID := clipped.edges[i]
edge.edgeID = edgeID
edge.edge = shape.Edge(edgeID)
edge.maxLevel = maxLevelForEdge(edge.edge)
if edge.hasInterior {
t.testEdge(shapeID, edge.edge)
}
var ok bool
edge.a, edge.b, ok = ClipToPaddedFace(edge.edge.V0, edge.edge.V1, p.id.Face(), cellPadding)
if !ok {
panic("invariant failure in ShapeIndex")
}
faceEdges = append(faceEdges, edge)
}
}
// Now create a clippedEdge for each faceEdge, and put them in "new_edges".
var newEdges []*clippedEdge
for _, faceEdge := range faceEdges {
clipped := &clippedEdge{
faceEdge: faceEdge,
bound: clippedEdgeBound(faceEdge.a, faceEdge.b, p.bound),
}
newEdges = append(newEdges, clipped)
}
// Discard any edges from "edges" that are being removed, and append the
// remainder to "newEdges" (This keeps the edges sorted by shape id.)
for i, clipped := range edges {
if !s.isShapeBeingRemoved(clipped.faceEdge.shapeID) {
newEdges = append(newEdges, edges[i:]...)
break
}
}
// Update the edge list and delete this cell from the index.
edges, newEdges = newEdges, edges
delete(s.cellMap, p.id)
// TODO(roberts): delete from s.Cells
}
// testAllEdges calls the trackers testEdge on all edges from shapes that have interiors.
func (s *ShapeIndex) testAllEdges(edges []*clippedEdge, t *tracker) {
for _, edge := range edges {
if edge.faceEdge.hasInterior {
t.testEdge(edge.faceEdge.shapeID, edge.faceEdge.edge)
}
}
}
// countShapes reports the number of distinct shapes that are either associated with the
// given edges, or that are currently stored in the InteriorTracker.
func (s *ShapeIndex) countShapes(edges []*clippedEdge, shapeIDs []int32) int {
count := 0
lastShapeID := int32(-1)
// next clipped shape id in the shapeIDs list.
clippedNext := int32(0)
// index of the current element in the shapeIDs list.
shapeIDidx := 0
for _, edge := range edges {
if edge.faceEdge.shapeID == lastShapeID {
continue
}
count++
lastShapeID = edge.faceEdge.shapeID
// Skip over any containing shapes up to and including this one,
// updating count as appropriate.
for ; shapeIDidx < len(shapeIDs); shapeIDidx++ {
clippedNext = shapeIDs[shapeIDidx]
if clippedNext > lastShapeID {
break
}
if clippedNext < lastShapeID {
count++
}
}
}
// Count any remaining containing shapes.
count += len(shapeIDs) - shapeIDidx
return count
}
// maxLevelForEdge reports the maximum level for a given edge.
func maxLevelForEdge(edge Edge) int {
// Compute the maximum cell size for which this edge is considered long.
// The calculation does not need to be perfectly accurate, so we use Norm
// rather than Angle for speed.
cellSize := edge.V0.Sub(edge.V1.Vector).Norm() * cellSizeToLongEdgeRatio
// Now return the first level encountered during subdivision where the
// average cell size is at most cellSize.
return AvgEdgeMetric.MinLevel(cellSize)
}
// removeShapeInternal does the actual work for removing a given shape from the index.
func (s *ShapeIndex) removeShapeInternal(removed *removedShape, allEdges [][]faceEdge, t *tracker) {
// TODO(roberts): finish the implementation of this.
}
|