package fog // Diff 是一份待导入快照与当前迷雾之间的差异画像。 // // 导入有两种语义完全不同的模式:叠加只做按位或、不可能丢数据;整体替换会先清空, // 快照里没有的块一律消失。以前两种模式在界面上只是一个下拉框,站长在下手之前 // 看不到「这次能补多少」和「这次会丢多少」,实测里整体替换静默丢掉过 14% 的已探索区域。 type Diff struct { CurBlocks, CurCells int // 库里现有 IncBlocks, IncCells int // 快照本身 // GainBlocks/GainCells 是叠加模式真正新增的部分:快照里当前完全没有的整块, // 加上重叠块里补亮的格。 GainBlocks int GainCells int // OverlapBlocks 是与现有块坐标相同的块数,其中 SameBlocks 是一格都不会变的。 OverlapBlocks int SameBlocks int // LostBlocks/LostCells 是快照里完全没有、只有整体替换才会消失的部分。 LostBlocks int LostCells int curAreaM2, incAreaM2, gainAreaM2, lostAreaM2 float64 } // DiffBlocks 比对当前块集与一份待导入快照。入参块集不要求有序。 func DiffBlocks(cur, inc []Block) Diff { var d Diff d.CurBlocks, d.IncBlocks = len(cur), len(inc) curMap := make(map[[2]int][]byte, len(cur)) for _, b := range cur { curMap[[2]int{b.GX, b.GY}] = b.Bitmap n := popcountSum(b.Bitmap) d.CurCells += n d.curAreaM2 += float64(n) * cellAreaM2(b.GX, b.GY) } incSet := make(map[[2]int]struct{}, len(inc)) for _, b := range inc { incSet[[2]int{b.GX, b.GY}] = struct{}{} n := popcountSum(b.Bitmap) d.IncCells += n d.incAreaM2 += float64(n) * cellAreaM2(b.GX, b.GY) old, ok := curMap[[2]int{b.GX, b.GY}] if !ok { d.GainBlocks++ d.GainCells += n d.gainAreaM2 += float64(n) * cellAreaM2(b.GX, b.GY) continue } d.OverlapBlocks++ gain := 0 for i := range b.Bitmap { gain += int(popcount[b.Bitmap[i]&^old[i]]) } if gain == 0 { d.SameBlocks++ continue } d.GainCells += gain d.gainAreaM2 += float64(gain) * cellAreaM2(b.GX, b.GY) } for _, b := range cur { if _, ok := incSet[[2]int{b.GX, b.GY}]; ok { continue } n := popcountSum(b.Bitmap) d.LostBlocks++ d.LostCells += n d.lostAreaM2 += float64(n) * cellAreaM2(b.GX, b.GY) } return d } func (d Diff) CurAreaKm2() float64 { return d.curAreaM2 / 1e6 } func (d Diff) IncAreaKm2() float64 { return d.incAreaM2 / 1e6 } func (d Diff) GainAreaKm2() float64 { return d.gainAreaM2 / 1e6 } func (d Diff) LostAreaKm2() float64 { return d.lostAreaM2 / 1e6 } // MergeAreaKm2 是叠加后的总量,CoverAreaKm2 是整体替换后的总量。 func (d Diff) MergeAreaKm2() float64 { return (d.curAreaM2 + d.gainAreaM2) / 1e6 } func (d Diff) CoverAreaKm2() float64 { return d.incAreaM2 / 1e6 } // Changed 报告叠加是否会让地图有任何变化——为假说明这份快照已经被完全覆盖过。 func (d Diff) Changed() bool { return d.GainCells > 0 }