Search code examples
cbinarybit-manipulationbit

How to de-interleave bits (un-Mortonize) the "Obvious way" ? (In 3D)


I know a similar question has already been asked. However, I have some trouble manipulating bits and can't adapt/understand the solution.

First of all I used the Interleave bits the obvious way to get myself a Morton number. I adapted it to 3D like this:

unsigned long xyz2w(unsigned int x, unsigned int y, unsigned int z)
{
    unsigned long w=0;

    for (int i = 0; i < sizeof(x) * CHAR_BIT; i++)
    {
        w |= (x & 1U << i) << i | (y & 1U << i) << (i + 1) | (z & 1U << i) << (i + 2);
    }

    return w;
}

(It seems to work fine but if you ever find an error, please let me know)

Now I would like to do the opposite operation. That is: extracting the 3 coordinates based on the Morton number.

Obviously, I tried to use the adapted-for-3D part of the previous SO topic, but I didn't understand how it works. That's why I am creating a new topic.

I am not looking for a high-performance code but for a clear and understandable code that works similar to the xyz2w function (ie "The obvious way").


Solution

    1. Your code invokes undefined behaviour at << (i + 1)

    Example implementation:

    It is not efficient but I split long lines to be more easy to understand

    #define MASK(x) (1ULL << (sizeof(x) * CHAR_BIT - 1))
    
    unsigned long long xyz2w(unsigned short x, unsigned short y, unsigned short z)
    {
        unsigned long long w = 0;
        for(unsigned short mask = MASK(x); mask; mask >>= 1)
        {
            w <<= 1;
            w += (!!(x & mask)); 
            w <<= 1;
            w += (!!(y & mask)); 
            w <<= 1;
            w += (!!(z & mask)); 
        }
        return w;
    }
    void w2xyz(unsigned long long w, unsigned short *x, unsigned short *y, unsigned short *z)
    {
        *x = 0; *y = 0; *z = 0; 
        for(unsigned short bit = 0; bit < sizeof(*x) * CHAR_BIT; bit++)
        {
            *z += (w & 1) << bit;
            w >>= 1;
            *y += (w & 1) << bit;
            w >>= 1;
            *x += (w & 1) << bit;
            w >>= 1;
        }
    }
    
    int main(void)
    {
        unsigned short x = 0xaabb;
        unsigned short y = 0x3355;
        unsigned short z = 0x22ff;
    
        unsigned long long w = xyz2w(x,y,z);
        printf("w = 0x%llx, x = %hx, y = %hx, z = %hx\n", w, x, y, z);
        x = 0; y = 0; z = 0;
        w2xyz(w, &x, &y, &z);
        printf("x = %hx, y = %hx, z = %hx\n", x, y, z);
    }
    

    https://godbolt.org/z/WPrKosG5q