// Copyright 2014, 2015 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 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: min16(a.X, b.X), Y: min16(a.Y, b.Y), Z: min16(a.Z, b.Z)} } func MaxCoord(a, b Coord) Coord { return Coord{ X: max16(a.X, b.X), Y: max16(a.Y, b.Y), Z: max16(a.Z, b.Z)} } // DecodeStringFromBytes constructs a database key out of byte slice. func DecodeStringFromBytes(key []byte) (pos int64, err error) { return strconv.ParseInt(string(key), 10, 64) } func keyToBytes(key int64, buf []byte) []byte { return strconv.AppendInt(buf, key, 10) } func StringToBytes(key int64) []byte { return strconv.AppendInt(nil, key, 10) } // EncodeStringToBytes encodes 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 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 DecodeStringBytesToCoord(key []byte) (coord Coord, err error) { var k int64 if k, err = DecodeStringFromBytes(key); err != nil { return } coord = PlainToCoord(k) 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) { pos, err := DecodeStringFromBytesToInterleaved(key) if err != nil { return nil, err } return EncodeToBigEndian(pos) } func TranscodeInterleavedToPlain(key []byte) ([]byte, error) { pos, err := DecodeFromBigEndian(key) if err != nil { return nil, err } return EncodeStringToBytes(TransformInterleavedToPlain(pos)) } // NaiveBigMin is for correctness checks of BigMin 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 ( b001 = 1 b010 = 2 b011 = 2 | 1 b100 = 4 b101 = 4 | 1 ) bigmin := maxz pos := msb for m := int64(1) << msb; m != 0; m >>= 1 { var v uint8 if zcode&m == m { v = b100 } if minz&m == m { v |= b010 } if maxz&m == m { v |= b001 } switch v { case b001: bigmin = unsetbits(pos, minz) maxz = setbits(pos, maxz) case b011: return minz case b100: return bigmin case b101: minz = unsetbits(pos, minz) } pos-- } return bigmin }