arscan / hexasphere.js

🌐Generate a sphere covered (mostly) in hexagons.
https://www.robscanlon.com/hexasphere
247 stars 50 forks source link

Ordered Neighbours #10

Open AaronGaddes opened 3 years ago

AaronGaddes commented 3 years ago

Hi There. Firstly I'd like to say that this is a nifty project;

I am currently trying to build a little game which uses this library to create the game world. However I have run into an issue where I need the neighbours to be in the same order as the boundary points so that I am able to determine what tile is in a particular direction from the current tile.

Any chance that would be something you would be able to add into the library? I have tried making changes to a local copy of the library myself by using the face index as a kind of sorting key to the neighborHash but that didn't seem to work:

// tile.js
    var neighborHash = {};
    for(var f=0; f< this.faces.length; f++){
        // build boundary
        this.boundary.push(this.faces[f].getCentroid().segment(this.centerPoint, hexSize));

        // get neighboring tiles
        var otherPoints = this.faces[f].getOtherPoints(this.centerPoint);
        for(var o = 0; o < 2; o++){
            neighborHash[otherPoints[o]] = f;
        }

    }

    this.neighborIds = Object.entries(neighborHash).sort(([n1, f1], [n2, f2]) => f1 - f2).map(([n])=> n);
caramboleyo commented 1 year ago

The problem for this is here: https://github.com/arscan/hexasphere.js/blob/97858fa17eb25f992e799cfaf1c4725265c513f8/src/tile.js#L68

It creates the boundary by walking it point by point. For every point it determines the two other adjacent tiles, which border this point. Problem is here, that sometimes those two tiles come in reverse order. So although the boundary is being walked in correct order, some pairs are twisted. Because it is not walking the edges, but the corner points. While the edges would always have only one neighbor tile, the points have two.

Example: the boundary is along tiles with index 274 268 153 152 but those points return 268 274 152 153. The last two are twisted.

My quick hack for this is cashing it in a prev array and a first var to maintain order, surely @arscan will have a more elegant solution:

        const neighborHash = {};
        let prev = [];
        let first = null;
        for (var f = 0; f < this.faces.length; f++) {
            const p = this.faces[f].getCentroid().segment(this.centerPoint, hexSize);
            // build boundary
            this.boundary.push(p);

            // get neighboring tiles
            const otherPoints = this.faces[f].getOtherPoints(this.centerPoint);
            if (prev.includes(otherPoints[0])) {
                neighborHash[otherPoints[0]] = 1;
                if (first === null) {
                    first = prev[prev.indexOf(otherPoints[0]) === 0 ? 1 : 0];
                }
            }
            if (prev.includes(otherPoints[1])) {
                neighborHash[otherPoints[1]] = 1;
                if (first === null) {
                    first = prev[prev.indexOf(otherPoints[1]) === 0 ? 1 : 0];
                }
            }
            prev = otherPoints;
        }
        neighborHash[first] = 1;

        this.neighborIds = Object.keys(neighborHash);
caramboleyo commented 1 year ago

My solution still leaves one case where first and last seem to be twisted :'(