MTSatellite_old/common/coords.go

312 lines
6.2 KiB
Go

// 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)<<numBitsPerComponent +
int64(c.X)
}
func unsignedToSigned(i int16) int16 {
if i < maxPositive {
return i
}
return i - maxPositive*2
}
// To match C++ code.
func pythonModulo(i int16) int16 {
const mask = modulo - 1
if i >= 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
}