Files
grungnet/src/components/map/MapDrawing.js

263 lines
6.6 KiB
JavaScript
Raw Normal View History

2020-05-22 13:47:11 +10:00
import React, { useContext, useEffect, useState } from "react";
import shortid from "shortid";
2020-05-22 13:47:11 +10:00
import { Group, Line, Rect, Circle } from "react-konva";
import MapInteractionContext from "../../contexts/MapInteractionContext";
2020-04-18 18:11:21 +10:00
2020-04-28 17:04:31 +10:00
import { compare as comparePoints } from "../../helpers/vector2";
import {
getBrushPositionForTool,
getDefaultShapeData,
getUpdatedShapeData,
2020-04-28 17:04:31 +10:00
simplifyPoints,
2020-05-22 13:47:11 +10:00
getStrokeWidth,
} from "../../helpers/drawing";
2020-05-22 13:47:11 +10:00
import colors from "../../helpers/colors";
function MapDrawing({
shapes,
onShapeAdd,
onShapeRemove,
2020-05-22 13:47:11 +10:00
selectedToolId,
selectedToolSettings,
2020-04-20 15:17:56 +10:00
gridSize,
}) {
2020-05-22 13:47:11 +10:00
const {
stageDragState,
mapDragPosition,
stageScale,
mapWidth,
mapHeight,
} = useContext(MapInteractionContext);
const [drawingShape, setDrawingShape] = useState(null);
2020-04-19 17:39:26 +10:00
2020-05-22 13:47:11 +10:00
const shouldHover = selectedToolId === "erase";
const isEditing =
2020-05-22 13:47:11 +10:00
selectedToolId === "brush" ||
selectedToolId === "shape" ||
selectedToolId === "erase";
2020-04-19 17:39:26 +10:00
useEffect(() => {
if (!isEditing) {
return;
}
2020-04-18 18:11:21 +10:00
2020-05-22 13:47:11 +10:00
function startShape() {
const brushPosition = getBrushPositionForTool(
mapDragPosition,
selectedToolId,
selectedToolSettings,
gridSize,
shapes
);
2020-05-22 13:47:11 +10:00
const commonShapeData = {
color: selectedToolSettings && selectedToolSettings.color,
blend: selectedToolSettings && selectedToolSettings.useBlending,
id: shortid.generate(),
};
if (selectedToolId === "brush") {
setDrawingShape({
type: "path",
pathType: selectedToolSettings.type,
data: { points: [brushPosition] },
strokeWidth: selectedToolSettings.type === "stroke" ? 1 : 0,
...commonShapeData,
});
} else if (selectedToolId === "shape") {
setDrawingShape({
type: "shape",
shapeType: selectedToolSettings.type,
data: getDefaultShapeData(selectedToolSettings.type, brushPosition),
strokeWidth: 0,
...commonShapeData,
});
}
2020-04-19 00:24:06 +10:00
}
2020-05-22 13:47:11 +10:00
function continueShape() {
const brushPosition = getBrushPositionForTool(
2020-05-22 13:47:11 +10:00
mapDragPosition,
selectedToolId,
selectedToolSettings,
gridSize,
shapes
);
2020-05-22 13:47:11 +10:00
if (selectedToolId === "brush") {
setDrawingShape((prevShape) => {
const prevPoints = prevShape.data.points;
2020-04-28 17:04:31 +10:00
if (
comparePoints(
prevPoints[prevPoints.length - 1],
brushPosition,
0.001
)
) {
return prevShape;
}
2020-04-28 17:04:31 +10:00
const simplified = simplifyPoints(
[...prevPoints, brushPosition],
gridSize,
2020-05-22 13:47:11 +10:00
stageScale
);
return {
...prevShape,
data: { points: simplified },
};
});
2020-05-22 13:47:11 +10:00
} else if (selectedToolId === "shape") {
setDrawingShape((prevShape) => ({
...prevShape,
data: getUpdatedShapeData(
prevShape.shapeType,
prevShape.data,
brushPosition,
gridSize
),
}));
}
2020-04-18 18:11:21 +10:00
}
2020-05-22 13:47:11 +10:00
function endShape() {
if (selectedToolId === "brush" && drawingShape) {
if (drawingShape.data.points.length > 1) {
onShapeAdd(drawingShape);
}
} else if (selectedToolId === "shape" && drawingShape) {
onShapeAdd(drawingShape);
2020-04-19 17:39:26 +10:00
}
2020-05-22 13:47:11 +10:00
setDrawingShape(null);
}
2020-05-22 13:47:11 +10:00
switch (stageDragState) {
case "first":
startShape();
return;
case "dragging":
continueShape();
return;
case "last":
endShape();
return;
default:
return;
2020-04-19 00:24:06 +10:00
}
2020-05-22 13:47:11 +10:00
}, [
stageDragState,
mapDragPosition,
selectedToolId,
selectedToolSettings,
isEditing,
gridSize,
stageScale,
onShapeAdd,
shapes,
drawingShape,
]);
2020-05-22 13:47:11 +10:00
function handleShapeClick(_, shape) {
if (selectedToolId === "erase") {
onShapeRemove(shape.id);
}
}
2020-05-22 13:47:11 +10:00
function handleShapeMouseOver(event, shape) {
if (shouldHover) {
const path = event.target;
const hoverColor = "#BB99FF";
path.fill(hoverColor);
if (shape.type === "path") {
path.stroke(hoverColor);
2020-04-19 00:24:06 +10:00
}
2020-05-22 13:47:11 +10:00
path.getLayer().draw();
}
}
function handleShapeMouseOut(event, shape) {
if (shouldHover) {
const path = event.target;
const color = colors[shape.color] || shape.color;
path.fill(color);
if (shape.type === "path") {
path.stroke(color);
}
2020-05-22 13:47:11 +10:00
path.getLayer().draw();
}
}
function renderShape(shape) {
const defaultProps = {
key: shape.id,
onMouseOver: (e) => handleShapeMouseOver(e, shape),
onMouseOut: (e) => handleShapeMouseOut(e, shape),
onClick: (e) => handleShapeClick(e, shape),
fill: colors[shape.color] || shape.color,
opacity: shape.blend ? 0.5 : 1,
};
if (shape.type === "path") {
return (
<Line
points={shape.data.points.reduce(
(acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight],
[]
)}
stroke={colors[shape.color] || shape.color}
tension={0.5}
closed={shape.pathType === "fill"}
fillEnabled={shape.pathType === "fill"}
lineCap="round"
strokeWidth={getStrokeWidth(
shape.strokeWidth,
gridSize,
mapWidth,
mapHeight
)}
{...defaultProps}
/>
);
} else if (shape.type === "shape") {
if (shape.shapeType === "rectangle") {
return (
<Rect
x={shape.data.x * mapWidth}
y={shape.data.y * mapHeight}
width={shape.data.width * mapWidth}
height={shape.data.height * mapHeight}
{...defaultProps}
/>
);
} else if (shape.shapeType === "circle") {
const minSide = mapWidth < mapHeight ? mapWidth : mapHeight;
return (
<Circle
x={shape.data.x * mapWidth}
y={shape.data.y * mapHeight}
radius={shape.data.radius * minSide}
{...defaultProps}
/>
);
} else if (shape.shapeType === "triangle") {
return (
<Line
points={shape.data.points.reduce(
(acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight],
[]
)}
closed={true}
{...defaultProps}
/>
);
}
}
2020-05-22 13:47:11 +10:00
}
2020-04-18 18:11:21 +10:00
return (
2020-05-22 13:47:11 +10:00
<Group>
{shapes.map(renderShape)}
{drawingShape && renderShape(drawingShape)}
</Group>
2020-04-18 18:11:21 +10:00
);
}
export default MapDrawing;