I am creating a pathfinding application and I want to connect every hexgon(H) to its adjacent hexagons. The grid is a rectangle but it is populated with hexagons. The issue is the code right now to connect these hexagons is lengthy and extremely finicky. An example of what i am trying to achieve is:
The issue is that the connections between say one hexagon and its neighbours (range from 2-6 depending on their placement in the grid) is not working properly. An example of the code i am using right now to connect a hexagon with 6 neighbours is:
currentState.graph().addEdge(i, i 1, 1);
currentState.graph().addEdge(i, i - HexBoard.rows 1, 1);
currentState.graph().addEdge(i, i - HexBoard.rows, 1);
currentState.graph().addEdge(i, i HexBoard.rows 1, 1);
currentState.graph().addEdge(i, i HexBoard.rows , 1);
The graph is essetialy the grid, addEdge adds a connection from src ->dest with cost(c) in order. Is there any algorithm or way to make my code less bulky ? (right now it is polluted with if-else clauses)? The site which inspired me :https://clementmihailescu.github.io/Pathfinding-Visualizer/#
EDIT : The problem is not in drawing hexagons (They are already SVGs), it in assigning them edges and connections.
CodePudding user response:
Interesting problem... To set a solid foundation, here's a hexagon grid class that is neither lengthy nor finicky, based on a simple data structure of a linear array. A couple of notes...
- The
HexagonGrid
constructor accepts the hexagon grid dimensions in terms of the number of hexagons wide (hexWidth
) by number of hexagons high (hexHeight
). - The
hexHeight
alternates by an additional hexagon every other column for a more pleasing appearance. Thus an odd number forhexWidth
bookends the hexagon grid with the same number of hexagons in the first and last columns. - The
length
attribute represents the total number of hexagons in the grid. - Each hexagon is referenced by a linear index from 0..
length
.
To aid in visualizing the linear indexing scheme, the code snippet includes the linear index value in the hexagon. Such an indexing scheme offers the opportunity to have a parallel array of the same length which represents the characteristics of each specific hexagon by index.
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
class HexagonGrid {
constructor( hexWidth, hexHeight, edgeLength ) {
this.hexWidth = hexWidth;
this.hexHeight = hexHeight;
this.edgeLength = edgeLength;
this.cellWidthPair = this.hexHeight * 2 1;
this.length = this.cellWidthPair * ( hexWidth / 2 |0 ) hexHeight * ( hexWidth % 2 );
this.dx = edgeLength * Math.sin( Math.PI / 6 );
this.dy = edgeLength * Math.cos( Math.PI / 6 );
}
centerOfHexagon( i ) {
let xPairNo = i % this.cellWidthPair;
return {
x: this.dx this.edgeLength / 2 ( i / this.cellWidthPair |0 ) * ( this.dx this.edgeLength ) * 2 ( this.hexHeight <= i % this.cellWidthPair ) * ( this.dx this.edgeLength ),
y: xPairNo < this.hexHeight ? ( xPairNo 1 ) * this.dy * 2 : this.dy ( xPairNo - this.hexHeight ) * this.dy * 2
};
}
edge( i ) {
let topCheck = i % ( this.hexHeight 0.5 );
return (
i < this.hexHeight
|| ( i 1 ) % ( this.hexHeight 0.5 ) === this.hexHeight
|| i % ( this.hexHeight 0.5 ) === this.hexHeight
|| ( i 1 ) % ( this.hexHeight 0.5 ) === 0
|| i % ( this.hexHeight 0.5 ) === 0
|| this.length - this.hexHeight < i
);
}
drawHexagon( ctx, center ) {
let halfEdge = this.edgeLength / 2;
ctx.beginPath();
ctx.moveTo( center.x - halfEdge, center.y - this.dy );
ctx.lineTo( center.x halfEdge, center.y - this.dy );
ctx.lineTo( center.x halfEdge this.dx, center.y );
ctx.lineTo( center.x halfEdge, center.y this.dy );
ctx.lineTo( center.x - halfEdge, center.y this.dy );
ctx.lineTo( center.x - halfEdge - this.dx, center.y );
ctx.lineTo( center.x - halfEdge, center.y - this.dy );
ctx.stroke();
}
drawGrid( ctx, topLeft ) {
ctx.font = '10px Arial';
for ( let i = 0; i < this.length; i ) {
let center = this.centerOfHexagon( i );
this.drawHexagon( ctx, { x: topLeft.x center.x, y: topLeft.y center.y } );
ctx.fillStyle = this.edge( i ) ? 'red' : 'black';
ctx.fillText( i, topLeft.x center.x - 5, topLeft.y center.y 5 );
}
}
}
let myHexGrid = new HexagonGrid( 11, 5, 20 );
myHexGrid.drawGrid( ctx, { x: 20, y: 20 } );
<canvas id=canvas width=500 height=500 />
A large benefit of the linear index is that it makes path searching easier, as each interior hexagon is surrounded by hexagons with relative indexes of -1, -6, -5, 1, 6, 5. For example, applying the relative indexes to hexagon 18 results in a list of surrounding hexagons of 17, 12, 13, 19, 24, 23.
As a bonus, the edge
method indicates whether the hexagon is on the edge of the grid. (In the code snippet, the edge cells are identified by red text.) Highly recommend that edge cells not be part of the pathing (ie, they are unreachable) as this simplifies any path searching. Otherwise the logic to determine the traversable of cells becomes very complex, as now if on an edge cell, the relative indexes indicating the surrounding hexagons no longer apply...