2015-07-26 16:44:51 +02:00
|
|
|
// Copyright 2014, 2015 by Sascha L. Teichmann
|
2014-08-04 17:10:19 +02:00
|
|
|
// 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-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{
|
2015-07-21 22:01:10 +02:00
|
|
|
X: min16(a.X, b.X),
|
|
|
|
Y: min16(a.Y, b.Y),
|
|
|
|
Z: min16(a.Z, b.Z)}
|
2014-09-01 12:42:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func MaxCoord(a, b Coord) Coord {
|
|
|
|
return Coord{
|
2015-07-21 22:01:10 +02:00
|
|
|
X: max16(a.X, b.X),
|
|
|
|
Y: max16(a.Y, b.Y),
|
|
|
|
Z: max16(a.Z, b.Z)}
|
2014-09-01 12:42:57 +02:00
|
|
|
}
|
|
|
|
|
2015-05-27 18:13:39 +02:00
|
|
|
// DecodeStringFromBytes 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))
|
|
|
|
}
|
|
|
|
|
2015-05-27 18:13:39 +02:00
|
|
|
// EncodeStringToBytes encodes 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
|
|
|
|
}
|
|
|
|
|
2014-09-17 09:51:34 +02:00
|
|
|
func DecodeStringBytesToCoord(key []byte) (coord Coord, err error) {
|
|
|
|
var k int64
|
|
|
|
if k, err = DecodeStringFromBytes(key); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
coord = PlainToCoord(k)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-21 22:19:42 +02:00
|
|
|
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) {
|
2015-05-27 18:13:39 +02:00
|
|
|
pos, err := DecodeStringFromBytesToInterleaved(key)
|
|
|
|
if err != nil {
|
2014-08-23 13:49:45 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-05-27 18:13:39 +02:00
|
|
|
return EncodeToBigEndian(pos)
|
2014-08-23 13:49:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func TranscodeInterleavedToPlain(key []byte) ([]byte, error) {
|
2015-05-27 18:13:39 +02:00
|
|
|
pos, err := DecodeFromBigEndian(key)
|
|
|
|
if err != nil {
|
2014-08-23 13:49:45 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-05-27 18:13:39 +02:00
|
|
|
return EncodeStringToBytes(TransformInterleavedToPlain(pos))
|
2014-08-23 13:49:45 +02:00
|
|
|
}
|
2014-09-01 13:46:23 +02:00
|
|
|
|
2015-05-27 18:13:39 +02:00
|
|
|
// NaiveBigMin is for correctness checks of BigMin only.
|
2014-09-01 13:46:23 +02:00
|
|
|
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 (
|
2016-04-24 12:41:58 +02:00
|
|
|
b001 = 1
|
|
|
|
b010 = 2
|
|
|
|
b011 = 2 | 1
|
|
|
|
b100 = 4
|
|
|
|
b101 = 4 | 1
|
2014-09-06 20:38:49 +02:00
|
|
|
)
|
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 {
|
2016-04-24 12:41:58 +02:00
|
|
|
v = b100
|
2014-09-06 19:54:53 +02:00
|
|
|
}
|
2014-09-07 10:46:51 +02:00
|
|
|
if minz&m == m {
|
2016-04-24 12:41:58 +02:00
|
|
|
v |= b010
|
2014-09-06 19:54:53 +02:00
|
|
|
}
|
2014-09-07 10:46:51 +02:00
|
|
|
if maxz&m == m {
|
2016-04-24 12:41:58 +02:00
|
|
|
v |= b001
|
2014-09-06 19:54:53 +02:00
|
|
|
}
|
|
|
|
switch v {
|
2016-04-24 12:41:58 +02:00
|
|
|
case b001:
|
2014-09-06 19:54:53 +02:00
|
|
|
bigmin = unsetbits(pos, minz)
|
|
|
|
maxz = setbits(pos, maxz)
|
2016-04-24 12:41:58 +02:00
|
|
|
case b011:
|
2014-09-06 19:54:53 +02:00
|
|
|
return minz
|
2016-04-24 12:41:58 +02:00
|
|
|
case b100:
|
2014-09-06 19:54:53 +02:00
|
|
|
return bigmin
|
2016-04-24 12:41:58 +02:00
|
|
|
case b101:
|
2014-09-06 19:54:53 +02:00
|
|
|
minz = unsetbits(pos, minz)
|
|
|
|
}
|
|
|
|
pos--
|
|
|
|
}
|
|
|
|
return bigmin
|
|
|
|
}
|