Files
astro/moon/svg/occultation_band_cache.go
T

121 lines
3.6 KiB
Go
Raw Normal View History

package svg
import (
"math"
"sync"
"b612.me/astro/internal/geodata"
"b612.me/astro/moon"
)
// occultationBandPolygons 是一次紧凑掩带合并的只读结果。
type occultationBandPolygons struct {
polygons [][]geodata.GeoPoint
authoritative bool
}
type occultationBandCacheKey struct {
kind occultationFootprintSweepKind
digest [2]uint64
}
// occultationBandCacheCapacity 限制常驻的合并结果数量;掩带多边形可能上万点。
const occultationBandCacheCapacity = 6
var occultationBandCache = struct {
sync.RWMutex
entries map[occultationBandCacheKey]occultationBandPolygons
order []occultationBandCacheKey
}{entries: make(map[occultationBandCacheKey]occultationBandPolygons, occultationBandCacheCapacity)}
func lookupOccultationBandPolygons(key occultationBandCacheKey) (occultationBandPolygons, bool) {
occultationBandCache.RLock()
value, ok := occultationBandCache.entries[key]
occultationBandCache.RUnlock()
return value, ok
}
func storeOccultationBandPolygons(key occultationBandCacheKey, value occultationBandPolygons) {
occultationBandCache.Lock()
defer occultationBandCache.Unlock()
if _, exists := occultationBandCache.entries[key]; exists {
return
}
if len(occultationBandCache.order) >= occultationBandCacheCapacity {
delete(occultationBandCache.entries, occultationBandCache.order[0])
occultationBandCache.order = occultationBandCache.order[1:]
}
occultationBandCache.entries[key] = value
occultationBandCache.order = append(occultationBandCache.order, key)
}
// occultationBandDigest 汇总合并的全部输入;按 64 位字混合而不是逐字节,摘要成本必须远低于合并本身。
func occultationBandDigest(s occultationFootprintSweep) [2]uint64 {
const (
seed1 = 0x9e3779b97f4a7c15
seed2 = 0xc2b2ae3d27d4eb4f
prime = 0x100000001b3
)
first, second := uint64(seed1), uint64(seed2)
write := func(value uint64) {
first = (first ^ value) * prime
first ^= first >> 31
second = (second + value) * prime
second ^= second >> 33
}
writeFloat := func(value float64) { write(math.Float64bits(value)) }
writeTime := func(value int64) { write(uint64(value)) }
writePoint := func(point moon.OccultationPathPoint) {
writeTime(point.Time.UnixNano())
writeFloat(point.Longitude)
writeFloat(point.Latitude)
writeFloat(point.MoonAltitude)
writeFloat(point.WidthKM)
writeFloat(point.LimitSeparationKM)
}
writePolygons := func(polygons [][]moon.OccultationPathPoint) {
write(uint64(len(polygons)))
for _, polygon := range polygons {
write(uint64(len(polygon)))
for _, point := range polygon {
writePoint(point)
}
}
}
writeString := func(value string) {
write(uint64(len(value)))
for index := 0; index < len(value); index++ {
write(uint64(value[index]))
}
}
writeFootprints := func(footprints []moon.PlanetOccultationFootprint) {
write(uint64(len(footprints)))
for _, footprint := range footprints {
writeTime(footprint.Time.UnixNano())
writePolygons(footprint.Polygons)
writePolygons(footprint.InteriorPolygons)
writePolygons(footprint.Boundaries)
if footprint.Closed {
write(1)
} else {
write(0)
}
}
}
writeCurves := func(curves []moon.OccultationRiseSetCurve) {
write(uint64(len(curves)))
for _, curve := range curves {
writeString(string(curve.Phase))
writeString(string(curve.Direction))
writePolygons(curve.Segments)
}
}
write(uint64(s.kind))
writeFootprints(s.footprints)
writePolygons(s.contours)
writePolygons(s.visibilityContours)
writePolygons([][]moon.OccultationPathPoint{s.northern, s.southern})
writeCurves(s.curves)
return [2]uint64{first, second}
}