Files
astro/moon/svg/occultation_band_cache.go
T
b612 2bf8478639 feat: 完善日月食与月掩几何链路并扩展历法接口
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑
- 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口
- 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界
- 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名
- 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验
- 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口
- 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
2026-09-17 12:27:40 +08:00

121 lines
3.6 KiB
Go

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}
}