You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
6.7 KiB
137 lines
6.7 KiB
import {HexagonOrientation, HexLayout, LineParity} from "./HexMap";
|
|
|
|
/** Staggered (storage) coordinates for accessing cell storage. */
|
|
export interface StorageCoordinates {
|
|
/** The index of the line within the map. */
|
|
readonly line: number
|
|
/** The index of the cell within the line. */
|
|
readonly cell: number
|
|
}
|
|
|
|
/** Translates storage coordinates to a key unique among all cells in the map. */
|
|
export function storageCoordinatesToKey({line, cell}: StorageCoordinates): string {
|
|
return `${line},${cell}`
|
|
}
|
|
|
|
/**
|
|
* The full set of coordinates on screen for a hexagon.
|
|
* The axis changes depending on the orientation.
|
|
* For POINTY_TOP mode, the flat axis is the X axis, and the pointy axis is the Y axis.
|
|
* For FLAT_TOP mode, the flat axis is the Y axis, and the pointy axis is the X axis.
|
|
* The vertices of a hexagon will always be at these pairs, in this order, with the axes set as above:
|
|
* pointyStart, flatCenter
|
|
* pointyFirstThird, flatStart
|
|
* pointyLastThird, flatStart
|
|
* pointyEnd, flatCenter
|
|
* pointyLastThird, flatEnd
|
|
* pointyFirstThird, flatEnd
|
|
*/
|
|
export interface RenderCoordinates {
|
|
/** The orientation of the hexagons, for determining which axes to use. */
|
|
readonly orientation: HexagonOrientation,
|
|
/** The beginning of the pointy axis - where the first pointy vertex is, on the axis that runs through the pointy vertices. */
|
|
readonly pointyStart: number,
|
|
/** The end of the pointy axis - where the last pointy vertex is, on the axis that runs through the pointy vertices. */
|
|
readonly pointyEnd: number,
|
|
/** The first "third" (actually a quarter) of the pointy axis - where the first vertex of each flat side is, on the axis that runs through the pointy vertices. */
|
|
readonly pointyFirstThird: number,
|
|
/** The last "third" (actually a quarter) of the pointy axis - where the last vertex of each flat side is, on the axis that runs through the pointy vertices. */
|
|
readonly pointyLastThird: number,
|
|
/** The start of the flat axis - where the first flat side is, on the axis that runs through the flat sides. */
|
|
readonly flatStart: number,
|
|
/** The center of the flat axis - where the two pointy vertices are, on the axis that runs through the flat sides. */
|
|
readonly flatCenter: number,
|
|
/** The end of the flat axis - where the second flat side is, on the axis that runs through the flat sides. */
|
|
readonly flatEnd: number,
|
|
}
|
|
|
|
/** The offsets for rendering a hexagonal map. */
|
|
export interface RenderOffsets {
|
|
/** The distance from the left the first x coordinate should be. */
|
|
readonly left: number
|
|
/** The distance from the top the first y coordinate should be. */
|
|
readonly top: number
|
|
/** How big each hexagon should be. The "radius" from the center to any vertex. */
|
|
readonly size: number
|
|
/** The way the hex map should be displayed. Usually the same as the origin map. */
|
|
readonly displayMode: HexLayout
|
|
}
|
|
|
|
export interface RenderSize {
|
|
readonly width: number
|
|
readonly height: number
|
|
}
|
|
|
|
// Heavily based on https://www.redblobgames.com/grids/hexagons/
|
|
const POINTY_AXIS_FACTOR = 2;
|
|
const FLAT_AXIS_FACTOR = Math.sqrt(3);
|
|
export function storageCoordinatesToRenderCoordinates({line, cell}: StorageCoordinates, renderOffsets: RenderOffsets): RenderCoordinates {
|
|
const {orientation, indentedLines} = renderOffsets.displayMode;
|
|
|
|
const flatLength = renderOffsets.size * FLAT_AXIS_FACTOR;
|
|
const pointyLength = renderOffsets.size * POINTY_AXIS_FACTOR;
|
|
|
|
const flatStorageCoordinate = cell;
|
|
const pointyStorageCoordinate = line;
|
|
|
|
const lineParity = (pointyStorageCoordinate % 2) === 0 ? LineParity.EVEN : LineParity.ODD;
|
|
const isIndented = lineParity === indentedLines;
|
|
|
|
const flatOffset = orientation === HexagonOrientation.FLAT_TOP ? renderOffsets.top : renderOffsets.left;
|
|
const pointyOffset = orientation === HexagonOrientation.POINTY_TOP ? renderOffsets.top : renderOffsets.left;
|
|
|
|
const pointyStart = pointyOffset + pointyStorageCoordinate * pointyLength * 3 / 4;
|
|
const pointyFirstThird = pointyStart + pointyLength / 4
|
|
const pointyLastThird = pointyStart + pointyLength * 3 / 4
|
|
const pointyEnd = pointyStart + pointyLength;
|
|
|
|
const flatStart = flatOffset + flatLength * ((isIndented ? 0.5 : 0) + flatStorageCoordinate);
|
|
const flatCenter = flatStart + flatLength / 2;
|
|
const flatEnd = flatStart + flatLength;
|
|
|
|
return {
|
|
orientation,
|
|
pointyStart,
|
|
pointyFirstThird,
|
|
pointyLastThird,
|
|
pointyEnd,
|
|
flatStart,
|
|
flatCenter,
|
|
flatEnd
|
|
}
|
|
}
|
|
|
|
export function sizeFromLinesAndCells({offsets, lines, cells, rightMargin = 0, bottomMargin = 0}: {offsets: RenderOffsets, lines: number, cells: number, rightMargin?: number, bottomMargin?: number}): RenderSize {
|
|
const {
|
|
top: topMargin,
|
|
left: leftMargin,
|
|
displayMode: {
|
|
orientation,
|
|
indentedLines
|
|
}
|
|
} = offsets;
|
|
const flatMargins = orientation === HexagonOrientation.FLAT_TOP ? topMargin + bottomMargin : leftMargin + rightMargin;
|
|
const pointyMargins = orientation === HexagonOrientation.POINTY_TOP ? topMargin + bottomMargin : leftMargin + rightMargin;
|
|
|
|
const flatCellLength = offsets.size * FLAT_AXIS_FACTOR;
|
|
const pointyCellLength = offsets.size * POINTY_AXIS_FACTOR;
|
|
|
|
const hasIndents = lines > 1 || indentedLines === LineParity.EVEN
|
|
|
|
// Every line will be 3/4 of a cell length apart in the pointy direction;
|
|
// however, the last line is still one full pointy cell long.
|
|
const pointyLength = pointyMargins + ((cells - 1) * pointyCellLength * 3 / 4) + pointyCellLength;
|
|
// Every cell will be one full cell length apart in the flat direction;
|
|
// however, if there are indents, another half cell is needed to accommodate them.
|
|
const flatLength = flatMargins + lines * flatCellLength + (hasIndents ? flatCellLength / 2 : 0);
|
|
|
|
return orientation === HexagonOrientation.FLAT_TOP ? { width: pointyLength, height: flatLength } : { width: flatLength, height: pointyLength }
|
|
}
|
|
|
|
export function renderCoordinatesToPolygonPoints(coords: RenderCoordinates): string {
|
|
if (coords.orientation === HexagonOrientation.FLAT_TOP) {
|
|
return `${coords.pointyStart},${coords.flatCenter} ${coords.pointyFirstThird},${coords.flatStart} ${coords.pointyLastThird},${coords.flatStart} ${coords.pointyEnd},${coords.flatCenter} ${coords.pointyLastThird},${coords.flatEnd} ${coords.pointyFirstThird},${coords.flatEnd}`
|
|
} else /* if (coords.orientation === HexagonOrientation.POINTY_TOP) */ {
|
|
return `${coords.flatCenter},${coords.pointyStart} ${coords.flatStart},${coords.pointyFirstThird} ${coords.flatStart},${coords.pointyLastThird} ${coords.flatCenter},${coords.pointyEnd} ${coords.flatEnd},${coords.pointyLastThird} ${coords.flatEnd},${coords.pointyFirstThird}`
|
|
}
|
|
} |