// Copyright 2014, 2015, 2017 by Sascha L. Teichmann // Use of this source code is governed by the MIT license // that can be found in the LICENSE file. package common import "math" type Area struct { X1, Z1 int16 X2, Z2 int16 } func (a Area) contains(x, z int16) bool { return x >= a.X1 && x <= a.X2 && z >= a.Z1 && z <= a.Z2 } func (a Area) higher() bool { return a.Z2-a.Z1 > a.X2-a.X1 } func areasContain(areas []Area, x, z int16) bool { for _, r := range areas { if r.contains(x, z) { return true } } return false } // UncoveredAreas implements a greedy algorithm to figure out // a list of disjunct areas of free regions in the domain // to the (x, z) block plane. // oldAreas are searched and found free areas are appended // to newAreas which ist return. // This is useful to spatial query only blocks from db // that are not below already rendered blocks. func UncoveredAreas(r *Renderer, newAreas, oldAreas []Area) []Area { yM := r.yMin // Scan old areas. for _, oldArea := range oldAreas { for z := oldArea.Z1; z <= oldArea.Z2; z++ { row := z * int16(r.width) for x := oldArea.X1; x <= oldArea.X2; x++ { // Uncovered and not in list of new areas? if yM[row+x] > math.MinInt32 || areasContain(newAreas, x, z) { continue } area := Area{X1: x, Z1: z, X2: x, Z2: z} // Try to extend the area in x and/or z till no further extension is possible. for extendDirs := 1 | 2; extendDirs != 0; { var xFirst bool // Try to extend in the direction with most gain // of blocks. if area.higher() { // Higher means to win more blocks in x direction. xFirst = true } dirs: for i := 0; i < 2; i++ { if xFirst { // Extension in x possible? if extendDirs&1 == 1 { nx := area.X2 + 1 if nx >= int16(r.width) { extendDirs &= ^1 continue } // Scan line below current area if its fully free. for nz := area.Z1; nz <= area.Z2; nz++ { if yM[nz*int16(r.width)+nx] > math.MinInt32 || areasContain(newAreas, nx, nz) { extendDirs &= ^1 continue dirs } } // free -> extend area.X2 = nx } } else if extendDirs&2 == 2 { // Symmetric case in z direction nz := area.Z2 + 1 if nz >= int16(r.height) { extendDirs &= ^2 continue } // Scan line right beside the area if its free. row2 := nz * int16(r.width) for nx := area.X1; nx <= area.X2; nx++ { if yM[row2+nx] > math.MinInt32 || areasContain(newAreas, nx, nz) { extendDirs &= ^2 continue dirs } } area.Z2 = nz } // Switch to other search direction (x -> z or z -> x) xFirst = !xFirst } } // At this point the area is extended to max. newAreas = append(newAreas, area) } } } return newAreas }