diff options
author | 2024-08-02 11:46:41 +0000 | |
---|---|---|
committer | 2024-08-02 12:46:41 +0100 | |
commit | 94e87610c4ce9bbb1c614a61bab29c1422fed11b (patch) | |
tree | 2e06b8ce64212140e796f6077ba841b6cc678501 /vendor/github.com/golang/geo/s2/cellunion.go | |
parent | [feature] Allow import of following and blocks via CSV (#3150) (diff) | |
download | gotosocial-94e87610c4ce9bbb1c614a61bab29c1422fed11b.tar.xz |
[chore] add back exif-terminator and use only for jpeg,png,webp (#3161)
* add back exif-terminator and use only for jpeg,png,webp
* fix arguments passed to terminateExif()
* pull in latest exif-terminator
* fix test
* update processed img
---------
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Diffstat (limited to 'vendor/github.com/golang/geo/s2/cellunion.go')
-rw-r--r-- | vendor/github.com/golang/geo/s2/cellunion.go | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/vendor/github.com/golang/geo/s2/cellunion.go b/vendor/github.com/golang/geo/s2/cellunion.go new file mode 100644 index 000000000..0654de973 --- /dev/null +++ b/vendor/github.com/golang/geo/s2/cellunion.go @@ -0,0 +1,590 @@ +// 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 s2 + +import ( + "fmt" + "io" + "sort" + + "github.com/golang/geo/s1" +) + +// A CellUnion is a collection of CellIDs. +// +// It is normalized if it is sorted, and does not contain redundancy. +// Specifically, it may not contain the same CellID twice, nor a CellID that +// is contained by another, nor the four sibling CellIDs that are children of +// a single higher level CellID. +// +// CellUnions are not required to be normalized, but certain operations will +// return different results if they are not (e.g. Contains). +type CellUnion []CellID + +// CellUnionFromRange creates a CellUnion that covers the half-open range +// of leaf cells [begin, end). If begin == end the resulting union is empty. +// This requires that begin and end are both leaves, and begin <= end. +// To create a closed-ended range, pass in end.Next(). +func CellUnionFromRange(begin, end CellID) CellUnion { + // We repeatedly add the largest cell we can. + var cu CellUnion + for id := begin.MaxTile(end); id != end; id = id.Next().MaxTile(end) { + cu = append(cu, id) + } + // The output is normalized because the cells are added in order by the iteration. + return cu +} + +// CellUnionFromUnion creates a CellUnion from the union of the given CellUnions. +func CellUnionFromUnion(cellUnions ...CellUnion) CellUnion { + var cu CellUnion + for _, cellUnion := range cellUnions { + cu = append(cu, cellUnion...) + } + cu.Normalize() + return cu +} + +// CellUnionFromIntersection creates a CellUnion from the intersection of the given CellUnions. +func CellUnionFromIntersection(x, y CellUnion) CellUnion { + var cu CellUnion + + // This is a fairly efficient calculation that uses binary search to skip + // over sections of both input vectors. It takes constant time if all the + // cells of x come before or after all the cells of y in CellID order. + var i, j int + for i < len(x) && j < len(y) { + iMin := x[i].RangeMin() + jMin := y[j].RangeMin() + if iMin > jMin { + // Either j.Contains(i) or the two cells are disjoint. + if x[i] <= y[j].RangeMax() { + cu = append(cu, x[i]) + i++ + } else { + // Advance j to the first cell possibly contained by x[i]. + j = y.lowerBound(j+1, len(y), iMin) + // The previous cell y[j-1] may now contain x[i]. + if x[i] <= y[j-1].RangeMax() { + j-- + } + } + } else if jMin > iMin { + // Identical to the code above with i and j reversed. + if y[j] <= x[i].RangeMax() { + cu = append(cu, y[j]) + j++ + } else { + i = x.lowerBound(i+1, len(x), jMin) + if y[j] <= x[i-1].RangeMax() { + i-- + } + } + } else { + // i and j have the same RangeMin(), so one contains the other. + if x[i] < y[j] { + cu = append(cu, x[i]) + i++ + } else { + cu = append(cu, y[j]) + j++ + } + } + } + + // The output is generated in sorted order. + cu.Normalize() + return cu +} + +// CellUnionFromIntersectionWithCellID creates a CellUnion from the intersection +// of a CellUnion with the given CellID. This can be useful for splitting a +// CellUnion into chunks. +func CellUnionFromIntersectionWithCellID(x CellUnion, id CellID) CellUnion { + var cu CellUnion + if x.ContainsCellID(id) { + cu = append(cu, id) + cu.Normalize() + return cu + } + + idmax := id.RangeMax() + for i := x.lowerBound(0, len(x), id.RangeMin()); i < len(x) && x[i] <= idmax; i++ { + cu = append(cu, x[i]) + } + + cu.Normalize() + return cu +} + +// CellUnionFromDifference creates a CellUnion from the difference (x - y) +// of the given CellUnions. +func CellUnionFromDifference(x, y CellUnion) CellUnion { + // TODO(roberts): This is approximately O(N*log(N)), but could probably + // use similar techniques as CellUnionFromIntersectionWithCellID to be more efficient. + + var cu CellUnion + for _, xid := range x { + cu.cellUnionDifferenceInternal(xid, &y) + } + + // The output is generated in sorted order, and there should not be any + // cells that can be merged (provided that both inputs were normalized). + return cu +} + +// The C++ constructor methods FromNormalized and FromVerbatim are not necessary +// since they don't call Normalize, and just set the CellIDs directly on the object, +// so straight casting is sufficient in Go to replicate this behavior. + +// IsValid reports whether the cell union is valid, meaning that the CellIDs are +// valid, non-overlapping, and sorted in increasing order. +func (cu *CellUnion) IsValid() bool { + for i, cid := range *cu { + if !cid.IsValid() { + return false + } + if i == 0 { + continue + } + if (*cu)[i-1].RangeMax() >= cid.RangeMin() { + return false + } + } + return true +} + +// IsNormalized reports whether the cell union is normalized, meaning that it is +// satisfies IsValid and that no four cells have a common parent. +// Certain operations such as Contains will return a different +// result if the cell union is not normalized. +func (cu *CellUnion) IsNormalized() bool { + for i, cid := range *cu { + if !cid.IsValid() { + return false + } + if i == 0 { + continue + } + if (*cu)[i-1].RangeMax() >= cid.RangeMin() { + return false + } + if i < 3 { + continue + } + if areSiblings((*cu)[i-3], (*cu)[i-2], (*cu)[i-1], cid) { + return false + } + } + return true +} + +// Normalize normalizes the CellUnion. +func (cu *CellUnion) Normalize() { + sortCellIDs(*cu) + + output := make([]CellID, 0, len(*cu)) // the list of accepted cells + // Loop invariant: output is a sorted list of cells with no redundancy. + for _, ci := range *cu { + // The first two passes here either ignore this new candidate, + // or remove previously accepted cells that are covered by this candidate. + + // Ignore this cell if it is contained by the previous one. + // We only need to check the last accepted cell. The ordering of the + // cells implies containment (but not the converse), and output has no redundancy, + // so if this candidate is not contained by the last accepted cell + // then it cannot be contained by any previously accepted cell. + if len(output) > 0 && output[len(output)-1].Contains(ci) { + continue + } + + // Discard any previously accepted cells contained by this one. + // This could be any contiguous trailing subsequence, but it can't be + // a discontiguous subsequence because of the containment property of + // sorted S2 cells mentioned above. + j := len(output) - 1 // last index to keep + for j >= 0 { + if !ci.Contains(output[j]) { + break + } + j-- + } + output = output[:j+1] + + // See if the last three cells plus this one can be collapsed. + // We loop because collapsing three accepted cells and adding a higher level cell + // could cascade into previously accepted cells. + for len(output) >= 3 && areSiblings(output[len(output)-3], output[len(output)-2], output[len(output)-1], ci) { + // Replace four children by their parent cell. + output = output[:len(output)-3] + ci = ci.immediateParent() // checked !ci.isFace above + } + output = append(output, ci) + } + *cu = output +} + +// IntersectsCellID reports whether this CellUnion intersects the given cell ID. +func (cu *CellUnion) IntersectsCellID(id CellID) bool { + // Find index of array item that occurs directly after our probe cell: + i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] }) + + if i != len(*cu) && (*cu)[i].RangeMin() <= id.RangeMax() { + return true + } + return i != 0 && (*cu)[i-1].RangeMax() >= id.RangeMin() +} + +// ContainsCellID reports whether the CellUnion contains the given cell ID. +// Containment is defined with respect to regions, e.g. a cell contains its 4 children. +// +// CAVEAT: If you have constructed a non-normalized CellUnion, note that groups +// of 4 child cells are *not* considered to contain their parent cell. To get +// this behavior you must use one of the call Normalize() explicitly. +func (cu *CellUnion) ContainsCellID(id CellID) bool { + // Find index of array item that occurs directly after our probe cell: + i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] }) + + if i != len(*cu) && (*cu)[i].RangeMin() <= id { + return true + } + return i != 0 && (*cu)[i-1].RangeMax() >= id +} + +// Denormalize replaces this CellUnion with an expanded version of the +// CellUnion where any cell whose level is less than minLevel or where +// (level - minLevel) is not a multiple of levelMod is replaced by its +// children, until either both of these conditions are satisfied or the +// maximum level is reached. +func (cu *CellUnion) Denormalize(minLevel, levelMod int) { + var denorm CellUnion + for _, id := range *cu { + level := id.Level() + newLevel := level + if newLevel < minLevel { + newLevel = minLevel + } + if levelMod > 1 { + newLevel += (maxLevel - (newLevel - minLevel)) % levelMod + if newLevel > maxLevel { + newLevel = maxLevel + } + } + if newLevel == level { + denorm = append(denorm, id) + } else { + end := id.ChildEndAtLevel(newLevel) + for ci := id.ChildBeginAtLevel(newLevel); ci != end; ci = ci.Next() { + denorm = append(denorm, ci) + } + } + } + *cu = denorm +} + +// RectBound returns a Rect that bounds this entity. +func (cu *CellUnion) RectBound() Rect { + bound := EmptyRect() + for _, c := range *cu { + bound = bound.Union(CellFromCellID(c).RectBound()) + } + return bound +} + +// CapBound returns a Cap that bounds this entity. +func (cu *CellUnion) CapBound() Cap { + if len(*cu) == 0 { + return EmptyCap() + } + + // Compute the approximate centroid of the region. This won't produce the + // bounding cap of minimal area, but it should be close enough. + var centroid Point + + for _, ci := range *cu { + area := AvgAreaMetric.Value(ci.Level()) + centroid = Point{centroid.Add(ci.Point().Mul(area))} + } + + if zero := (Point{}); centroid == zero { + centroid = PointFromCoords(1, 0, 0) + } else { + centroid = Point{centroid.Normalize()} + } + + // Use the centroid as the cap axis, and expand the cap angle so that it + // contains the bounding caps of all the individual cells. Note that it is + // *not* sufficient to just bound all the cell vertices because the bounding + // cap may be concave (i.e. cover more than one hemisphere). + c := CapFromPoint(centroid) + for _, ci := range *cu { + c = c.AddCap(CellFromCellID(ci).CapBound()) + } + + return c +} + +// ContainsCell reports whether this cell union contains the given cell. +func (cu *CellUnion) ContainsCell(c Cell) bool { + return cu.ContainsCellID(c.id) +} + +// IntersectsCell reports whether this cell union intersects the given cell. +func (cu *CellUnion) IntersectsCell(c Cell) bool { + return cu.IntersectsCellID(c.id) +} + +// ContainsPoint reports whether this cell union contains the given point. +func (cu *CellUnion) ContainsPoint(p Point) bool { + return cu.ContainsCell(CellFromPoint(p)) +} + +// CellUnionBound computes a covering of the CellUnion. +func (cu *CellUnion) CellUnionBound() []CellID { + return cu.CapBound().CellUnionBound() +} + +// LeafCellsCovered reports the number of leaf cells covered by this cell union. +// This will be no more than 6*2^60 for the whole sphere. +func (cu *CellUnion) LeafCellsCovered() int64 { + var numLeaves int64 + for _, c := range *cu { + numLeaves += 1 << uint64((maxLevel-int64(c.Level()))<<1) + } + return numLeaves +} + +// Returns true if the given four cells have a common parent. +// This requires that the four CellIDs are distinct. +func areSiblings(a, b, c, d CellID) bool { + // A necessary (but not sufficient) condition is that the XOR of the + // four cell IDs must be zero. This is also very fast to test. + if (a ^ b ^ c) != d { + return false + } + + // Now we do a slightly more expensive but exact test. First, compute a + // mask that blocks out the two bits that encode the child position of + // "id" with respect to its parent, then check that the other three + // children all agree with "mask". + mask := d.lsb() << 1 + mask = ^(mask + (mask << 1)) + idMasked := (uint64(d) & mask) + return ((uint64(a)&mask) == idMasked && + (uint64(b)&mask) == idMasked && + (uint64(c)&mask) == idMasked && + !d.isFace()) +} + +// Contains reports whether this CellUnion contains all of the CellIDs of the given CellUnion. +func (cu *CellUnion) Contains(o CellUnion) bool { + // TODO(roberts): Investigate alternatives such as divide-and-conquer + // or alternating-skip-search that may be significantly faster in both + // the average and worst case. This applies to Intersects as well. + for _, id := range o { + if !cu.ContainsCellID(id) { + return false + } + } + + return true +} + +// Intersects reports whether this CellUnion intersects any of the CellIDs of the given CellUnion. +func (cu *CellUnion) Intersects(o CellUnion) bool { + for _, c := range *cu { + if o.IntersectsCellID(c) { + return true + } + } + + return false +} + +// lowerBound returns the index in this CellUnion to the first element whose value +// is not considered to go before the given cell id. (i.e., either it is equivalent +// or comes after the given id.) If there is no match, then end is returned. +func (cu *CellUnion) lowerBound(begin, end int, id CellID) int { + for i := begin; i < end; i++ { + if (*cu)[i] >= id { + return i + } + } + + return end +} + +// cellUnionDifferenceInternal adds the difference between the CellID and the union to +// the result CellUnion. If they intersect but the difference is non-empty, it divides +// and conquers. +func (cu *CellUnion) cellUnionDifferenceInternal(id CellID, other *CellUnion) { + if !other.IntersectsCellID(id) { + (*cu) = append((*cu), id) + return + } + + if !other.ContainsCellID(id) { + for _, child := range id.Children() { + cu.cellUnionDifferenceInternal(child, other) + } + } +} + +// ExpandAtLevel expands this CellUnion by adding a rim of cells at expandLevel +// around the unions boundary. +// +// For each cell c in the union, we add all cells at level +// expandLevel that abut c. There are typically eight of those +// (four edge-abutting and four sharing a vertex). However, if c is +// finer than expandLevel, we add all cells abutting +// c.Parent(expandLevel) as well as c.Parent(expandLevel) itself, +// as an expandLevel cell rarely abuts a smaller cell. +// +// Note that the size of the output is exponential in +// expandLevel. For example, if expandLevel == 20 and the input +// has a cell at level 10, there will be on the order of 4000 +// adjacent cells in the output. For most applications the +// ExpandByRadius method below is easier to use. +func (cu *CellUnion) ExpandAtLevel(level int) { + var output CellUnion + levelLsb := lsbForLevel(level) + for i := len(*cu) - 1; i >= 0; i-- { + id := (*cu)[i] + if id.lsb() < levelLsb { + id = id.Parent(level) + // Optimization: skip over any cells contained by this one. This is + // especially important when very small regions are being expanded. + for i > 0 && id.Contains((*cu)[i-1]) { + i-- + } + } + output = append(output, id) + output = append(output, id.AllNeighbors(level)...) + } + sortCellIDs(output) + + *cu = output + cu.Normalize() +} + +// ExpandByRadius expands this CellUnion such that it contains all points whose +// distance to the CellUnion is at most minRadius, but do not use cells that +// are more than maxLevelDiff levels higher than the largest cell in the input. +// The second parameter controls the tradeoff between accuracy and output size +// when a large region is being expanded by a small amount (e.g. expanding Canada +// by 1km). For example, if maxLevelDiff == 4 the region will always be expanded +// by approximately 1/16 the width of its largest cell. Note that in the worst case, +// the number of cells in the output can be up to 4 * (1 + 2 ** maxLevelDiff) times +// larger than the number of cells in the input. +func (cu *CellUnion) ExpandByRadius(minRadius s1.Angle, maxLevelDiff int) { + minLevel := maxLevel + for _, cid := range *cu { + minLevel = minInt(minLevel, cid.Level()) + } + + // Find the maximum level such that all cells are at least "minRadius" wide. + radiusLevel := MinWidthMetric.MaxLevel(minRadius.Radians()) + if radiusLevel == 0 && minRadius.Radians() > MinWidthMetric.Value(0) { + // The requested expansion is greater than the width of a face cell. + // The easiest way to handle this is to expand twice. + cu.ExpandAtLevel(0) + } + cu.ExpandAtLevel(minInt(minLevel+maxLevelDiff, radiusLevel)) +} + +// Equal reports whether the two CellUnions are equal. +func (cu CellUnion) Equal(o CellUnion) bool { + if len(cu) != len(o) { + return false + } + for i := 0; i < len(cu); i++ { + if cu[i] != o[i] { + return false + } + } + return true +} + +// AverageArea returns the average area of this CellUnion. +// This is accurate to within a factor of 1.7. +func (cu *CellUnion) AverageArea() float64 { + return AvgAreaMetric.Value(maxLevel) * float64(cu.LeafCellsCovered()) +} + +// ApproxArea returns the approximate area of this CellUnion. This method is accurate +// to within 3% percent for all cell sizes and accurate to within 0.1% for cells +// at level 5 or higher within the union. +func (cu *CellUnion) ApproxArea() float64 { + var area float64 + for _, id := range *cu { + area += CellFromCellID(id).ApproxArea() + } + return area +} + +// ExactArea returns the area of this CellUnion as accurately as possible. +func (cu *CellUnion) ExactArea() float64 { + var area float64 + for _, id := range *cu { + area += CellFromCellID(id).ExactArea() + } + return area +} + +// Encode encodes the CellUnion. +func (cu *CellUnion) Encode(w io.Writer) error { + e := &encoder{w: w} + cu.encode(e) + return e.err +} + +func (cu *CellUnion) encode(e *encoder) { + e.writeInt8(encodingVersion) + e.writeInt64(int64(len(*cu))) + for _, ci := range *cu { + ci.encode(e) + } +} + +// Decode decodes the CellUnion. +func (cu *CellUnion) Decode(r io.Reader) error { + d := &decoder{r: asByteReader(r)} + cu.decode(d) + return d.err +} + +func (cu *CellUnion) decode(d *decoder) { + version := d.readInt8() + if d.err != nil { + return + } + if version != encodingVersion { + d.err = fmt.Errorf("only version %d is supported", encodingVersion) + return + } + n := d.readInt64() + if d.err != nil { + return + } + const maxCells = 1000000 + if n > maxCells { + d.err = fmt.Errorf("too many cells (%d; max is %d)", n, maxCells) + return + } + *cu = make([]CellID, n) + for i := range *cu { + (*cu)[i].decode(d) + } +} |