All files / src/core/grid square-grid.ts

20.19% Statements 21/104
100% Branches 2/2
22.22% Functions 2/9
20.19% Lines 21/104

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 1221x           1x 1x   1x           1x             1x     1x 1x 1x 1x 1x 1x 1x 1x 1x   1x                     1x               1x                                         1x                                         1x                                               1x  
import { Point, EdgeInfo } from "../../types";
import { pointsClose, removeDuplicateEdges } from "../utils/geometry";
import { Grid } from "./grid";
import { scanCellWindow } from "./grid-scan";
import { findClosestEdgeInWindow, findClosestVertexInWindow } from "./grid-search";
 
export class SquareGrid implements Grid {
  constructor(private scale: number) {}
 
  pixelToCell(pixel: Point): { col: number; row: number } {
    const col = Math.floor(pixel.x / this.scale);
    const row = Math.floor(pixel.y / this.scale);
    return { col, row };
  }
 
  cellToPixel(cell: { col: number; row: number }): Point {
    return {
      x: cell.col * this.scale + this.scale / 2,
      y: cell.row * this.scale + this.scale / 2,
    };
  }
 
  getNeighbors(cell: {
    col: number;
    row: number;
  }): { col: number; row: number }[] {
    const { col, row } = cell;
    return [
      { col, row: row - 1 }, // North
      { col: col + 1, row }, // East
      { col, row: row + 1 }, // South
      { col: col - 1, row }, // West
    ];
  }
 
  getCellPolygon(cell: { col: number; row: number }): Point[] {
    const x = cell.col * this.scale;
    const y = cell.row * this.scale;
    return [
      { x, y },
      { x: x + this.scale, y },
      { x: x + this.scale, y: y + this.scale },
      { x, y: y + this.scale },
    ];
  }
 
  getCellEdges(cell: { col: number; row: number }): EdgeInfo[] {
    const poly = this.getCellPolygon(cell);
    return poly.map((p, i) => ({
      type: "edge",
      points: [p, poly[(i + 1) % poly.length]],
    }));
  }
 
  getEdgeAt(
    pixel: Point,
    threshold: number,
    gridWidth: number,
    gridHeight: number
  ): EdgeInfo | null {
    const approxCol = Math.floor(pixel.x / this.scale);
    const approxRow = Math.floor(pixel.y / this.scale);
    return findClosestEdgeInWindow(
      pixel,
      threshold,
      gridWidth,
      gridHeight,
      approxCol,
      approxRow,
      1,
      1,
      (cell) => this.getCellEdges(cell)
    );
  }
 
  getVertexAt(
    pixel: Point,
    threshold: number,
    gridWidth: number,
    gridHeight: number
  ): Point | null {
    const approxCol = Math.floor(pixel.x / this.scale);
    const approxRow = Math.floor(pixel.y / this.scale);
    return findClosestVertexInWindow(
      pixel,
      threshold,
      gridWidth,
      gridHeight,
      approxCol,
      approxRow,
      1,
      1,
      (cell) => this.getCellPolygon(cell)
    );
  }
 
  getEdgesAtVertex(
    vertex: Point,
    gridWidth: number,
    gridHeight: number
  ): EdgeInfo[] {
    const edges: EdgeInfo[] = [];
    const epsilon = 0.1;
 
    const approxCol = Math.floor(vertex.x / this.scale);
    const approxRow = Math.floor(vertex.y / this.scale);
 
    scanCellWindow(gridWidth, gridHeight, approxCol, approxRow, 1, 1, (c, r) => {
      const cellEdges = this.getCellEdges({ col: c, row: r });
      for (const edge of cellEdges) {
        if (
          pointsClose(vertex, edge.points[0], epsilon) ||
          pointsClose(vertex, edge.points[1], epsilon)
        ) {
          edges.push(edge);
        }
      }
    });
    return removeDuplicateEdges(edges);
  }
}