paddedcell.go (8581B)
1 // Copyright 2016 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package s2 16 17 import ( 18 "github.com/golang/geo/r1" 19 "github.com/golang/geo/r2" 20 ) 21 22 // PaddedCell represents a Cell whose (u,v)-range has been expanded on 23 // all sides by a given amount of "padding". Unlike Cell, its methods and 24 // representation are optimized for clipping edges against Cell boundaries 25 // to determine which cells are intersected by a given set of edges. 26 type PaddedCell struct { 27 id CellID 28 padding float64 29 bound r2.Rect 30 middle r2.Rect // A rect in (u, v)-space that belongs to all four children. 31 iLo, jLo int // Minimum (i,j)-coordinates of this cell before padding 32 orientation int // Hilbert curve orientation of this cell. 33 level int 34 } 35 36 // PaddedCellFromCellID constructs a padded cell with the given padding. 37 func PaddedCellFromCellID(id CellID, padding float64) *PaddedCell { 38 p := &PaddedCell{ 39 id: id, 40 padding: padding, 41 middle: r2.EmptyRect(), 42 } 43 44 // Fast path for constructing a top-level face (the most common case). 45 if id.isFace() { 46 limit := padding + 1 47 p.bound = r2.Rect{r1.Interval{-limit, limit}, r1.Interval{-limit, limit}} 48 p.middle = r2.Rect{r1.Interval{-padding, padding}, r1.Interval{-padding, padding}} 49 p.orientation = id.Face() & 1 50 return p 51 } 52 53 _, p.iLo, p.jLo, p.orientation = id.faceIJOrientation() 54 p.level = id.Level() 55 p.bound = ijLevelToBoundUV(p.iLo, p.jLo, p.level).ExpandedByMargin(padding) 56 ijSize := sizeIJ(p.level) 57 p.iLo &= -ijSize 58 p.jLo &= -ijSize 59 60 return p 61 } 62 63 // PaddedCellFromParentIJ constructs the child of parent with the given (i,j) index. 64 // The four child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j 65 // indices correspond to increasing u- and v-values respectively. 66 func PaddedCellFromParentIJ(parent *PaddedCell, i, j int) *PaddedCell { 67 // Compute the position and orientation of the child incrementally from the 68 // orientation of the parent. 69 pos := ijToPos[parent.orientation][2*i+j] 70 71 p := &PaddedCell{ 72 id: parent.id.Children()[pos], 73 padding: parent.padding, 74 bound: parent.bound, 75 orientation: parent.orientation ^ posToOrientation[pos], 76 level: parent.level + 1, 77 middle: r2.EmptyRect(), 78 } 79 80 ijSize := sizeIJ(p.level) 81 p.iLo = parent.iLo + i*ijSize 82 p.jLo = parent.jLo + j*ijSize 83 84 // For each child, one corner of the bound is taken directly from the parent 85 // while the diagonally opposite corner is taken from middle(). 86 middle := parent.Middle() 87 if i == 1 { 88 p.bound.X.Lo = middle.X.Lo 89 } else { 90 p.bound.X.Hi = middle.X.Hi 91 } 92 if j == 1 { 93 p.bound.Y.Lo = middle.Y.Lo 94 } else { 95 p.bound.Y.Hi = middle.Y.Hi 96 } 97 98 return p 99 } 100 101 // CellID returns the CellID this padded cell represents. 102 func (p PaddedCell) CellID() CellID { 103 return p.id 104 } 105 106 // Padding returns the amount of padding on this cell. 107 func (p PaddedCell) Padding() float64 { 108 return p.padding 109 } 110 111 // Level returns the level this cell is at. 112 func (p PaddedCell) Level() int { 113 return p.level 114 } 115 116 // Center returns the center of this cell. 117 func (p PaddedCell) Center() Point { 118 ijSize := sizeIJ(p.level) 119 si := uint32(2*p.iLo + ijSize) 120 ti := uint32(2*p.jLo + ijSize) 121 return Point{faceSiTiToXYZ(p.id.Face(), si, ti).Normalize()} 122 } 123 124 // Middle returns the rectangle in the middle of this cell that belongs to 125 // all four of its children in (u,v)-space. 126 func (p *PaddedCell) Middle() r2.Rect { 127 // We compute this field lazily because it is not needed the majority of the 128 // time (i.e., for cells where the recursion terminates). 129 if p.middle.IsEmpty() { 130 ijSize := sizeIJ(p.level) 131 u := stToUV(siTiToST(uint32(2*p.iLo + ijSize))) 132 v := stToUV(siTiToST(uint32(2*p.jLo + ijSize))) 133 p.middle = r2.Rect{ 134 r1.Interval{u - p.padding, u + p.padding}, 135 r1.Interval{v - p.padding, v + p.padding}, 136 } 137 } 138 return p.middle 139 } 140 141 // Bound returns the bounds for this cell in (u,v)-space including padding. 142 func (p PaddedCell) Bound() r2.Rect { 143 return p.bound 144 } 145 146 // ChildIJ returns the (i,j) coordinates for the child cell at the given traversal 147 // position. The traversal position corresponds to the order in which child 148 // cells are visited by the Hilbert curve. 149 func (p PaddedCell) ChildIJ(pos int) (i, j int) { 150 ij := posToIJ[p.orientation][pos] 151 return ij >> 1, ij & 1 152 } 153 154 // EntryVertex return the vertex where the space-filling curve enters this cell. 155 func (p PaddedCell) EntryVertex() Point { 156 // The curve enters at the (0,0) vertex unless the axis directions are 157 // reversed, in which case it enters at the (1,1) vertex. 158 i := p.iLo 159 j := p.jLo 160 if p.orientation&invertMask != 0 { 161 ijSize := sizeIJ(p.level) 162 i += ijSize 163 j += ijSize 164 } 165 return Point{faceSiTiToXYZ(p.id.Face(), uint32(2*i), uint32(2*j)).Normalize()} 166 } 167 168 // ExitVertex returns the vertex where the space-filling curve exits this cell. 169 func (p PaddedCell) ExitVertex() Point { 170 // The curve exits at the (1,0) vertex unless the axes are swapped or 171 // inverted but not both, in which case it exits at the (0,1) vertex. 172 i := p.iLo 173 j := p.jLo 174 ijSize := sizeIJ(p.level) 175 if p.orientation == 0 || p.orientation == swapMask+invertMask { 176 i += ijSize 177 } else { 178 j += ijSize 179 } 180 return Point{faceSiTiToXYZ(p.id.Face(), uint32(2*i), uint32(2*j)).Normalize()} 181 } 182 183 // ShrinkToFit returns the smallest CellID that contains all descendants of this 184 // padded cell whose bounds intersect the given rect. For algorithms that use 185 // recursive subdivision to find the cells that intersect a particular object, this 186 // method can be used to skip all of the initial subdivision steps where only 187 // one child needs to be expanded. 188 // 189 // Note that this method is not the same as returning the smallest cell that contains 190 // the intersection of this cell with rect. Because of the padding, even if one child 191 // completely contains rect it is still possible that a neighboring child may also 192 // intersect the given rect. 193 // 194 // The provided Rect must intersect the bounds of this cell. 195 func (p *PaddedCell) ShrinkToFit(rect r2.Rect) CellID { 196 // Quick rejection test: if rect contains the center of this cell along 197 // either axis, then no further shrinking is possible. 198 if p.level == 0 { 199 // Fast path (most calls to this function start with a face cell). 200 if rect.X.Contains(0) || rect.Y.Contains(0) { 201 return p.id 202 } 203 } 204 205 ijSize := sizeIJ(p.level) 206 if rect.X.Contains(stToUV(siTiToST(uint32(2*p.iLo+ijSize)))) || 207 rect.Y.Contains(stToUV(siTiToST(uint32(2*p.jLo+ijSize)))) { 208 return p.id 209 } 210 211 // Otherwise we expand rect by the given padding on all sides and find 212 // the range of coordinates that it spans along the i- and j-axes. We then 213 // compute the highest bit position at which the min and max coordinates 214 // differ. This corresponds to the first cell level at which at least two 215 // children intersect rect. 216 217 // Increase the padding to compensate for the error in uvToST. 218 // (The constant below is a provable upper bound on the additional error.) 219 padded := rect.ExpandedByMargin(p.padding + 1.5*dblEpsilon) 220 iMin, jMin := p.iLo, p.jLo // Min i- or j- coordinate spanned by padded 221 var iXor, jXor int // XOR of the min and max i- or j-coordinates 222 223 if iMin < stToIJ(uvToST(padded.X.Lo)) { 224 iMin = stToIJ(uvToST(padded.X.Lo)) 225 } 226 if a, b := p.iLo+ijSize-1, stToIJ(uvToST(padded.X.Hi)); a <= b { 227 iXor = iMin ^ a 228 } else { 229 iXor = iMin ^ b 230 } 231 232 if jMin < stToIJ(uvToST(padded.Y.Lo)) { 233 jMin = stToIJ(uvToST(padded.Y.Lo)) 234 } 235 if a, b := p.jLo+ijSize-1, stToIJ(uvToST(padded.Y.Hi)); a <= b { 236 jXor = jMin ^ a 237 } else { 238 jXor = jMin ^ b 239 } 240 241 // Compute the highest bit position where the two i- or j-endpoints differ, 242 // and then choose the cell level that includes both of these endpoints. So 243 // if both pairs of endpoints are equal we choose maxLevel; if they differ 244 // only at bit 0, we choose (maxLevel - 1), and so on. 245 levelMSB := uint64(((iXor | jXor) << 1) + 1) 246 level := maxLevel - findMSBSetNonZero64(levelMSB) 247 if level <= p.level { 248 return p.id 249 } 250 251 return cellIDFromFaceIJ(p.id.Face(), iMin, jMin).Parent(level) 252 }