module TK.SpaceTac { /** * Abstract grid for the arena where the battle takes place * * The grid is used to snap arena coordinates on grid vertices, for ships and targets * * The default implementation does not enforce any grid or unit (leaves coordinates as they are) */ export class ArenaGrid { /** * Get the base unit of measurement between two points */ getUnit(): number { return 1; } /** * Check that an arena location is on a grid vertex */ check(loc: IArenaLocation): boolean { return arenaDistance(loc, this.snap(loc)) < 1e-8; } /** * Snap a floating point arena location to a grid vertex */ snap(loc: IArenaLocation): IArenaLocation { return loc; } /** * Measure the distance between two points * * This returns a distance in grid units */ measure(loc1: IArenaLocation, loc2: IArenaLocation): number { return arenaDistance(this.snap(loc1), this.snap(loc2)); } /** * Check that a location is in range of another */ inRange(loc1: IArenaLocation, loc2: IArenaLocation, range: number): boolean { return this.measure(loc1, loc2) - range < 1e-8; } } /** * Pixel unbounded grid * * This will only round the coordinates to the pixels, with an optional unit for distance measurements */ export class PixelGrid extends ArenaGrid { constructor(protected readonly unit = 1) { super(); } getUnit(): number { return this.unit; } snap(loc: IArenaLocation): IArenaLocation { return new ArenaLocation(Math.round(loc.x), Math.round(loc.y)); } measure(loc1: IArenaLocation, loc2: IArenaLocation): number { // FIXME let d = super.measure(loc1, loc2) / this.unit; let r = Math.round(d); if (r >= d) { return Math.ceil(d); } else if (d - r < 1e-8) { return r; } else { return Math.floor(d); } } } /** * Hexagonal unbounded arena grid * * This grid is composed of regular hexagons where all vertices are at a same distance "unit" of the hexagon center */ export class HexagonalArenaGrid extends PixelGrid { private readonly yunit: number; constructor(unit: number, yfactor = Math.sqrt(0.75)) { super(unit); this.yunit = unit * yfactor; } snap(loc: IArenaLocation): IArenaLocation { let yr = Math.round(loc.y / this.yunit); let xr: number; if (yr % 2 == 0) { xr = Math.round(loc.x / this.unit); } else { xr = Math.round((loc.x - 0.5 * this.unit) / this.unit) + 0.5; } return new ArenaLocation((xr * this.unit) || 0, (yr * this.yunit) || 0); } } }