// Copyright 2014 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 ( "encoding/binary" "fmt" "strconv" ) const ( numBitsPerComponent = 12 modulo = 1 << numBitsPerComponent maxPositive = modulo / 2 minValue = -1 << (numBitsPerComponent - 1) maxValue = 1<<(numBitsPerComponent-1) - 1 ) type ( Coord struct { X, Y, Z int16 } Cuboid struct { P1, P2 Coord } KeyTransformer func(int64) int64 KeyEncoder func(int64) ([]byte, error) KeyDecoder func([]byte) (int64, error) KeyTranscoder func([]byte) ([]byte, error) KeySplitter func(int64) Coord KeyJoiner func(Coord) int64 ) func (cub Cuboid) Contains(c Coord) bool { return c.X >= cub.P1.X && c.X <= cub.P2.X && c.Y >= cub.P1.Y && c.Y <= cub.P2.Y && c.Z >= cub.P1.Z && c.Z <= cub.P2.Z } func (c Coord) String() string { return fmt.Sprintf("(%d, %d, %d)", c.X, c.Y, c.Z) } func minComponent(a, b int16) int16 { if a < b { return a } return b } func maxComponent(a, b int16) int16 { if a > b { return a } return b } func clipComponent(x int16) int16 { if x < minValue { return minValue } if x > maxValue { return maxValue } return x } func ClipCoord(c Coord) Coord { return Coord{ X: clipComponent(c.X), Y: clipComponent(c.Y), Z: clipComponent(c.Z)} } func MinCoord(a, b Coord) Coord { return Coord{ X: minComponent(a.X, b.X), Y: minComponent(a.Y, b.Y), Z: minComponent(a.Z, b.Z)} } func MaxCoord(a, b Coord) Coord { return Coord{ X: maxComponent(a.X, b.X), Y: maxComponent(a.Y, b.Y), Z: maxComponent(a.Z, b.Z)} } // Constructs a database key out of byte slice. func DecodeStringFromBytes(key []byte) (pos int64, err error) { return strconv.ParseInt(string(key), 10, 64) } func StringToBytes(key int64) []byte { return []byte(strconv.FormatInt(key, 10)) } // Encode a block pos to byte slice. func EncodeStringToBytes(key int64) ([]byte, error) { return StringToBytes(key), nil } func ToBigEndian(key int64) []byte { enc := make([]byte, 8) binary.BigEndian.PutUint64(enc, uint64(key)) return enc } func EncodeToBigEndian(key int64) ([]byte, error) { return ToBigEndian(key), nil } func FromBigEndian(key []byte) int64 { return int64(binary.BigEndian.Uint64(key)) } func DecodeFromBigEndian(key []byte) (int64, error) { return FromBigEndian(key), nil } func CoordToInterleaved(c Coord) (result int64) { const end = 1 << (numBitsPerComponent + 1) x := c.X - minValue y := c.Y - minValue z := c.Z - minValue setmask := int64(1) for mask := int16(1); mask != end; mask <<= 1 { if x&mask != 0 { result |= setmask } setmask <<= 1 if y&mask != 0 { result |= setmask } setmask <<= 1 if z&mask != 0 { result |= setmask } setmask <<= 1 } return } func invert16(x int16) int16 { return -x - 1 } func InterleavedToCoord(pos int64) Coord { const end = 1 << (numBitsPerComponent + 1) var x, y, z int16 for mask := int16(1); mask != end; mask <<= 1 { if pos&1 == 1 { x |= mask } pos >>= 1 if pos&1 == 1 { y |= mask } pos >>= 1 if pos&1 == 1 { z |= mask } pos >>= 1 } return Coord{X: x + minValue, Y: y + minValue, Z: z + minValue} } func CoordToPlain(c Coord) int64 { return int64(c.Z)<<(2*numBitsPerComponent) + int64(c.Y)<= 0 { return i & mask } return modulo - -i&mask } func PlainToCoord(i int64) (c Coord) { c.X = unsignedToSigned(pythonModulo(int16(i))) i = (i - int64(c.X)) >> numBitsPerComponent c.Y = unsignedToSigned(pythonModulo(int16(i))) i = (i - int64(c.Y)) >> numBitsPerComponent c.Z = unsignedToSigned(pythonModulo(int16(i))) return } func TransformPlainToInterleaved(pos int64) int64 { return CoordToInterleaved(PlainToCoord(pos)) } func TransformInterleavedToPlain(pos int64) int64 { return CoordToPlain(InterleavedToCoord(pos)) } func DecodeStringFromBytesToInterleaved(key []byte) (v int64, err error) { if v, err = DecodeStringFromBytes(key); err != nil { return } v = TransformPlainToInterleaved(v) return } func EncodeStringToBytesFromInterleaved(key int64) ([]byte, error) { return EncodeStringToBytes(TransformInterleavedToPlain(key)) } func IdentityTranscoder(key []byte) ([]byte, error) { return key, nil } func TranscodePlainToInterleaved(key []byte) ([]byte, error) { if pos, err := DecodeStringFromBytesToInterleaved(key); err != nil { return nil, err } else { return EncodeToBigEndian(pos) } } func TranscodeInterleavedToPlain(key []byte) ([]byte, error) { if pos, err := DecodeFromBigEndian(key); err != nil { return nil, err } else { return EncodeStringToBytes(TransformInterleavedToPlain(pos)) } } // For correctness checks only. func NaiveBigMin(minz, maxz, zcode int64) int64 { var ( c1 = InterleavedToCoord(minz) c2 = InterleavedToCoord(maxz) cand = maxz c Coord ) for c.X = c1.X; c.X <= c2.X; c.X++ { for c.Y = c1.Y; c.Y <= c2.Y; c.Y++ { for c.Z = c1.Z; c.Z <= c2.Z; c.Z++ { if z := CoordToInterleaved(c); z > zcode && z < cand { cand = z } } } } return cand } const ( msb = uint8(3*numBitsPerComponent - 1) mask = int64(0x924924924) full = int64(0xfffffffff) ) func setbits(p uint8, v int64) int64 { m := (mask >> (msb - p)) & (^(full << p) & full) return (v | m) & ^(1 << p) & full } func unsetbits(p uint8, v int64) int64 { m := ^(mask >> (msb - p)) & full return (v & m) | (int64(1) << p) } func BigMin(minz, maxz, zcode int64) int64 { const ( _001_ = 1 _010_ = 2 _011_ = 2 | 1 _100_ = 4 _101_ = 4 | 1 ) bigmin := maxz pos := msb for m := int64(1) << msb; m != 0; m >>= 1 { var v uint8 if zcode&m == m { v = _100_ } if minz&m == m { v |= _010_ } if maxz&m == m { v |= _001_ } switch v { case _001_: bigmin = unsetbits(pos, minz) maxz = setbits(pos, maxz) case _011_: return minz case _100_: return bigmin case _101_: minz = unsetbits(pos, minz) } pos-- } return bigmin }