All files / src/shared/lib/tiles gridSelection.ts

100% Statements 40/40
100% Branches 9/9
100% Functions 7/7
100% Lines 32/32

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                        47x           36x 36x   257x       47x 189x   47x       36x             47x         53x   47x             16x 16x   16x 26x 36x       16x     47x               20x 20x   20x   20x 31x 53x       20x 31x 53x 53x   53x 3x     50x         20x    
export interface GridSelection {
  x: number;
  y: number;
  width: number;
  height: number;
}
 
export interface GridOffset {
  x: number;
  y: number;
}
 
const createGrid = <T>(
  values: readonly T[],
  width: number,
  height: number,
  emptyValue: T,
): T[] => {
  const size = width * height;
  return Array.from(
    { length: size },
    (_, index) => values[index] ?? emptyValue,
  );
};
 
const getGridIndex = (x: number, y: number, width: number): number =>
  y * width + x;
 
const getSelectionBounds = (
  selection: GridSelection,
  width: number,
  height: number,
) => ({
  xStart: Math.max(0, selection.x),
  yStart: Math.max(0, selection.y),
  xEnd: Math.min(width, selection.x + selection.width),
  yEnd: Math.min(height, selection.y + selection.height),
});
 
const isInsideGrid = (
  x: number,
  y: number,
  width: number,
  height: number,
): boolean => x >= 0 && y >= 0 && x < width && y < height;
 
export const clearGridSelection = <T>(
  values: readonly T[],
  width: number,
  height: number,
  selection: GridSelection,
  emptyValue: T,
): T[] => {
  const result = createGrid(values, width, height, emptyValue);
  const bounds = getSelectionBounds(selection, width, height);
 
  for (let y = bounds.yStart; y < bounds.yEnd; y++) {
    for (let x = bounds.xStart; x < bounds.xEnd; x++) {
      result[getGridIndex(x, y, width)] = emptyValue;
    }
  }
 
  return result;
};
 
export const moveGridSelection = <T>(
  values: readonly T[],
  width: number,
  height: number,
  selection: GridSelection,
  offset: GridOffset,
  emptyValue: T,
): T[] => {
  const source = createGrid(values, width, height, emptyValue);
  const result = [...source];
 
  const bounds = getSelectionBounds(selection, width, height);
 
  for (let y = bounds.yStart; y < bounds.yEnd; y++) {
    for (let x = bounds.xStart; x < bounds.xEnd; x++) {
      result[getGridIndex(x, y, width)] = emptyValue;
    }
  }
 
  for (let y = bounds.yStart; y < bounds.yEnd; y++) {
    for (let x = bounds.xStart; x < bounds.xEnd; x++) {
      const targetX = x + offset.x;
      const targetY = y + offset.y;
 
      if (!isInsideGrid(targetX, targetY, width, height)) {
        continue;
      }
 
      result[getGridIndex(targetX, targetY, width)] =
        source[getGridIndex(x, y, width)];
    }
  }
 
  return result;
};