Added brush grid snapping
This commit is contained in:
parent
6fb2e9f113
commit
d4f8433bb1
@ -45,6 +45,7 @@ function Map({
|
|||||||
|
|
||||||
const [selectedTool, setSelectedTool] = useState("pan");
|
const [selectedTool, setSelectedTool] = useState("pan");
|
||||||
const [brushColor, setBrushColor] = useState("black");
|
const [brushColor, setBrushColor] = useState("black");
|
||||||
|
const [useBrushGridSnapping, setUseBrushGridSnapping] = useState(false);
|
||||||
|
|
||||||
const [drawnShapes, setDrawnShapes] = useState([]);
|
const [drawnShapes, setDrawnShapes] = useState([]);
|
||||||
function handleShapeAdd(shape) {
|
function handleShapeAdd(shape) {
|
||||||
@ -192,7 +193,9 @@ function Map({
|
|||||||
const mapRef = useRef(null);
|
const mapRef = useRef(null);
|
||||||
const mapContainerRef = useRef();
|
const mapContainerRef = useRef();
|
||||||
const gridX = mapData && mapData.gridX;
|
const gridX = mapData && mapData.gridX;
|
||||||
const tokenSizePercent = (1 / gridX) * 100;
|
const gridY = mapData && mapData.gridY;
|
||||||
|
const gridSizeNormalized = { x: 1 / gridX || 0, y: 1 / gridY || 0 };
|
||||||
|
const tokenSizePercent = gridSizeNormalized.x * 100;
|
||||||
const aspectRatio = (mapData && mapData.width / mapData.height) || 1;
|
const aspectRatio = (mapData && mapData.width / mapData.height) || 1;
|
||||||
|
|
||||||
const mapImage = (
|
const mapImage = (
|
||||||
@ -280,6 +283,8 @@ function Map({
|
|||||||
onShapeAdd={handleShapeAdd}
|
onShapeAdd={handleShapeAdd}
|
||||||
onShapeRemove={handleShapeRemove}
|
onShapeRemove={handleShapeRemove}
|
||||||
brushColor={brushColor}
|
brushColor={brushColor}
|
||||||
|
useGridSnapping={useBrushGridSnapping}
|
||||||
|
gridSize={gridSizeNormalized}
|
||||||
/>
|
/>
|
||||||
{mapTokens}
|
{mapTokens}
|
||||||
</Box>
|
</Box>
|
||||||
@ -296,6 +301,8 @@ function Map({
|
|||||||
brushColor={brushColor}
|
brushColor={brushColor}
|
||||||
onBrushColorChange={setBrushColor}
|
onBrushColorChange={setBrushColor}
|
||||||
onEraseAll={handleShapeRemoveAll}
|
onEraseAll={handleShapeRemoveAll}
|
||||||
|
useBrushGridSnapping={useBrushGridSnapping}
|
||||||
|
onBrushGridSnappingChange={setUseBrushGridSnapping}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<ProxyToken
|
<ProxyToken
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Flex, Box, IconButton, Button } from "theme-ui";
|
import { Flex, Box, IconButton, Button, Label } from "theme-ui";
|
||||||
|
|
||||||
import AddMapButton from "./AddMapButton";
|
import AddMapButton from "./AddMapButton";
|
||||||
import ExpandMoreIcon from "../icons/ExpandMoreIcon";
|
import ExpandMoreIcon from "../icons/ExpandMoreIcon";
|
||||||
@ -8,6 +8,8 @@ import BrushToolIcon from "../icons/BrushToolIcon";
|
|||||||
import EraseToolIcon from "../icons/EraseToolIcon";
|
import EraseToolIcon from "../icons/EraseToolIcon";
|
||||||
import UndoIcon from "../icons/UndoIcon";
|
import UndoIcon from "../icons/UndoIcon";
|
||||||
import RedoIcon from "../icons/RedoIcon";
|
import RedoIcon from "../icons/RedoIcon";
|
||||||
|
import GridOnIcon from "../icons/GridOnIcon";
|
||||||
|
import GridOffIcon from "../icons/GridOffIcon";
|
||||||
|
|
||||||
import colors, { colorOptions } from "../helpers/colors";
|
import colors, { colorOptions } from "../helpers/colors";
|
||||||
|
|
||||||
@ -25,6 +27,8 @@ function MapControls({
|
|||||||
brushColor,
|
brushColor,
|
||||||
onBrushColorChange,
|
onBrushColorChange,
|
||||||
onEraseAll,
|
onEraseAll,
|
||||||
|
useBrushGridSnapping,
|
||||||
|
onBrushGridSnappingChange,
|
||||||
}) {
|
}) {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
@ -66,6 +70,33 @@ function MapControls({
|
|||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Label
|
||||||
|
sx={{
|
||||||
|
fontSize: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
":hover": { color: "primary", cursor: "pointer" },
|
||||||
|
":active": { color: "secondary" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
aria-label={
|
||||||
|
useBrushGridSnapping
|
||||||
|
? "Disable Brush Grid Snapping"
|
||||||
|
: "Enable Brush Grid Snapping"
|
||||||
|
}
|
||||||
|
title={
|
||||||
|
useBrushGridSnapping
|
||||||
|
? "Disable Brush Grid Snapping"
|
||||||
|
: "Enable Brush Grid Snapping"
|
||||||
|
}
|
||||||
|
onClick={() => onBrushGridSnappingChange(!useBrushGridSnapping)}
|
||||||
|
>
|
||||||
|
{useBrushGridSnapping ? <GridOffIcon /> : <GridOnIcon />}
|
||||||
|
</IconButton>
|
||||||
|
Grid Lock
|
||||||
|
</Label>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
),
|
),
|
||||||
erase: (
|
erase: (
|
||||||
|
@ -3,6 +3,7 @@ import simplify from "simplify-js";
|
|||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
|
|
||||||
import colors from "../helpers/colors";
|
import colors from "../helpers/colors";
|
||||||
|
import { snapPositionToGrid } from "../helpers/shared";
|
||||||
|
|
||||||
function MapDrawing({
|
function MapDrawing({
|
||||||
width,
|
width,
|
||||||
@ -12,6 +13,8 @@ function MapDrawing({
|
|||||||
onShapeAdd,
|
onShapeAdd,
|
||||||
onShapeRemove,
|
onShapeRemove,
|
||||||
brushColor,
|
brushColor,
|
||||||
|
useGridSnapping,
|
||||||
|
gridSize,
|
||||||
}) {
|
}) {
|
||||||
const canvasRef = useRef();
|
const canvasRef = useRef();
|
||||||
const containerRef = useRef();
|
const containerRef = useRef();
|
||||||
@ -46,7 +49,10 @@ function MapDrawing({
|
|||||||
setPointerPosition(position);
|
setPointerPosition(position);
|
||||||
setIsDrawing(true);
|
setIsDrawing(true);
|
||||||
if (selectedTool === "brush") {
|
if (selectedTool === "brush") {
|
||||||
setBrushPoints([position]);
|
const brushPosition = useGridSnapping
|
||||||
|
? snapPositionToGrid(position, gridSize)
|
||||||
|
: position;
|
||||||
|
setBrushPoints([brushPosition]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +67,15 @@ function MapDrawing({
|
|||||||
}
|
}
|
||||||
if (isDrawing && selectedTool === "brush") {
|
if (isDrawing && selectedTool === "brush") {
|
||||||
setPointerPosition(position);
|
setPointerPosition(position);
|
||||||
setBrushPoints((prevPoints) => [...prevPoints, position]);
|
const brushPosition = useGridSnapping
|
||||||
|
? snapPositionToGrid(position, gridSize)
|
||||||
|
: position;
|
||||||
|
setBrushPoints((prevPoints) => {
|
||||||
|
if (prevPoints[prevPoints.length - 1] === brushPosition) {
|
||||||
|
return prevPoints;
|
||||||
|
}
|
||||||
|
return [...prevPoints, brushPosition];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,3 +23,14 @@ export function fromEntries(iterable) {
|
|||||||
export function isStreamStopped(stream) {
|
export function isStreamStopped(stream) {
|
||||||
return stream.getTracks().reduce((a, b) => a && b, { mute: true });
|
return stream.getTracks().reduce((a, b) => a && b, { mute: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function roundTo(x, to) {
|
||||||
|
return Math.round(x / to) * to;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function snapPositionToGrid(position, gridSize) {
|
||||||
|
return {
|
||||||
|
x: roundTo(position.x, gridSize.x),
|
||||||
|
y: roundTo(position.y, gridSize.y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
18
src/icons/GridOffIcon.js
Normal file
18
src/icons/GridOffIcon.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function GridOnIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
fill="currentcolor"
|
||||||
|
>
|
||||||
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||||
|
<path d="M8 4v.89l2 2V4h4v4h-2.89l2 2H14v.89l2 2V10h4v4h-2.89l2 2H20v.89l2 2V4c0-1.1-.9-2-2-2H5.11l2 2H8zm8 0h3c.55 0 1 .45 1 1v3h-4V4zm6.16 17.88L2.12 1.84c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L2 4.55V20c0 1.1.9 2 2 2h15.45l1.3 1.3c.39.39 1.02.39 1.41 0 .39-.39.39-1.03 0-1.42zM10 12.55L11.45 14H10v-1.45zm-6-6L5.45 8H4V6.55zM8 20H5c-.55 0-1-.45-1-1v-3h4v4zm0-6H4v-4h3.45l.55.55V14zm6 6h-4v-4h3.45l.55.55V20zm2 0v-1.45L17.45 20H16z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GridOnIcon;
|
18
src/icons/GridOnIcon.js
Normal file
18
src/icons/GridOnIcon.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function GridOnIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
fill="currentcolor"
|
||||||
|
>
|
||||||
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||||
|
<path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H5c-.55 0-1-.45-1-1v-3h4v4zm0-6H4v-4h4v4zm0-6H4V5c0-.55.45-1 1-1h3v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm5 12h-3v-4h4v3c0 .55-.45 1-1 1zm1-6h-4v-4h4v4zm0-6h-4V4h3c.55 0 1 .45 1 1v3z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GridOnIcon;
|
Loading…
Reference in New Issue
Block a user