2014-08-04 17:10:19 +02:00
|
|
|
// 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.
|
|
|
|
|
2014-08-16 16:06:42 +02:00
|
|
|
package common
|
2014-08-04 17:10:19 +02:00
|
|
|
|
2014-08-16 17:41:54 +02:00
|
|
|
import (
|
2014-08-18 16:29:17 +02:00
|
|
|
"encoding/binary"
|
2014-08-18 21:33:58 +02:00
|
|
|
"fmt"
|
2014-08-16 17:41:54 +02:00
|
|
|
"strconv"
|
|
|
|
)
|
2014-08-04 17:10:19 +02:00
|
|
|
|
2014-08-18 18:01:34 +02:00
|
|
|
const (
|
|
|
|
numBitsPerComponent = 12
|
2014-08-18 19:58:10 +02:00
|
|
|
modulo = 1 << numBitsPerComponent
|
|
|
|
maxPositive = modulo / 2
|
2014-09-07 10:46:51 +02:00
|
|
|
minValue = -1 << (numBitsPerComponent - 1)
|
|
|
|
maxValue = 1<<(numBitsPerComponent-1) - 1
|
2014-08-18 18:01:34 +02:00
|
|
|
)
|
|
|
|
|
2014-08-18 16:29:17 +02:00
|
|
|
type (
|
2014-08-18 21:33:58 +02:00
|
|
|
Coord struct {
|
|
|
|
X, Y, Z int16
|
|
|
|
}
|
|
|
|
|
2014-09-01 13:46:23 +02:00
|
|
|
Cuboid struct {
|
|
|
|
P1, P2 Coord
|
|
|
|
}
|
|
|
|
|
2014-08-18 16:29:17 +02:00
|
|
|
KeyTransformer func(int64) int64
|
2014-08-18 18:01:34 +02:00
|
|
|
KeyEncoder func(int64) ([]byte, error)
|
|
|
|
KeyDecoder func([]byte) (int64, error)
|
2014-08-22 22:26:03 +02:00
|
|
|
KeyTranscoder func([]byte) ([]byte, error)
|
2014-08-18 21:33:58 +02:00
|
|
|
KeySplitter func(int64) Coord
|
|
|
|
KeyJoiner func(Coord) int64
|
2014-08-18 16:29:17 +02:00
|
|
|
)
|
|
|
|
|
2014-09-01 13:46:23 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func (c Coord) String() string {
|
|
|
|
return fmt.Sprintf("(%d, %d, %d)", c.X, c.Y, c.Z)
|
|
|
|
}
|
|
|
|
|
2014-09-01 12:42:57 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-09-07 21:36:59 +02:00
|
|
|
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)}
|
|
|
|
}
|
|
|
|
|
2014-09-01 12:42:57 +02:00
|
|
|
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)}
|
|
|
|
}
|
|
|
|
|
2014-08-04 17:10:19 +02:00
|
|
|
// Constructs a database key out of byte slice.
|
2014-08-18 18:01:34 +02:00
|
|
|
func DecodeStringFromBytes(key []byte) (pos int64, err error) {
|
2014-08-04 17:10:19 +02:00
|
|
|
return strconv.ParseInt(string(key), 10, 64)
|
|
|
|
}
|
2014-08-16 17:41:54 +02:00
|
|
|
|
2014-09-01 18:39:42 +02:00
|
|
|
func StringToBytes(key int64) []byte {
|
|
|
|
return []byte(strconv.FormatInt(key, 10))
|
|
|
|
}
|
|
|
|
|
2014-08-16 17:41:54 +02:00
|
|
|
// Encode a block pos to byte slice.
|
2014-08-18 18:01:34 +02:00
|
|
|
func EncodeStringToBytes(key int64) ([]byte, error) {
|
2014-09-01 18:39:42 +02:00
|
|
|
return StringToBytes(key), nil
|
2014-08-16 17:41:54 +02:00
|
|
|
}
|
2014-08-18 16:29:17 +02:00
|
|
|
|
2014-09-01 18:26:33 +02:00
|
|
|
func ToBigEndian(key int64) []byte {
|
|
|
|
enc := make([]byte, 8)
|
2014-08-18 16:29:17 +02:00
|
|
|
binary.BigEndian.PutUint64(enc, uint64(key))
|
2014-09-01 18:26:33 +02:00
|
|
|
return enc
|
|
|
|
}
|
|
|
|
|
|
|
|
func EncodeToBigEndian(key int64) ([]byte, error) {
|
|
|
|
return ToBigEndian(key), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func FromBigEndian(key []byte) int64 {
|
|
|
|
return int64(binary.BigEndian.Uint64(key))
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 18:01:34 +02:00
|
|
|
func DecodeFromBigEndian(key []byte) (int64, error) {
|
2014-09-01 18:26:33 +02:00
|
|
|
return FromBigEndian(key), nil
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func CoordToInterleaved(c Coord) (result int64) {
|
2014-08-18 16:29:17 +02:00
|
|
|
const end = 1 << (numBitsPerComponent + 1)
|
2014-09-07 11:07:49 +02:00
|
|
|
x := c.X - minValue
|
|
|
|
y := c.Y - minValue
|
|
|
|
z := c.Z - minValue
|
2014-08-18 16:29:17 +02:00
|
|
|
setmask := int64(1)
|
2014-09-07 11:07:49 +02:00
|
|
|
for mask := int16(1); mask != end; mask <<= 1 {
|
2014-09-07 09:58:09 +02:00
|
|
|
if x&mask != 0 {
|
2014-08-18 16:29:17 +02:00
|
|
|
result |= setmask
|
|
|
|
}
|
|
|
|
setmask <<= 1
|
2014-09-07 09:58:09 +02:00
|
|
|
if y&mask != 0 {
|
2014-08-18 16:29:17 +02:00
|
|
|
result |= setmask
|
|
|
|
}
|
|
|
|
setmask <<= 1
|
2014-09-07 09:58:09 +02:00
|
|
|
if z&mask != 0 {
|
2014-08-18 16:29:17 +02:00
|
|
|
result |= setmask
|
|
|
|
}
|
|
|
|
setmask <<= 1
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func InterleavedToCoord(pos int64) Coord {
|
2014-08-18 16:29:17 +02:00
|
|
|
const end = 1 << (numBitsPerComponent + 1)
|
2014-08-18 21:33:58 +02:00
|
|
|
var x, y, z int16
|
2014-08-18 16:29:17 +02:00
|
|
|
for mask := int16(1); mask != end; mask <<= 1 {
|
2014-08-18 21:33:58 +02:00
|
|
|
if pos&1 == 1 {
|
2014-08-18 16:29:17 +02:00
|
|
|
x |= mask
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
pos >>= 1
|
|
|
|
if pos&1 == 1 {
|
2014-08-18 16:29:17 +02:00
|
|
|
y |= mask
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
pos >>= 1
|
|
|
|
if pos&1 == 1 {
|
2014-08-18 16:29:17 +02:00
|
|
|
z |= mask
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
pos >>= 1
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
2014-09-07 11:15:14 +02:00
|
|
|
return Coord{X: x + minValue, Y: y + minValue, Z: z + minValue}
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func CoordToPlain(c Coord) int64 {
|
|
|
|
return int64(c.Z)<<(2*numBitsPerComponent) +
|
|
|
|
int64(c.Y)<<numBitsPerComponent +
|
|
|
|
int64(c.X)
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func unsignedToSigned(i int16) int16 {
|
|
|
|
if i < maxPositive {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
return i - maxPositive*2
|
|
|
|
}
|
|
|
|
|
|
|
|
// To match C++ code.
|
|
|
|
func pythonModulo(i int16) int16 {
|
2014-08-18 19:58:10 +02:00
|
|
|
const mask = modulo - 1
|
2014-08-18 16:29:17 +02:00
|
|
|
if i >= 0 {
|
2014-08-18 19:58:10 +02:00
|
|
|
return i & mask
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
2014-08-18 19:58:10 +02:00
|
|
|
return modulo - -i&mask
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
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)))
|
2014-08-18 16:29:17 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func TransformPlainToInterleaved(pos int64) int64 {
|
2014-08-18 21:33:58 +02:00
|
|
|
return CoordToInterleaved(PlainToCoord(pos))
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 22:19:42 +02:00
|
|
|
func TransformInterleavedToPlain(pos int64) int64 {
|
2014-08-18 21:33:58 +02:00
|
|
|
return CoordToPlain(InterleavedToCoord(pos))
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
2014-08-21 14:46:34 +02:00
|
|
|
|
2014-08-21 22:19:42 +02:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2014-08-22 22:26:03 +02:00
|
|
|
func IdentityTranscoder(key []byte) ([]byte, error) {
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
2014-08-23 13:49:45 +02:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
2014-09-01 13:46:23 +02:00
|
|
|
|
|
|
|
// For correctness checks only.
|
|
|
|
func NaiveBigMin(minz, maxz, zcode int64) int64 {
|
2014-09-01 18:44:56 +02:00
|
|
|
var (
|
|
|
|
c1 = InterleavedToCoord(minz)
|
|
|
|
c2 = InterleavedToCoord(maxz)
|
2014-09-06 19:54:53 +02:00
|
|
|
cand = maxz
|
2014-09-01 18:44:56 +02:00
|
|
|
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 {
|
2014-09-01 13:46:23 +02:00
|
|
|
cand = z
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cand
|
|
|
|
}
|
2014-09-06 19:54:53 +02:00
|
|
|
|
|
|
|
const (
|
2014-09-07 09:58:09 +02:00
|
|
|
msb = uint8(3*numBitsPerComponent - 1)
|
2014-09-06 23:34:05 +02:00
|
|
|
mask = int64(0x924924924)
|
|
|
|
full = int64(0xfffffffff)
|
2014-09-06 19:54:53 +02:00
|
|
|
)
|
|
|
|
|
2014-09-06 20:38:49 +02:00
|
|
|
func setbits(p uint8, v int64) int64 {
|
2014-09-06 19:54:53 +02:00
|
|
|
m := (mask >> (msb - p)) & (^(full << p) & full)
|
|
|
|
return (v | m) & ^(1 << p) & full
|
|
|
|
}
|
|
|
|
|
2014-09-06 20:38:49 +02:00
|
|
|
func unsetbits(p uint8, v int64) int64 {
|
2014-09-06 19:54:53 +02:00
|
|
|
m := ^(mask >> (msb - p)) & full
|
|
|
|
return (v & m) | (int64(1) << p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func BigMin(minz, maxz, zcode int64) int64 {
|
2014-09-06 20:38:49 +02:00
|
|
|
const (
|
|
|
|
_001_ = 1
|
|
|
|
_010_ = 2
|
|
|
|
_011_ = 2 | 1
|
|
|
|
_100_ = 4
|
|
|
|
_101_ = 4 | 1
|
|
|
|
)
|
2014-09-06 19:54:53 +02:00
|
|
|
bigmin := maxz
|
|
|
|
pos := msb
|
|
|
|
for m := int64(1) << msb; m != 0; m >>= 1 {
|
2014-09-06 20:38:49 +02:00
|
|
|
var v uint8
|
2014-09-07 10:46:51 +02:00
|
|
|
if zcode&m == m {
|
2014-09-06 19:54:53 +02:00
|
|
|
v = _100_
|
|
|
|
}
|
2014-09-07 10:46:51 +02:00
|
|
|
if minz&m == m {
|
2014-09-06 19:54:53 +02:00
|
|
|
v |= _010_
|
|
|
|
}
|
2014-09-07 10:46:51 +02:00
|
|
|
if maxz&m == m {
|
2014-09-06 19:54:53 +02:00
|
|
|
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
|
|
|
|
}
|