2020-05-25 15:07:12 +10:00
|
|
|
import React, { useContext, useState, useCallback } from "react";
|
2020-04-28 22:05:47 +10:00
|
|
|
import shortid from "shortid";
|
2020-05-22 17:22:32 +10:00
|
|
|
import { Group, Line } from "react-konva";
|
|
|
|
|
import useImage from "use-image";
|
|
|
|
|
|
|
|
|
|
import diagonalPattern from "../../images/DiagonalPattern.png";
|
|
|
|
|
|
|
|
|
|
import MapInteractionContext from "../../contexts/MapInteractionContext";
|
2020-04-28 22:05:47 +10:00
|
|
|
|
|
|
|
|
import { compare as comparePoints } from "../../helpers/vector2";
|
|
|
|
|
import {
|
|
|
|
|
getBrushPositionForTool,
|
|
|
|
|
simplifyPoints,
|
2020-05-22 17:22:32 +10:00
|
|
|
getStrokeWidth,
|
2020-04-28 22:05:47 +10:00
|
|
|
} from "../../helpers/drawing";
|
|
|
|
|
|
2020-05-22 17:22:32 +10:00
|
|
|
import colors from "../../helpers/colors";
|
2020-05-25 15:07:12 +10:00
|
|
|
import useMapBrush from "../../helpers/useMapBrush";
|
2020-04-29 20:40:34 +10:00
|
|
|
|
2020-04-28 22:05:47 +10:00
|
|
|
function MapFog({
|
|
|
|
|
shapes,
|
|
|
|
|
onShapeAdd,
|
2020-05-31 12:12:16 +10:00
|
|
|
onShapesRemove,
|
|
|
|
|
onShapesEdit,
|
2020-05-22 17:22:32 +10:00
|
|
|
selectedToolId,
|
|
|
|
|
selectedToolSettings,
|
2020-04-28 22:05:47 +10:00
|
|
|
gridSize,
|
|
|
|
|
}) {
|
2020-05-25 15:07:12 +10:00
|
|
|
const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext);
|
2020-04-28 22:05:47 +10:00
|
|
|
const [drawingShape, setDrawingShape] = useState(null);
|
2020-05-31 12:12:16 +10:00
|
|
|
const [isBrushDown, setIsBrushDown] = useState(false);
|
|
|
|
|
const [editingShapes, setEditingShapes] = useState([]);
|
2020-04-28 22:05:47 +10:00
|
|
|
|
2020-05-22 17:22:32 +10:00
|
|
|
const isEditing = selectedToolId === "fog";
|
2020-04-28 22:05:47 +10:00
|
|
|
const shouldHover =
|
|
|
|
|
isEditing &&
|
2020-05-22 17:22:32 +10:00
|
|
|
(selectedToolSettings.type === "toggle" ||
|
|
|
|
|
selectedToolSettings.type === "remove");
|
2020-04-28 22:05:47 +10:00
|
|
|
|
2020-05-22 17:22:32 +10:00
|
|
|
const [patternImage] = useImage(diagonalPattern);
|
2020-04-29 18:21:44 +10:00
|
|
|
|
2020-05-31 12:12:16 +10:00
|
|
|
const handleBrushUp = useCallback(() => {
|
|
|
|
|
setIsBrushDown(false);
|
|
|
|
|
if (editingShapes.length > 0) {
|
|
|
|
|
if (selectedToolSettings.type === "remove") {
|
|
|
|
|
onShapesRemove(editingShapes.map((shape) => shape.id));
|
|
|
|
|
} else if (selectedToolSettings.type === "toggle") {
|
|
|
|
|
onShapesEdit(
|
|
|
|
|
editingShapes.map((shape) => ({ ...shape, visible: !shape.visible }))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
setEditingShapes([]);
|
|
|
|
|
}
|
|
|
|
|
}, [editingShapes, onShapesRemove, onShapesEdit, selectedToolSettings]);
|
|
|
|
|
|
2020-05-25 15:07:12 +10:00
|
|
|
const handleShapeDraw = useCallback(
|
|
|
|
|
(brushState, mapBrushPosition) => {
|
|
|
|
|
function startShape() {
|
|
|
|
|
const brushPosition = getBrushPositionForTool(
|
|
|
|
|
mapBrushPosition,
|
|
|
|
|
selectedToolId,
|
|
|
|
|
selectedToolSettings,
|
|
|
|
|
gridSize,
|
|
|
|
|
shapes
|
|
|
|
|
);
|
|
|
|
|
if (selectedToolSettings.type === "add") {
|
|
|
|
|
setDrawingShape({
|
|
|
|
|
type: "fog",
|
|
|
|
|
data: { points: [brushPosition] },
|
|
|
|
|
strokeWidth: 0.5,
|
|
|
|
|
color: "black",
|
|
|
|
|
blend: false,
|
|
|
|
|
id: shortid.generate(),
|
|
|
|
|
visible: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-05-31 12:12:16 +10:00
|
|
|
setIsBrushDown(true);
|
2020-05-22 17:22:32 +10:00
|
|
|
}
|
|
|
|
|
|
2020-05-25 15:07:12 +10:00
|
|
|
function continueShape() {
|
|
|
|
|
const brushPosition = getBrushPositionForTool(
|
|
|
|
|
mapBrushPosition,
|
|
|
|
|
selectedToolId,
|
|
|
|
|
selectedToolSettings,
|
|
|
|
|
gridSize,
|
|
|
|
|
shapes
|
|
|
|
|
);
|
|
|
|
|
if (selectedToolSettings.type === "add") {
|
|
|
|
|
setDrawingShape((prevShape) => {
|
|
|
|
|
const prevPoints = prevShape.data.points;
|
|
|
|
|
if (
|
|
|
|
|
comparePoints(
|
|
|
|
|
prevPoints[prevPoints.length - 1],
|
|
|
|
|
brushPosition,
|
|
|
|
|
0.001
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
return prevShape;
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
...prevShape,
|
|
|
|
|
data: { points: [...prevPoints, brushPosition] },
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-04-28 22:05:47 +10:00
|
|
|
}
|
|
|
|
|
|
2020-05-25 15:07:12 +10:00
|
|
|
function endShape() {
|
|
|
|
|
if (selectedToolSettings.type === "add" && drawingShape) {
|
|
|
|
|
if (drawingShape.data.points.length > 1) {
|
|
|
|
|
const shape = {
|
|
|
|
|
...drawingShape,
|
|
|
|
|
data: {
|
|
|
|
|
points: simplifyPoints(
|
|
|
|
|
drawingShape.data.points,
|
|
|
|
|
gridSize,
|
|
|
|
|
// Downscale fog as smoothing doesn't currently work with edge snapping
|
|
|
|
|
stageScale / 2
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
onShapeAdd(shape);
|
|
|
|
|
}
|
2020-05-22 17:22:32 +10:00
|
|
|
}
|
2020-05-25 15:07:12 +10:00
|
|
|
setDrawingShape(null);
|
2020-05-31 12:12:16 +10:00
|
|
|
handleBrushUp();
|
2020-05-22 17:22:32 +10:00
|
|
|
}
|
|
|
|
|
|
2020-05-25 15:07:12 +10:00
|
|
|
switch (brushState) {
|
|
|
|
|
case "first":
|
|
|
|
|
startShape();
|
|
|
|
|
return;
|
|
|
|
|
case "drawing":
|
|
|
|
|
continueShape();
|
|
|
|
|
return;
|
|
|
|
|
case "last":
|
|
|
|
|
endShape();
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[
|
|
|
|
|
selectedToolId,
|
|
|
|
|
selectedToolSettings,
|
|
|
|
|
gridSize,
|
|
|
|
|
stageScale,
|
|
|
|
|
onShapeAdd,
|
|
|
|
|
shapes,
|
|
|
|
|
drawingShape,
|
2020-05-31 12:12:16 +10:00
|
|
|
handleBrushUp,
|
2020-05-25 15:07:12 +10:00
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
useMapBrush(isEditing, handleShapeDraw);
|
2020-05-22 17:22:32 +10:00
|
|
|
|
2020-05-31 12:12:16 +10:00
|
|
|
function handleShapeOver(shape, isDown) {
|
|
|
|
|
if (shouldHover && isDown) {
|
|
|
|
|
if (editingShapes.findIndex((s) => s.id === shape.id) === -1) {
|
|
|
|
|
setEditingShapes((prevShapes) => [...prevShapes, shape]);
|
2020-04-29 20:40:34 +10:00
|
|
|
}
|
2020-04-28 22:05:47 +10:00
|
|
|
}
|
2020-05-22 17:22:32 +10:00
|
|
|
}
|
2020-04-28 22:05:47 +10:00
|
|
|
|
2020-05-22 17:22:32 +10:00
|
|
|
function renderShape(shape) {
|
|
|
|
|
return (
|
|
|
|
|
<Line
|
|
|
|
|
key={shape.id}
|
2020-05-31 12:12:16 +10:00
|
|
|
onMouseMove={() => handleShapeOver(shape, isBrushDown)}
|
|
|
|
|
onTouchOver={() => handleShapeOver(shape, isBrushDown)}
|
|
|
|
|
onMouseDown={() => handleShapeOver(shape, true)}
|
|
|
|
|
onTouchStart={() => handleShapeOver(shape, true)}
|
2020-05-22 17:22:32 +10:00
|
|
|
points={shape.data.points.reduce(
|
|
|
|
|
(acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight],
|
|
|
|
|
[]
|
|
|
|
|
)}
|
|
|
|
|
stroke={colors[shape.color] || shape.color}
|
|
|
|
|
fill={colors[shape.color] || shape.color}
|
|
|
|
|
closed
|
|
|
|
|
lineCap="round"
|
|
|
|
|
strokeWidth={getStrokeWidth(
|
|
|
|
|
shape.strokeWidth,
|
|
|
|
|
gridSize,
|
|
|
|
|
mapWidth,
|
|
|
|
|
mapHeight
|
|
|
|
|
)}
|
|
|
|
|
visible={isEditing || shape.visible}
|
|
|
|
|
opacity={isEditing ? 0.5 : 1}
|
|
|
|
|
fillPatternImage={patternImage}
|
|
|
|
|
fillPriority={isEditing && !shape.visible ? "pattern" : "color"}
|
2020-04-28 22:05:47 +10:00
|
|
|
/>
|
2020-05-22 17:22:32 +10:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-31 12:12:16 +10:00
|
|
|
function renderEditingShape(shape) {
|
|
|
|
|
const editingShape = {
|
|
|
|
|
...shape,
|
|
|
|
|
color: "#BB99FF",
|
|
|
|
|
};
|
|
|
|
|
return renderShape(editingShape);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 17:22:32 +10:00
|
|
|
return (
|
|
|
|
|
<Group>
|
|
|
|
|
{shapes.map(renderShape)}
|
|
|
|
|
{drawingShape && renderShape(drawingShape)}
|
2020-05-31 12:12:16 +10:00
|
|
|
{editingShapes.length > 0 && editingShapes.map(renderEditingShape)}
|
2020-05-22 17:22:32 +10:00
|
|
|
</Group>
|
2020-04-28 22:05:47 +10:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default MapFog;
|