Changed grid overlay to use repeating images instead of shapes
This commit is contained in:
parent
663e1af4d9
commit
2d5589a02e
@ -1,135 +1,74 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React from "react";
|
||||||
import { Line, Group, RegularPolygon } from "react-konva";
|
import { Group, Rect } from "react-konva";
|
||||||
|
import useImage from "use-image";
|
||||||
|
|
||||||
import { getCellLocation } from "../helpers/grid";
|
|
||||||
import Vector2 from "../helpers/Vector2";
|
import Vector2 from "../helpers/Vector2";
|
||||||
|
|
||||||
import { useGrid } from "../contexts/GridContext";
|
import { useGrid } from "../contexts/GridContext";
|
||||||
import { useMapInteraction } from "../contexts/MapInteractionContext";
|
|
||||||
|
|
||||||
import useDebounce from "../hooks/useDebounce";
|
import squarePatternDark from "../images/SquarePatternDark.png";
|
||||||
|
import squarePatternLight from "../images/SquarePatternLight.png";
|
||||||
|
import hexPatternDark from "../images/HexPatternDark.png";
|
||||||
|
import hexPatternLight from "../images/HexPatternLight.png";
|
||||||
|
|
||||||
function Grid({ strokeWidth, stroke }) {
|
function Grid({ stroke }) {
|
||||||
const {
|
const { grid, gridPixelSize, gridOffset, gridCellPixelSize } = useGrid();
|
||||||
grid,
|
let imageSource;
|
||||||
gridStrokeWidth,
|
if (grid.type === "square") {
|
||||||
gridPixelSize,
|
if (stroke === "black") {
|
||||||
gridOffset,
|
imageSource = squarePatternDark;
|
||||||
gridCellPixelSize,
|
} else {
|
||||||
} = useGrid();
|
imageSource = squarePatternLight;
|
||||||
|
}
|
||||||
const gridGroupRef = useRef();
|
} else {
|
||||||
const { stageScale, mapWidth } = useMapInteraction();
|
if (stroke === "black") {
|
||||||
const debouncedStageScale = useDebounce(stageScale, 50);
|
imageSource = hexPatternDark;
|
||||||
useEffect(() => {
|
} else {
|
||||||
const gridGroup = gridGroupRef.current;
|
imageSource = hexPatternLight;
|
||||||
if (gridGroup && grid?.size.x && grid?.size.y && debouncedStageScale) {
|
|
||||||
const gridRect = gridGroup.getClientRect();
|
|
||||||
if (gridRect.width > 0 && gridRect.height > 0) {
|
|
||||||
// 150 pixels per grid cell
|
|
||||||
const maxMapSize = Math.min(
|
|
||||||
Math.max(grid.size.x, grid.size.y) * 150,
|
|
||||||
7680 // Max 8K
|
|
||||||
);
|
|
||||||
const maxGridSize =
|
|
||||||
Math.max(gridRect.width, gridRect.height) / debouncedStageScale;
|
|
||||||
const maxPixelRatio = maxMapSize / maxGridSize;
|
|
||||||
gridGroup.cache({
|
|
||||||
pixelRatio: Math.min(
|
|
||||||
Math.max(debouncedStageScale * 2, 1),
|
|
||||||
maxPixelRatio
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [grid, debouncedStageScale, mapWidth]);
|
|
||||||
|
const [patternImage] = useImage(imageSource);
|
||||||
|
|
||||||
if (!grid?.size.x || !grid?.size.y) {
|
if (!grid?.size.x || !grid?.size.y) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const negativeGridOffset = Vector2.multiply(gridOffset, -1);
|
const negativeGridOffset = Vector2.multiply(gridOffset, -1);
|
||||||
const finalStrokeWidth = gridStrokeWidth * strokeWidth;
|
|
||||||
|
|
||||||
const shapes = [];
|
let patternProps = {};
|
||||||
if (grid.type === "square") {
|
if (grid.type === "square") {
|
||||||
for (let x = 1; x < grid.size.x; x++) {
|
// Square grid pattern is 150 DPI
|
||||||
shapes.push(
|
const scale = gridCellPixelSize.width / 300;
|
||||||
<Line
|
patternProps.fillPatternScaleX = scale;
|
||||||
key={`grid_x_${x}`}
|
patternProps.fillPatternScaleY = scale;
|
||||||
points={[
|
patternProps.fillPatternOffsetX = gridCellPixelSize.width / 2;
|
||||||
x * gridCellPixelSize.width,
|
patternProps.fillPatternOffsetY = gridCellPixelSize.height / 2;
|
||||||
0,
|
} else if (grid.type === "hexVertical") {
|
||||||
x * gridCellPixelSize.width,
|
// Hex tile pattern is 153 DPI to better fit hex tiles
|
||||||
gridPixelSize.height,
|
const scale = gridCellPixelSize.width / 153;
|
||||||
]}
|
patternProps.fillPatternScaleX = scale;
|
||||||
stroke={stroke}
|
patternProps.fillPatternScaleY = scale;
|
||||||
strokeWidth={finalStrokeWidth}
|
patternProps.fillPatternOffsetY = gridCellPixelSize.radius / 2;
|
||||||
opacity={0.5}
|
} else if (grid.type === "hexHorizontal") {
|
||||||
offset={negativeGridOffset}
|
const scale = gridCellPixelSize.height / 153;
|
||||||
/>
|
patternProps.fillPatternScaleX = scale;
|
||||||
);
|
patternProps.fillPatternScaleY = scale;
|
||||||
}
|
patternProps.fillPatternOffsetY = -gridCellPixelSize.radius / 2;
|
||||||
for (let y = 1; y < grid.size.y; y++) {
|
patternProps.fillPatternRotation = 90;
|
||||||
shapes.push(
|
|
||||||
<Line
|
|
||||||
key={`grid_y_${y}`}
|
|
||||||
points={[
|
|
||||||
0,
|
|
||||||
y * gridCellPixelSize.height,
|
|
||||||
gridPixelSize.width,
|
|
||||||
y * gridCellPixelSize.height,
|
|
||||||
]}
|
|
||||||
stroke={stroke}
|
|
||||||
strokeWidth={finalStrokeWidth}
|
|
||||||
opacity={0.5}
|
|
||||||
offset={negativeGridOffset}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (grid.type === "hexVertical" || grid.type === "hexHorizontal") {
|
|
||||||
// End at grid size + 1 to overshoot the bounds of the grid to ensure all lines are drawn
|
|
||||||
for (let x = 0; x < grid.size.x + 1; x++) {
|
|
||||||
for (let y = 0; y < grid.size.y + 1; y++) {
|
|
||||||
const cellLocation = getCellLocation(grid, x, y, gridCellPixelSize);
|
|
||||||
shapes.push(
|
|
||||||
<Group
|
|
||||||
key={`grid_${x}_${y}`}
|
|
||||||
x={cellLocation.x}
|
|
||||||
y={cellLocation.y}
|
|
||||||
offset={negativeGridOffset}
|
|
||||||
>
|
|
||||||
{/* Offset the hex tile to align to top left of grid */}
|
|
||||||
<Group offset={Vector2.multiply(gridCellPixelSize, -0.5)}>
|
|
||||||
<RegularPolygon
|
|
||||||
sides={6}
|
|
||||||
radius={gridCellPixelSize.radius}
|
|
||||||
stroke={stroke}
|
|
||||||
strokeWidth={finalStrokeWidth}
|
|
||||||
opacity={0.5}
|
|
||||||
rotation={grid.type === "hexVertical" ? 0 : 90}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group>
|
||||||
// Clip grid to bounds to cover hex overshoot
|
<Rect
|
||||||
clipFunc={(context) => {
|
width={gridPixelSize.width}
|
||||||
context.rect(
|
height={gridPixelSize.height}
|
||||||
gridOffset.x - finalStrokeWidth / 2,
|
offset={negativeGridOffset}
|
||||||
gridOffset.y - finalStrokeWidth / 2,
|
fillPatternImage={patternImage}
|
||||||
gridPixelSize.width + finalStrokeWidth,
|
fillPatternRepeat="repeat"
|
||||||
gridPixelSize.height + finalStrokeWidth
|
opacity={stroke === "black" ? 0.8 : 0.4}
|
||||||
);
|
{...patternProps}
|
||||||
}}
|
/>
|
||||||
ref={gridGroupRef}
|
|
||||||
>
|
|
||||||
{shapes}
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ function MapEditor({ map, onSettingsChange }) {
|
|||||||
width={mapWidth}
|
width={mapWidth}
|
||||||
height={mapHeight}
|
height={mapHeight}
|
||||||
>
|
>
|
||||||
<MapGrid map={map} strokeWidth={0.5} />
|
<MapGrid map={map} />
|
||||||
<MapGridEditor map={map} onGridChange={handleGridChange} />
|
<MapGridEditor map={map} onGridChange={handleGridChange} />
|
||||||
</GridProvider>
|
</GridProvider>
|
||||||
)}
|
)}
|
||||||
|
@ -8,7 +8,7 @@ import { getImageLightness } from "../../helpers/image";
|
|||||||
|
|
||||||
import Grid from "../Grid";
|
import Grid from "../Grid";
|
||||||
|
|
||||||
function MapGrid({ map, strokeWidth }) {
|
function MapGrid({ map }) {
|
||||||
let mapSourceMap = map;
|
let mapSourceMap = map;
|
||||||
// Use lowest resolution for grid lightness
|
// Use lowest resolution for grid lightness
|
||||||
if (map && map.type === "file" && map.resolutions) {
|
if (map && map.type === "file" && map.resolutions) {
|
||||||
@ -29,13 +29,7 @@ function MapGrid({ map, strokeWidth }) {
|
|||||||
}
|
}
|
||||||
}, [mapImage, mapLoadingStatus]);
|
}, [mapImage, mapLoadingStatus]);
|
||||||
|
|
||||||
return (
|
return <Grid stroke={isImageLight ? "black" : "white"} />;
|
||||||
<Grid strokeWidth={strokeWidth} stroke={isImageLight ? "black" : "white"} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MapGrid.defaultProps = {
|
|
||||||
strokeWidth: 0.1,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MapGrid;
|
export default MapGrid;
|
||||||
|
BIN
src/images/HexPatternDark.png
Normal file
BIN
src/images/HexPatternDark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
src/images/HexPatternLight.png
Normal file
BIN
src/images/HexPatternLight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
src/images/SquarePatternDark.png
Normal file
BIN
src/images/SquarePatternDark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
src/images/SquarePatternLight.png
Normal file
BIN
src/images/SquarePatternLight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Loading…
Reference in New Issue
Block a user