bitmasking/Bitmasking.js

/**
 * Bitmasking utils
 * @module Bitmasking
 */

const map = [
    [0],
    [1, 4, 16, 64],
    [5, 20, 80, 65],
    [7, 28, 112, 193],
    [17, 68],
    [21, 84, 81, 69],
    [23, 92, 113, 197],
    [29, 116, 209, 71],
    [31, 124, 241, 199],
    [85],
    [87, 93, 117, 213],
    [95, 125, 245, 215],
    [119, 221],
    [127, 253, 247, 223],
    [255]
];

const front_map = [0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 3, 4, 5, 5];
const oriented_mask = { 2: 1, 8: 2, 10: 3, 11: 4, 16: 5, 18: 6, 22: 7, 24: 8, 26: 9, 27: 10, 30: 11, 31: 12, 64: 13, 66: 14, 72: 15, 74: 16, 75: 17, 80: 18, 82: 19, 86: 20, 88: 21, 90: 22, 91: 23, 94: 24, 95: 25, 104: 26, 106: 27, 107: 28, 120: 29, 122: 30, 123: 31, 126: 32, 127: 33, 208: 34, 210: 35, 214: 36, 216: 37, 218: 38, 219: 39, 222: 40, 223: 41, 248: 42, 250: 43, 251: 44, 254: 45, 255: 46, 0: 47 };
const noncorner_map = [
    [0],
    [1, 4, 8, 2],
    [3, 5, 12, 10],
    [6, 9],
    [7, 13, 14, 11],
    [15]
];

function getTileposAndRotation(map, m) {
    let rotation = 0;
    let tilepos = 0;
    for (const b in map) {
        let rots = map[b];
        let ri = rots.indexOf(m);
        if (ri !== -1) {
            tilepos = b * 1;
            rotation = ri * Math.PI * 0.5;
            break;
        }
    }

    return { m: tilepos, rot: rotation };
}

/**
 * Get a bitmask texture offset for tiles without corners (tiles with 6 textures that can be rotated)
 * 
 * ![](/img/bitmask_6.png)
 * @param {Grid} grid
 * @param {number} x
 * @param {number} y
 * @param {Array} connects_to
 * @param {string} [prop_path="type"] the property path if its an object, otherwise pass null
 * @return {number} 
 */
function get4MaskRotated(grid, x, y, connects_to, prop_path = "type") {
    let n = grid.neighbours(x, y, prop_path);
    let m = 0;
    let dirs = ["n", "w", "e", "s"];
    connects_to.forEach(tag => {
        for (let i = 0; i < dirs.length; i++) {
            if (n[dirs[i]].tags) {
                if (n[dirs[i]].hasTag(tag)) {
                    m += Math.pow(2, i);
                }
            } else {
                if (n[dirs[i]] == tag) {
                    m += Math.pow(2, i);
                }
            }
        }
    });

    return getTileposAndRotation(noncorner_map, m);
}

/**
 * Get a bitmask texture offset for always front-facing tiles, like walls
 * 
 *  **Example texture:**  
 *  ![](/img/bitmask_front.png)
 * @param {Grid} grid
 * @param {number} x
 * @param {number} y
 * @param {Array} connects_to
 * @param {string} [prop_path="type"] the property path if its an object, otherwise pass null
 * @return {number} 
 */
function get4MaskFront(grid, x, y, connects_to, prop_path = "type") {
    let n = grid.neighbours(x, y, prop_path);
    let m = 0;
    connects_to.forEach(tag => {
        if (n.e == tag) {
            m += 1;
        }
        if (n.se == tag) {
            m += 2;
        }
        if (n.sw == tag) {
            m += 4;
        }
        if (n.w == tag) {
            m += 8;
        }
    });

    return front_map[m];
}


/**
 *  Get a bitmask texture offset for oriented tiles with corners (tiles with 48 textures that can't be rotated)
 * 
 *  **Example texture:**  
 *  ![](/img/bitmask_oriented.png)
 * @param {Grid} grid
 * @param {number} x
 * @param {number} y
 * @param {Array} connects_to
 * @param {string} [prop_path="type"] the property path if its an object, otherwise pass null
 * @return {number} 
 */
function get8Mask(grid, x, y, connects_to, prop_path = "type") {
    let m = 0;
    let n = grid.neighbours(x, y, prop_path);

    for (const key in n) {
        if (n[key] === null) continue;

        if (n[key].tags) {
            n[key] = n[key].hasAnyTag(connects_to);
        } else {
            n[key] = connects_to.includes(n[key]);
        }
    }

    if (n.n) {
        m += 2;
        if (n.ne && n.e) m += 4;
    }
    if (n.e) {
        m += 16;
        if (n.se && n.s) m += 128;
    }
    if (n.s) {
        m += 64;
        if (n.sw && n.w) m += 32;
    }
    if (n.w) {
        m += 8;
        if (n.nw && n.n) m += 1;
    }

    return oriented_mask[m] || m;
}

/**
 *  Get a bitmask texture offset for tiles with corners (tiles with 15 textures that can be rotated)
 * 
 *  **Example texture:**  
 *  ![](/img/bitmask_rotatable.png)
 * @param {Grid} grid
 * @param {number} x
 * @param {number} y
 * @param {Array} connects_to
 * @param {string} [prop_path="type"] the property path if its an object, otherwise pass null
 * @return {number} 
 */
function get8MaskRotated(grid, x, y, connects_to, prop_path = "type") {
    let m = grid.bitmask(x, y, connects_to, prop_path);
    return getTileposAndRotation(map, m);
}

export {
    get4MaskFront,
    get4MaskRotated,
    get8Mask,
    get8MaskRotated
};