From 9eaece24e544de6567872c9de536bdd00355341e Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Tue, 9 Jun 2020 12:45:52 +1000 Subject: [PATCH 01/50] Added fog subtraction tool --- package.json | 1 + src/components/map/Map.js | 32 +++++++++++++++++++ src/components/map/MapFog.js | 31 ++++++++++++++++-- .../map/controls/FogToolSettings.js | 8 +++++ src/icons/FogRemoveIcon.js | 2 +- src/icons/FogSubtractIcon.js | 18 +++++++++++ yarn.lock | 12 +++++++ 7 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 src/icons/FogSubtractIcon.js diff --git a/package.json b/package.json index 3939b41..54fbc10 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "interactjs": "^1.9.7", "konva": "^6.0.0", "normalize-wheel": "^1.0.1", + "polygon-clipping": "^0.14.3", "raw.macro": "^0.3.0", "react": "^16.13.0", "react-dom": "^16.13.0", diff --git a/src/components/map/Map.js b/src/components/map/Map.js index 38b54dc..a22a170 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -1,4 +1,5 @@ import React, { useState, useContext, useEffect } from "react"; +import polygonClipping from "polygon-clipping"; import MapControls from "./MapControls"; import MapInteraction from "./MapInteraction"; @@ -108,6 +109,10 @@ function Map({ onFogDraw({ type: "add", shapes: [shape] }); } + function handleFogShapeSubtract(shape) { + onFogDraw({ type: "subtract", shapes: [shape] }); + } + function handleFogShapesRemove(shapeIds) { onFogDraw({ type: "remove", shapeIds }); } @@ -133,6 +138,32 @@ function Map({ if (action.type === "remove") { shapesById = omit(shapesById, action.shapeIds); } + if (action.type === "subtract") { + const actionGeom = action.shapes.map((actionShape) => [ + actionShape.data.points.map(({ x, y }) => [x, y]), + ]); + let subtractedShapes = {}; + for (let shape of Object.values(shapesById)) { + let shapeGeom = [[shape.data.points.map(({ x, y }) => [x, y])]]; + const difference = polygonClipping.difference( + shapeGeom, + actionGeom + ); + for (let i = 0; i < difference.length; i++) { + for (let j = 0; j < difference[i].length; j++) { + let newId = `${shape.id}-${i}_${j}`; + subtractedShapes[newId] = { + ...shape, + id: newId, + data: { + points: difference[i][j].map(([x, y]) => ({ x, y })), + }, + }; + } + } + } + shapesById = subtractedShapes; + } } return Object.values(shapesById); } @@ -287,6 +318,7 @@ function Map({ { const prevPoints = prevShape.data.points; if ( @@ -122,6 +129,23 @@ function MapFog({ onShapeAdd(shape); } } + if (selectedToolSettings.type === "subtract" && drawingShape) { + if (drawingShape.data.points.length > 1) { + const shape = { + data: { + points: simplifyPoints( + drawingShape.data.points, + gridSize, + // Downscale fog as smoothing doesn't currently work with edge snapping + stageScale / 2 + ), + }, + id: drawingShape.id, + type: drawingShape.type, + }; + onShapeSubtract(shape); + } + } setDrawingShape(null); handleBrushUp(); } @@ -146,6 +170,7 @@ function MapFog({ gridSize, stageScale, onShapeAdd, + onShapeSubtract, shapes, drawingShape, handleBrushUp, diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index 96e22e2..bf9539c 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -6,6 +6,7 @@ import RadioIconButton from "./RadioIconButton"; import GridSnappingToggle from "./GridSnappingToggle"; import FogAddIcon from "../../../icons/FogAddIcon"; +import FogSubtractIcon from "../../../icons/FogSubtractIcon"; import FogRemoveIcon from "../../../icons/FogRemoveIcon"; import FogToggleIcon from "../../../icons/FogToggleIcon"; @@ -29,6 +30,13 @@ function BrushToolSettings({ > + onSettingChange({ type: "subtract" })} + isSelected={settings.type === "subtract"} + > + + onSettingChange({ type: "remove" })} diff --git a/src/icons/FogRemoveIcon.js b/src/icons/FogRemoveIcon.js index af6c2e6..ec1183d 100644 --- a/src/icons/FogRemoveIcon.js +++ b/src/icons/FogRemoveIcon.js @@ -10,7 +10,7 @@ function FogRemoveIcon() { fill="currentcolor" > - + ); } diff --git a/src/icons/FogSubtractIcon.js b/src/icons/FogSubtractIcon.js new file mode 100644 index 0000000..af6c2e6 --- /dev/null +++ b/src/icons/FogSubtractIcon.js @@ -0,0 +1,18 @@ +import React from "react"; + +function FogRemoveIcon() { + return ( + + + + + ); +} + +export default FogRemoveIcon; diff --git a/yarn.lock b/yarn.lock index df494c8..681a016 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8251,6 +8251,13 @@ pnp-webpack-plugin@1.6.0: dependencies: ts-pnp "^1.1.2" +polygon-clipping@^0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/polygon-clipping/-/polygon-clipping-0.14.3.tgz#02affe4e2aaee69f686ea9dcd5f9566dd4c941af" + integrity sha512-bIaMFYIsHOShMN0JZsvkfk66S7gKMQMlYUpV7LIx+WOBYvJT09eCgoAv2JCDNj6SofA4+o5tlmV76zzUiG4aBQ== + dependencies: + splaytree "^3.0.1" + portfinder@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" @@ -10433,6 +10440,11 @@ spdy@^4.0.1: select-hose "^2.0.0" spdy-transport "^3.0.0" +splaytree@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/splaytree/-/splaytree-3.0.1.tgz#038d8e6d597a3a1893e8e82693a0f4413b3559f7" + integrity sha512-WvQIHRDXLSVn72xjlIG/WGhv/4QO3m+iY2TpVdFRaXd1+/vkNlpkpw1QaNH5taghF9eWXDHWMWnzXyicR8d6ig== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" From f4d71cd6bfeb3d0b5c0aff71446fd511ade44074 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 14 Jun 2020 12:27:05 +1000 Subject: [PATCH 02/50] Add support for holes in fog subtraction --- src/components/map/Map.js | 31 ++++++---- src/components/map/MapFog.js | 29 +++++++--- src/helpers/konva.js | 107 +++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 src/helpers/konva.js diff --git a/src/components/map/Map.js b/src/components/map/Map.js index a22a170..35b11f7 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -144,22 +144,33 @@ function Map({ ]); let subtractedShapes = {}; for (let shape of Object.values(shapesById)) { - let shapeGeom = [[shape.data.points.map(({ x, y }) => [x, y])]]; + const shapePoints = shape.data.points.map(({ x, y }) => [x, y]); + const shapeHoles = shape.data.holes.map((hole) => + hole.map(({ x, y }) => [x, y]) + ); + let shapeGeom = [[shapePoints, ...shapeHoles]]; const difference = polygonClipping.difference( shapeGeom, actionGeom ); for (let i = 0; i < difference.length; i++) { - for (let j = 0; j < difference[i].length; j++) { - let newId = `${shape.id}-${i}_${j}`; - subtractedShapes[newId] = { - ...shape, - id: newId, - data: { - points: difference[i][j].map(([x, y]) => ({ x, y })), - }, - }; + let newId = difference.length > 1 ? `${shape.id}-${i}` : shape.id; + // Holes detected + let holes = []; + if (difference[i].length > 1) { + for (let j = 1; j < difference[i].length; j++) { + holes.push(difference[i][j].map(([x, y]) => ({ x, y }))); + } } + + subtractedShapes[newId] = { + ...shape, + id: newId, + data: { + points: difference[i][0].map(([x, y]) => ({ x, y })), + holes, + }, + }; } } shapesById = subtractedShapes; diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index 66569e2..30c24a5 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -1,6 +1,6 @@ import React, { useContext, useState, useCallback } from "react"; import shortid from "shortid"; -import { Group, Line } from "react-konva"; +import { Group } from "react-konva"; import useImage from "use-image"; import diagonalPattern from "../../images/DiagonalPattern.png"; @@ -13,9 +13,9 @@ import { simplifyPoints, getStrokeWidth, } from "../../helpers/drawing"; - import colors from "../../helpers/colors"; import useMapBrush from "../../helpers/useMapBrush"; +import { HoleyLine } from "../../helpers/konva"; function MapFog({ shapes, @@ -70,7 +70,7 @@ function MapFog({ ) { setDrawingShape({ type: "fog", - data: { points: [brushPosition] }, + data: { points: [brushPosition], holes: [] }, strokeWidth: 0.5, color: selectedToolSettings.type === "add" ? "black" : "red", blend: false, @@ -106,7 +106,10 @@ function MapFog({ } return { ...prevShape, - data: { points: [...prevPoints, brushPosition] }, + data: { + ...prevShape.data, + points: [...prevPoints, brushPosition], + }, }; }); } @@ -118,6 +121,7 @@ function MapFog({ const shape = { ...drawingShape, data: { + ...drawingShape.data, points: simplifyPoints( drawingShape.data.points, gridSize, @@ -133,6 +137,7 @@ function MapFog({ if (drawingShape.data.points.length > 1) { const shape = { data: { + ...drawingShape.data, points: simplifyPoints( drawingShape.data.points, gridSize, @@ -187,18 +192,23 @@ function MapFog({ } } + function reducePoints(acc, point) { + return [...acc, point.x * mapWidth, point.y * mapHeight]; + } + function renderShape(shape) { + const points = shape.data.points.reduce(reducePoints, []); + const holes = + shape.data.holes && + shape.data.holes.map((hole) => hole.reduce(reducePoints, [])); return ( - handleShapeOver(shape, isBrushDown)} onTouchOver={() => handleShapeOver(shape, isBrushDown)} onMouseDown={() => handleShapeOver(shape, true)} onTouchStart={() => handleShapeOver(shape, true)} - points={shape.data.points.reduce( - (acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight], - [] - )} + points={points} stroke={colors[shape.color] || shape.color} fill={colors[shape.color] || shape.color} closed @@ -213,6 +223,7 @@ function MapFog({ opacity={isEditing ? 0.5 : 1} fillPatternImage={patternImage} fillPriority={isEditing && !shape.visible ? "pattern" : "color"} + holes={holes} /> ); } diff --git a/src/helpers/konva.js b/src/helpers/konva.js new file mode 100644 index 0000000..d424eb1 --- /dev/null +++ b/src/helpers/konva.js @@ -0,0 +1,107 @@ +import React from "react"; +import { Line } from "react-konva"; + +// Holes should be wound in the opposite direction as the containing points array +export function HoleyLine({ holes, ...props }) { + // Converted from https://github.com/rfestag/konva/blob/master/src/shapes/Line.ts + function drawLine(points, context, shape) { + const length = points.length; + const tension = shape.tension(); + const closed = shape.closed(); + const bezier = shape.bezier(); + + if (!length) { + return; + } + + context.moveTo(points[0], points[1]); + + if (tension !== 0 && length > 4) { + const tensionPoints = shape.getTensionPoints(); + const tensionLength = tensionPoints.length; + let n = closed ? 0 : 4; + + if (!closed) { + context.quadraticCurveTo( + tensionPoints[0], + tensionPoints[1], + tensionPoints[2], + tensionPoints[3] + ); + } + + while (n < tensionLength - 2) { + context.bezierCurveTo( + tensionPoints[n++], + tensionPoints[n++], + tensionPoints[n++], + tensionPoints[n++], + tensionPoints[n++], + tensionPoints[n++] + ); + } + + if (!closed) { + context.quadraticCurveTo( + tensionPoints[tensionLength - 2], + tensionPoints[tensionLength - 1], + points[length - 2], + points[length - 1] + ); + } + } else if (bezier) { + // no tension but bezier + let n = 2; + + while (n < length) { + context.bezierCurveTo( + points[n++], + points[n++], + points[n++], + points[n++], + points[n++], + points[n++] + ); + } + } else { + // no tension + for (let n = 2; n < length; n += 2) { + context.lineTo(points[n], points[n + 1]); + } + } + } + + // Draw points and holes + function sceneFunc(context, shape) { + const points = shape.points(); + const closed = shape.closed(); + + if (!points.length) { + return; + } + + context.beginPath(); + console.log(); + drawLine(points, context, shape); + + context.beginPath(); + drawLine(points, context, shape); + + // closed e.g. polygons and blobs + if (closed) { + context.closePath(); + if (holes && holes.length) { + for (let hole of holes) { + drawLine(hole, context, shape); + context.closePath(); + } + } + context.fillStrokeShape(shape); + } else { + // open e.g. lines and splines + context.strokeShape(shape); + } + } + + return ; +} From 5a93d9a5269ff574992ef55bcb2f8318f583a01d Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Fri, 19 Jun 2020 13:45:21 +1000 Subject: [PATCH 03/50] Fixed map fog draw undo not being sent to other players --- src/routes/Game.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/Game.js b/src/routes/Game.js index 6f2de46..6c1a2d3 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -145,7 +145,7 @@ function Game() { }); } - function updateDrawActionIndex(change, indexKey, actionsKey, peerId) { + function updateDrawActionIndex(change, indexKey, actionsKey) { const newIndex = Math.min( Math.max(currentMapState[indexKey] + change, -1), currentMapState[actionsKey].length - 1 @@ -201,7 +201,7 @@ function Game() { "fogDrawActions" ); for (let peer of Object.values(peers)) { - peer.connection.send({ id: "fogDrawIndex", data: index }); + peer.connection.send({ id: "mapFogIndex", data: index }); } } @@ -212,7 +212,7 @@ function Game() { "fogDrawActions" ); for (let peer of Object.values(peers)) { - peer.connection.send({ id: "fogDrawIndex", data: index }); + peer.connection.send({ id: "mapFogIndex", data: index }); } } From aa4ba33a0baeda72d6a2893e1b6927b62f22dc93 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Fri, 19 Jun 2020 18:04:58 +1000 Subject: [PATCH 04/50] Added fog polygon tool and changed fog interaction method --- src/components/map/Map.js | 2 +- src/components/map/MapDrawing.js | 1 + src/components/map/MapFog.js | 328 +++++++++++------- src/components/map/MapInteraction.js | 16 +- .../map/controls/FogSubtractToggle.js | 19 + .../map/controls/FogToolSettings.js | 64 ++-- src/helpers/konva.js | 15 + src/icons/EraseToolIcon.js | 11 +- src/icons/FogBrushIcon.js | 18 + src/icons/FogPolygonIcon.js | 18 + src/icons/FogRemoveIcon.js | 2 +- 11 files changed, 319 insertions(+), 175 deletions(-) create mode 100644 src/components/map/controls/FogSubtractToggle.js create mode 100644 src/icons/FogBrushIcon.js create mode 100644 src/icons/FogPolygonIcon.js diff --git a/src/components/map/Map.js b/src/components/map/Map.js index 35b11f7..28e07a6 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -48,7 +48,7 @@ function Map({ const [selectedToolId, setSelectedToolId] = useState("pan"); const [toolSettings, setToolSettings] = useState({ - fog: { type: "add", useEdgeSnapping: true, useGridSnapping: false }, + fog: { type: "polygon", useEdgeSnapping: false, useFogCut: false }, brush: { color: "darkGray", type: "stroke", diff --git a/src/components/map/MapDrawing.js b/src/components/map/MapDrawing.js index 7901f1a..3974981 100644 --- a/src/components/map/MapDrawing.js +++ b/src/components/map/MapDrawing.js @@ -159,6 +159,7 @@ function MapDrawing({ ] ); + // Move away from this as it is too slow to respond useMapBrush(isEditing, handleShapeDraw); function handleShapeOver(shape, isDown) { diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index 30c24a5..cace424 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -1,4 +1,4 @@ -import React, { useContext, useState, useCallback } from "react"; +import React, { useContext, useState, useEffect } from "react"; import shortid from "shortid"; import { Group } from "react-konva"; import useImage from "use-image"; @@ -6,6 +6,7 @@ import useImage from "use-image"; import diagonalPattern from "../../images/DiagonalPattern.png"; import MapInteractionContext from "../../contexts/MapInteractionContext"; +import MapStageContext from "../../contexts/MapStageContext"; import { compare as comparePoints } from "../../helpers/vector2"; import { @@ -14,8 +15,10 @@ import { getStrokeWidth, } from "../../helpers/drawing"; import colors from "../../helpers/colors"; -import useMapBrush from "../../helpers/useMapBrush"; -import { HoleyLine } from "../../helpers/konva"; +import { + HoleyLine, + getRelativePointerPositionNormalized, +} from "../../helpers/konva"; function MapFog({ shapes, @@ -28,6 +31,7 @@ function MapFog({ gridSize, }) { const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext); + const mapStageRef = useContext(MapStageContext); const [drawingShape, setDrawingShape] = useState(null); const [isBrushDown, setIsBrushDown] = useState(false); const [editingShapes, setEditingShapes] = useState([]); @@ -40,149 +44,209 @@ function MapFog({ const [patternImage] = useImage(diagonalPattern); - 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([]); + useEffect(() => { + if (!isEditing) { + return; } - }, [editingShapes, onShapesRemove, onShapesEdit, selectedToolSettings]); - const handleShapeDraw = useCallback( - (brushState, mapBrushPosition) => { - function startShape() { - const brushPosition = getBrushPositionForTool( - mapBrushPosition, - selectedToolId, - selectedToolSettings, - gridSize, - shapes - ); - if ( - selectedToolSettings.type === "add" || - selectedToolSettings.type === "subtract" - ) { - setDrawingShape({ - type: "fog", - data: { points: [brushPosition], holes: [] }, - strokeWidth: 0.5, - color: selectedToolSettings.type === "add" ? "black" : "red", - blend: false, - id: shortid.generate(), - visible: true, - }); - } - setIsBrushDown(true); + const mapStage = mapStageRef.current; + + function getBrushPosition() { + const mapImage = mapStage.findOne("#mapImage"); + return getBrushPositionForTool( + getRelativePointerPositionNormalized(mapImage), + selectedToolId, + selectedToolSettings, + gridSize, + shapes + ); + } + + function handleBrushDown() { + const brushPosition = getBrushPosition(); + if (selectedToolSettings.type === "brush") { + setDrawingShape({ + type: "fog", + data: { + points: [brushPosition], + holes: [], + }, + strokeWidth: 0.5, + color: selectedToolSettings.useFogSubtract ? "red" : "black", + blend: false, + id: shortid.generate(), + visible: true, + }); } + setIsBrushDown(true); + } - function continueShape() { - const brushPosition = getBrushPositionForTool( - mapBrushPosition, - selectedToolId, - selectedToolSettings, - gridSize, - shapes - ); - if ( - selectedToolSettings.type === "add" || - selectedToolSettings.type === "subtract" - ) { - setDrawingShape((prevShape) => { - const prevPoints = prevShape.data.points; - if ( - comparePoints( - prevPoints[prevPoints.length - 1], - brushPosition, - 0.001 - ) - ) { - return prevShape; - } - return { - ...prevShape, - data: { - ...prevShape.data, - points: [...prevPoints, brushPosition], - }, - }; - }); - } + function handleBrushMove() { + if ( + selectedToolSettings.type === "brush" && + isBrushDown && + drawingShape + ) { + const brushPosition = getBrushPosition(); + setDrawingShape((prevShape) => { + const prevPoints = prevShape.data.points; + if ( + comparePoints( + prevPoints[prevPoints.length - 1], + brushPosition, + 0.001 + ) + ) { + return prevShape; + } + return { + ...prevShape, + data: { + ...prevShape.data, + points: [...prevPoints, brushPosition], + }, + }; + }); } + if (selectedToolSettings.type === "polygon" && drawingShape) { + const brushPosition = getBrushPosition(); + setDrawingShape((prevShape) => { + return { + ...prevShape, + data: { + ...prevShape.data, + points: [...prevShape.data.points.slice(0, -1), brushPosition], + }, + }; + }); + } + } - function endShape() { - if (selectedToolSettings.type === "add" && drawingShape) { - if (drawingShape.data.points.length > 1) { - const shape = { - ...drawingShape, - data: { - ...drawingShape.data, - points: simplifyPoints( - drawingShape.data.points, - gridSize, - // Downscale fog as smoothing doesn't currently work with edge snapping - stageScale / 2 - ), - }, - }; + function handleBrushUp() { + if (selectedToolSettings.type === "brush" && drawingShape) { + const subtract = selectedToolSettings.useFogSubtract; + + if (drawingShape.data.points.length > 1) { + let shapeData = {}; + if (subtract) { + shapeData = { id: drawingShape.id, type: drawingShape.type }; + } else { + shapeData = drawingShape; + } + const shape = { + ...shapeData, + data: { + ...drawingShape.data, + points: simplifyPoints( + drawingShape.data.points, + gridSize, + // Downscale fog as smoothing doesn't currently work with edge snapping + stageScale / 2 + ), + }, + }; + if (subtract) { + onShapeSubtract(shape); + } else { onShapeAdd(shape); } } - if (selectedToolSettings.type === "subtract" && drawingShape) { - if (drawingShape.data.points.length > 1) { - const shape = { - data: { - ...drawingShape.data, - points: simplifyPoints( - drawingShape.data.points, - gridSize, - // Downscale fog as smoothing doesn't currently work with edge snapping - stageScale / 2 - ), - }, - id: drawingShape.id, - type: drawingShape.type, - }; - onShapeSubtract(shape); - } - } + setDrawingShape(null); - handleBrushUp(); } - switch (brushState) { - case "first": - startShape(); - return; - case "drawing": - continueShape(); - return; - case "last": - endShape(); - return; - default: - return; + if (selectedToolSettings.type === "polygon") { + const brushPosition = getBrushPosition(); + setDrawingShape({ + type: "fog", + data: { + points: [ + ...(drawingShape ? drawingShape.data.points : [brushPosition]), + brushPosition, + ], + holes: [], + }, + strokeWidth: 0.5, + color: selectedToolSettings.useFogSubtract ? "red" : "black", + blend: false, + id: shortid.generate(), + visible: true, + }); } - }, - [ - selectedToolId, - selectedToolSettings, - gridSize, - stageScale, - onShapeAdd, - onShapeSubtract, - shapes, - drawingShape, - handleBrushUp, - ] - ); - useMapBrush(isEditing, handleShapeDraw); + 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([]); + } + + setIsBrushDown(false); + } + + function handleKeyDown(event) { + if ( + event.key === "Enter" && + selectedToolSettings.type === "polygon" && + drawingShape + ) { + const subtract = selectedToolSettings.useFogSubtract; + const data = { + ...drawingShape.data, + // Remove the last point as it hasn't been placed yet + points: drawingShape.data.points.slice(0, -1), + }; + if (subtract) { + onShapeSubtract({ + id: drawingShape.id, + type: drawingShape.type, + data: data, + }); + } else { + onShapeAdd({ ...drawingShape, data: data }); + } + + setDrawingShape(null); + } + if (event.key === "Escape" && drawingShape) { + setDrawingShape(null); + } + } + + mapStage.on("mousedown", handleBrushDown); + mapStage.on("mousemove", handleBrushMove); + mapStage.on("mouseup", handleBrushUp); + mapStage.container().addEventListener("keydown", handleKeyDown); + + return () => { + mapStage.off("mousedown", handleBrushDown); + mapStage.off("mousemove", handleBrushMove); + mapStage.off("mouseup", handleBrushUp); + mapStage.container().removeEventListener("keydown", handleKeyDown); + }; + }, [ + mapStageRef, + isEditing, + drawingShape, + editingShapes, + gridSize, + isBrushDown, + onShapeAdd, + onShapeSubtract, + onShapesEdit, + onShapesRemove, + selectedToolId, + selectedToolSettings, + shapes, + stageScale, + ]); function handleShapeOver(shape, isDown) { if (shouldHover && isDown) { diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js index 60e929a..6e20aa5 100644 --- a/src/components/map/MapInteraction.js +++ b/src/components/map/MapInteraction.js @@ -11,7 +11,9 @@ import useDataSource from "../../helpers/useDataSource"; import { mapSources as defaultMapSources } from "../../maps"; import { MapInteractionProvider } from "../../contexts/MapInteractionContext"; -import MapStageContext from "../../contexts/MapStageContext"; +import MapStageContext, { + MapStageProvider, +} from "../../contexts/MapStageContext"; import AuthContext from "../../contexts/AuthContext"; const wheelZoomSpeed = -0.001; @@ -201,6 +203,14 @@ function MapInteraction({ map, children, controls, selectedToolId }) { mapDragPositionRef, }; + // Enable keyboard interaction for map stage container + useEffect(() => { + const container = mapStageRef.current.container(); + container.tabIndex = 1; + container.style.outline = "none"; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( - {children} + + {children} + diff --git a/src/components/map/controls/FogSubtractToggle.js b/src/components/map/controls/FogSubtractToggle.js new file mode 100644 index 0000000..841ee57 --- /dev/null +++ b/src/components/map/controls/FogSubtractToggle.js @@ -0,0 +1,19 @@ +import React from "react"; +import { IconButton } from "theme-ui"; + +import FogAddIcon from "../../../icons/FogAddIcon"; +import FogSubtractIcon from "../../../icons/FogSubtractIcon"; + +function FogSubtractToggle({ useFogSubtract, onFogSubtractChange }) { + return ( + onFogSubtractChange(!useFogSubtract)} + > + {useFogSubtract ? : } + + ); +} + +export default FogSubtractToggle; diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index bf9539c..616fc5a 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -3,10 +3,10 @@ import { Flex } from "theme-ui"; import EdgeSnappingToggle from "./EdgeSnappingToggle"; import RadioIconButton from "./RadioIconButton"; -import GridSnappingToggle from "./GridSnappingToggle"; +import FogSubtractToggle from "./FogSubtractToggle"; -import FogAddIcon from "../../../icons/FogAddIcon"; -import FogSubtractIcon from "../../../icons/FogSubtractIcon"; +import FogBrushIcon from "../../../icons/FogBrushIcon"; +import FogPolygonIcon from "../../../icons/FogPolygonIcon"; import FogRemoveIcon from "../../../icons/FogRemoveIcon"; import FogToggleIcon from "../../../icons/FogToggleIcon"; @@ -24,18 +24,40 @@ function BrushToolSettings({ return ( onSettingChange({ type: "add" })} - isSelected={settings.type === "add"} + title="Fog Polygon" + onClick={() => onSettingChange({ type: "polygon" })} + isSelected={settings.type === "polygon"} > - + onSettingChange({ type: "subtract" })} - isSelected={settings.type === "subtract"} + title="Fog Brush" + onClick={() => onSettingChange({ type: "brush" })} + isSelected={settings.type === "brush"} > - + + + + + onSettingChange({ useFogSubtract }) + } + /> + {/* TODO: Re-enable edge snapping when holes are fixed */} + {/* + onSettingChange({ useEdgeSnapping }) + } + /> */} + + onSettingChange({ type: "toggle" })} + isSelected={settings.type === "toggle"} + > + - onSettingChange({ type: "toggle" })} - isSelected={settings.type === "toggle"} - > - - - - - onSettingChange({ useEdgeSnapping }) - } - /> - - onSettingChange({ useGridSnapping }) - } - /> onToolAction("fogUndo")} diff --git a/src/helpers/konva.js b/src/helpers/konva.js index d424eb1..dd799be 100644 --- a/src/helpers/konva.js +++ b/src/helpers/konva.js @@ -105,3 +105,18 @@ export function HoleyLine({ holes, ...props }) { return ; } + +export function getRelativePointerPosition(node) { + let transform = node.getAbsoluteTransform().copy(); + transform.invert(); + let posision = node.getStage().getPointerPosition(); + return transform.point(posision); +} + +export function getRelativePointerPositionNormalized(node) { + const relativePosition = getRelativePointerPosition(node); + return { + x: relativePosition.x / node.width(), + y: relativePosition.y / node.height(), + }; +} diff --git a/src/icons/EraseToolIcon.js b/src/icons/EraseToolIcon.js index 710ccbf..e15389e 100644 --- a/src/icons/EraseToolIcon.js +++ b/src/icons/EraseToolIcon.js @@ -7,15 +7,10 @@ function EraseToolIcon() { height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" + fill="currentcolor" > - - - - + + ); } diff --git a/src/icons/FogBrushIcon.js b/src/icons/FogBrushIcon.js new file mode 100644 index 0000000..827c10d --- /dev/null +++ b/src/icons/FogBrushIcon.js @@ -0,0 +1,18 @@ +import React from "react"; + +function FogBrushIcon() { + return ( + + + + + ); +} + +export default FogBrushIcon; diff --git a/src/icons/FogPolygonIcon.js b/src/icons/FogPolygonIcon.js new file mode 100644 index 0000000..cfd2bd1 --- /dev/null +++ b/src/icons/FogPolygonIcon.js @@ -0,0 +1,18 @@ +import React from "react"; + +function FogPolygonIcon() { + return ( + + + + + ); +} + +export default FogPolygonIcon; diff --git a/src/icons/FogRemoveIcon.js b/src/icons/FogRemoveIcon.js index ec1183d..9ad8330 100644 --- a/src/icons/FogRemoveIcon.js +++ b/src/icons/FogRemoveIcon.js @@ -10,7 +10,7 @@ function FogRemoveIcon() { fill="currentcolor" > - + ); } From 3ba7c8809ce285f884025cfc6495c4674e0a5109 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 21 Jun 2020 11:01:03 +1000 Subject: [PATCH 05/50] Moved drawing tools into one section --- src/components/map/Map.js | 29 ++--- src/components/map/MapControls.js | 24 +--- src/components/map/MapDrawing.js | 35 +++--- src/components/map/MapInteraction.js | 3 +- .../map/controls/BrushToolSettings.js | 61 ---------- .../map/controls/DrawingToolSettings.js | 104 ++++++++++++++++++ .../map/controls/EraseToolSettings.js | 34 ------ .../map/controls/ShapeToolSettings.js | 69 ------------ src/helpers/drawing.js | 7 +- ...{ShapeCircleIcon.js => BrushCircleIcon.js} | 4 +- .../{BrushFillIcon.js => BrushPaintIcon.js} | 0 ...RectangleIcon.js => BrushRectangleIcon.js} | 4 +- ...peTriangleIcon.js => BrushTriangleIcon.js} | 4 +- src/icons/ShapeToolIcon.js | 20 ---- 14 files changed, 152 insertions(+), 246 deletions(-) delete mode 100644 src/components/map/controls/BrushToolSettings.js create mode 100644 src/components/map/controls/DrawingToolSettings.js delete mode 100644 src/components/map/controls/EraseToolSettings.js delete mode 100644 src/components/map/controls/ShapeToolSettings.js rename src/icons/{ShapeCircleIcon.js => BrushCircleIcon.js} (84%) rename src/icons/{BrushFillIcon.js => BrushPaintIcon.js} (100%) rename src/icons/{ShapeRectangleIcon.js => BrushRectangleIcon.js} (83%) rename src/icons/{ShapeTriangleIcon.js => BrushTriangleIcon.js} (84%) delete mode 100644 src/icons/ShapeToolIcon.js diff --git a/src/components/map/Map.js b/src/components/map/Map.js index 28e07a6..39fc9f6 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -49,14 +49,9 @@ function Map({ const [selectedToolId, setSelectedToolId] = useState("pan"); const [toolSettings, setToolSettings] = useState({ fog: { type: "polygon", useEdgeSnapping: false, useFogCut: false }, - brush: { - color: "darkGray", - type: "stroke", - useBlending: false, - }, - shape: { + drawing: { color: "red", - type: "rectangle", + type: "brush", useBlending: true, }, }); @@ -189,33 +184,27 @@ function Map({ const disabledControls = []; if (!allowMapDrawing) { - disabledControls.push("brush"); - disabledControls.push("shape"); - disabledControls.push("erase"); + disabledControls.push("drawing"); } if (!map) { disabledControls.push("pan"); } - if (mapShapes.length === 0) { - disabledControls.push("erase"); - } if (!allowFogDrawing) { disabledControls.push("fog"); } - const disabledSettings = { fog: [], brush: [], shape: [], erase: [] }; + const disabledSettings = { fog: [], drawing: [] }; + if (mapShapes.length === 0) { + disabledSettings.drawing.push("erase"); + } if (!mapState || mapState.mapDrawActionIndex < 0) { - disabledSettings.brush.push("undo"); - disabledSettings.shape.push("undo"); - disabledSettings.erase.push("undo"); + disabledSettings.drawing.push("undo"); } if ( !mapState || mapState.mapDrawActionIndex === mapState.mapDrawActions.length - 1 ) { - disabledSettings.brush.push("redo"); - disabledSettings.shape.push("redo"); - disabledSettings.erase.push("redo"); + disabledSettings.drawing.push("redo"); } if (!mapState || mapState.fogDrawActionIndex < 0) { disabledSettings.fog.push("undo"); diff --git a/src/components/map/MapControls.js b/src/components/map/MapControls.js index 93c2a25..0c75af7 100644 --- a/src/components/map/MapControls.js +++ b/src/components/map/MapControls.js @@ -7,15 +7,11 @@ import Divider from "../Divider"; import SelectMapButton from "./SelectMapButton"; import FogToolSettings from "./controls/FogToolSettings"; -import BrushToolSettings from "./controls/BrushToolSettings"; -import ShapeToolSettings from "./controls/ShapeToolSettings"; -import EraseToolSettings from "./controls/EraseToolSettings"; +import DrawingToolSettings from "./controls/DrawingToolSettings"; import PanToolIcon from "../../icons/PanToolIcon"; import FogToolIcon from "../../icons/FogToolIcon"; import BrushToolIcon from "../../icons/BrushToolIcon"; -import ShapeToolIcon from "../../icons/ShapeToolIcon"; -import EraseToolIcon from "../../icons/EraseToolIcon"; import ExpandMoreIcon from "../../icons/ExpandMoreIcon"; function MapContols({ @@ -45,26 +41,14 @@ function MapContols({ title: "Fog Tool", SettingsComponent: FogToolSettings, }, - brush: { + drawing: { id: "brush", icon: , title: "Brush Tool", - SettingsComponent: BrushToolSettings, - }, - shape: { - id: "shape", - icon: , - title: "Shape Tool", - SettingsComponent: ShapeToolSettings, - }, - erase: { - id: "erase", - icon: , - title: "Erase tool", - SettingsComponent: EraseToolSettings, + SettingsComponent: DrawingToolSettings, }, }; - const tools = ["pan", "fog", "brush", "shape", "erase"]; + const tools = ["pan", "fog", "drawing"]; const sections = [ { diff --git a/src/components/map/MapDrawing.js b/src/components/map/MapDrawing.js index 3974981..9fb49b8 100644 --- a/src/components/map/MapDrawing.js +++ b/src/components/map/MapDrawing.js @@ -29,11 +29,18 @@ function MapDrawing({ const [isBrushDown, setIsBrushDown] = useState(false); const [erasingShapes, setErasingShapes] = useState([]); - const shouldHover = selectedToolId === "erase"; - const isEditing = - selectedToolId === "brush" || - selectedToolId === "shape" || - selectedToolId === "erase"; + const shouldHover = + selectedToolSettings && selectedToolSettings.type === "erase"; + const isEditing = selectedToolId === "drawing"; + const isBrush = + selectedToolSettings && + (selectedToolSettings.type === "brush" || + selectedToolSettings.type === "paint"); + const isShape = + selectedToolSettings && + (selectedToolSettings.type === "rectangle" || + selectedToolSettings.type === "circle" || + selectedToolSettings.type === "triangle"); const handleBrushUp = useCallback(() => { setIsBrushDown(false); @@ -58,15 +65,15 @@ function MapDrawing({ blend: selectedToolSettings && selectedToolSettings.useBlending, id: shortid.generate(), }; - if (selectedToolId === "brush") { + if (isBrush) { setDrawingShape({ type: "path", - pathType: selectedToolSettings.type, + pathType: selectedToolSettings.type === "brush" ? "stroke" : "fill", data: { points: [brushPosition] }, - strokeWidth: selectedToolSettings.type === "stroke" ? 1 : 0, + strokeWidth: selectedToolSettings.type === "brush" ? 1 : 0, ...commonShapeData, }); - } else if (selectedToolId === "shape") { + } else if (isShape) { setDrawingShape({ type: "shape", shapeType: selectedToolSettings.type, @@ -86,7 +93,7 @@ function MapDrawing({ gridSize, shapes ); - if (selectedToolId === "brush") { + if (isBrush) { setDrawingShape((prevShape) => { const prevPoints = prevShape.data.points; if ( @@ -108,7 +115,7 @@ function MapDrawing({ data: { points: simplified }, }; }); - } else if (selectedToolId === "shape") { + } else if (isShape) { setDrawingShape((prevShape) => ({ ...prevShape, data: getUpdatedShapeData( @@ -122,11 +129,11 @@ function MapDrawing({ } function endShape() { - if (selectedToolId === "brush" && drawingShape) { + if (isBrush && drawingShape) { if (drawingShape.data.points.length > 1) { onShapeAdd(drawingShape); } - } else if (selectedToolId === "shape" && drawingShape) { + } else if (isShape && drawingShape) { onShapeAdd(drawingShape); } setDrawingShape(null); @@ -156,6 +163,8 @@ function MapDrawing({ shapes, drawingShape, handleBrushUp, + isBrush, + isShape, ] ); diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js index 6e20aa5..56494b2 100644 --- a/src/components/map/MapInteraction.js +++ b/src/components/map/MapInteraction.js @@ -172,8 +172,7 @@ function MapInteraction({ map, children, controls, selectedToolId }) { case "pan": return "move"; case "fog": - case "brush": - case "shape": + case "drawing": return "crosshair"; default: return "default"; diff --git a/src/components/map/controls/BrushToolSettings.js b/src/components/map/controls/BrushToolSettings.js deleted file mode 100644 index 5e2c1b9..0000000 --- a/src/components/map/controls/BrushToolSettings.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { Flex } from "theme-ui"; - -import ColorControl from "./ColorControl"; -import AlphaBlendToggle from "./AlphaBlendToggle"; -import RadioIconButton from "./RadioIconButton"; - -import BrushStrokeIcon from "../../../icons/BrushStrokeIcon"; -import BrushFillIcon from "../../../icons/BrushFillIcon"; - -import UndoButton from "./UndoButton"; -import RedoButton from "./RedoButton"; - -import Divider from "../../Divider"; - -function BrushToolSettings({ - settings, - onSettingChange, - onToolAction, - disabledActions, -}) { - return ( - - onSettingChange({ color })} - /> - - onSettingChange({ type: "stroke" })} - isSelected={settings.type === "stroke"} - > - - - onSettingChange({ type: "fill" })} - isSelected={settings.type === "fill"} - > - - - - onSettingChange({ useBlending })} - /> - - onToolAction("mapUndo")} - disabled={disabledActions.includes("undo")} - /> - onToolAction("mapRedo")} - disabled={disabledActions.includes("redo")} - /> - - ); -} - -export default BrushToolSettings; diff --git a/src/components/map/controls/DrawingToolSettings.js b/src/components/map/controls/DrawingToolSettings.js new file mode 100644 index 0000000..349def6 --- /dev/null +++ b/src/components/map/controls/DrawingToolSettings.js @@ -0,0 +1,104 @@ +import React from "react"; +import { Flex, IconButton } from "theme-ui"; + +import ColorControl from "./ColorControl"; +import AlphaBlendToggle from "./AlphaBlendToggle"; +import RadioIconButton from "./RadioIconButton"; + +import BrushIcon from "../../../icons/BrushToolIcon"; +import BrushFillIcon from "../../../icons/BrushPaintIcon"; +import BrushRectangleIcon from "../../../icons/BrushRectangleIcon"; +import BrushCircleIcon from "../../../icons/BrushCircleIcon"; +import BrushTriangleIcon from "../../../icons/BrushTriangleIcon"; +import EraseAllIcon from "../../../icons/EraseAllIcon"; +import EraseIcon from "../../../icons/EraseToolIcon"; + +import UndoButton from "./UndoButton"; +import RedoButton from "./RedoButton"; + +import Divider from "../../Divider"; + +function DrawingToolSettings({ + settings, + onSettingChange, + onToolAction, + disabledActions, +}) { + return ( + + onSettingChange({ color })} + /> + + onSettingChange({ type: "brush" })} + isSelected={settings.type === "brush"} + > + + + onSettingChange({ type: "paint" })} + isSelected={settings.type === "paint"} + > + + + onSettingChange({ type: "rectangle" })} + isSelected={settings.type === "rectangle"} + > + + + onSettingChange({ type: "circle" })} + isSelected={settings.type === "circle"} + > + + + onSettingChange({ type: "triangle" })} + isSelected={settings.type === "triangle"} + > + + + + onSettingChange({ type: "erase" })} + isSelected={settings.type === "erase"} + disabled={disabledActions.includes("erase")} + > + + + onToolAction("eraseAll")} + disabled={disabledActions.includes("erase")} + > + + + + onSettingChange({ useBlending })} + /> + + onToolAction("mapUndo")} + disabled={disabledActions.includes("undo")} + /> + onToolAction("mapRedo")} + disabled={disabledActions.includes("redo")} + /> + + ); +} + +export default DrawingToolSettings; diff --git a/src/components/map/controls/EraseToolSettings.js b/src/components/map/controls/EraseToolSettings.js deleted file mode 100644 index c41b117..0000000 --- a/src/components/map/controls/EraseToolSettings.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import { Flex, IconButton } from "theme-ui"; - -import EraseAllIcon from "../../../icons/EraseAllIcon"; - -import UndoButton from "./UndoButton"; -import RedoButton from "./RedoButton"; - -import Divider from "../../Divider"; - -function EraseToolSettings({ onToolAction, disabledActions }) { - return ( - - onToolAction("eraseAll")} - > - - - - onToolAction("mapUndo")} - disabled={disabledActions.includes("undo")} - /> - onToolAction("mapRedo")} - disabled={disabledActions.includes("redo")} - /> - - ); -} - -export default EraseToolSettings; diff --git a/src/components/map/controls/ShapeToolSettings.js b/src/components/map/controls/ShapeToolSettings.js deleted file mode 100644 index cd388b7..0000000 --- a/src/components/map/controls/ShapeToolSettings.js +++ /dev/null @@ -1,69 +0,0 @@ -import React from "react"; -import { Flex } from "theme-ui"; - -import ColorControl from "./ColorControl"; -import AlphaBlendToggle from "./AlphaBlendToggle"; -import RadioIconButton from "./RadioIconButton"; - -import ShapeRectangleIcon from "../../../icons/ShapeRectangleIcon"; -import ShapeCircleIcon from "../../../icons/ShapeCircleIcon"; -import ShapeTriangleIcon from "../../../icons/ShapeTriangleIcon"; - -import UndoButton from "./UndoButton"; -import RedoButton from "./RedoButton"; - -import Divider from "../../Divider"; - -function ShapeToolSettings({ - settings, - onSettingChange, - onToolAction, - disabledActions, -}) { - return ( - - onSettingChange({ color })} - /> - - onSettingChange({ type: "rectangle" })} - isSelected={settings.type === "rectangle"} - > - - - onSettingChange({ type: "circle" })} - isSelected={settings.type === "circle"} - > - - - onSettingChange({ type: "triangle" })} - isSelected={settings.type === "triangle"} - > - - - - onSettingChange({ useBlending })} - /> - - onToolAction("mapUndo")} - disabled={disabledActions.includes("undo")} - /> - onToolAction("mapRedo")} - disabled={disabledActions.includes("redo")} - /> - - ); -} - -export default ShapeToolSettings; diff --git a/src/helpers/drawing.js b/src/helpers/drawing.js index 0ff64d0..93becb4 100644 --- a/src/helpers/drawing.js +++ b/src/helpers/drawing.js @@ -12,7 +12,12 @@ export function getBrushPositionForTool( shapes ) { let position = brushPosition; - if (tool === "shape") { + if ( + tool === "drawing" && + (toolSettings.type === "rectangle" || + toolSettings.type === "circle" || + toolSettings.type === "triangle") + ) { const snapped = Vector2.roundTo(position, gridSize); const minGrid = Vector2.min(gridSize); const distance = Vector2.length(Vector2.subtract(snapped, position)); diff --git a/src/icons/ShapeCircleIcon.js b/src/icons/BrushCircleIcon.js similarity index 84% rename from src/icons/ShapeCircleIcon.js rename to src/icons/BrushCircleIcon.js index c393782..b4cf4ed 100644 --- a/src/icons/ShapeCircleIcon.js +++ b/src/icons/BrushCircleIcon.js @@ -1,6 +1,6 @@ import React from "react"; -function ShapeCircleIcon() { +function BrushCircleIcon() { return ( - - - - - - ); -} - -export default ShapeToolIcon; From a300e6bd7982fe14082538cc43c80c1756c7bb10 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 21 Jun 2020 13:18:03 +1000 Subject: [PATCH 06/50] Moved fog add toggle to two separate icons --- .../map/controls/FogSubtractToggle.js | 19 --------------- .../map/controls/FogToolSettings.js | 23 +++++++++++++------ 2 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 src/components/map/controls/FogSubtractToggle.js diff --git a/src/components/map/controls/FogSubtractToggle.js b/src/components/map/controls/FogSubtractToggle.js deleted file mode 100644 index 841ee57..0000000 --- a/src/components/map/controls/FogSubtractToggle.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from "react"; -import { IconButton } from "theme-ui"; - -import FogAddIcon from "../../../icons/FogAddIcon"; -import FogSubtractIcon from "../../../icons/FogSubtractIcon"; - -function FogSubtractToggle({ useFogSubtract, onFogSubtractChange }) { - return ( - onFogSubtractChange(!useFogSubtract)} - > - {useFogSubtract ? : } - - ); -} - -export default FogSubtractToggle; diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index 616fc5a..0ab7c10 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -3,12 +3,13 @@ import { Flex } from "theme-ui"; import EdgeSnappingToggle from "./EdgeSnappingToggle"; import RadioIconButton from "./RadioIconButton"; -import FogSubtractToggle from "./FogSubtractToggle"; import FogBrushIcon from "../../../icons/FogBrushIcon"; import FogPolygonIcon from "../../../icons/FogPolygonIcon"; import FogRemoveIcon from "../../../icons/FogRemoveIcon"; import FogToggleIcon from "../../../icons/FogToggleIcon"; +import FogAddIcon from "../../../icons/FogAddIcon"; +import FogSubtractIcon from "../../../icons/FogSubtractIcon"; import UndoButton from "./UndoButton"; import RedoButton from "./RedoButton"; @@ -38,12 +39,20 @@ function BrushToolSettings({ - - onSettingChange({ useFogSubtract }) - } - /> + onSettingChange({ useFogSubtract: false })} + isSelected={!settings.useFogSubtract} + > + + + onSettingChange({ useFogSubtract: true })} + isSelected={settings.useFogSubtract} + > + + {/* TODO: Re-enable edge snapping when holes are fixed */} {/* Date: Sun, 21 Jun 2020 14:55:17 +1000 Subject: [PATCH 07/50] Added grid snapping to fog polygon, added center snapping and fixed fog magnet --- .../map/controls/FogToolSettings.js | 44 +++++------ src/helpers/drawing.js | 73 +++++++++++++------ 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index 0ab7c10..2a583bc 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -39,28 +39,6 @@ function BrushToolSettings({ - onSettingChange({ useFogSubtract: false })} - isSelected={!settings.useFogSubtract} - > - - - onSettingChange({ useFogSubtract: true })} - isSelected={settings.useFogSubtract} - > - - - {/* TODO: Re-enable edge snapping when holes are fixed */} - {/* - onSettingChange({ useEdgeSnapping }) - } - /> */} - onSettingChange({ type: "toggle" })} @@ -76,6 +54,28 @@ function BrushToolSettings({ + onSettingChange({ useFogSubtract: false })} + isSelected={!settings.useFogSubtract} + > + + + onSettingChange({ useFogSubtract: true })} + isSelected={settings.useFogSubtract} + > + + + + + onSettingChange({ useEdgeSnapping }) + } + /> + onToolAction("fogUndo")} disabled={disabledActions.includes("undo")} diff --git a/src/helpers/drawing.js b/src/helpers/drawing.js index 93becb4..0229422 100644 --- a/src/helpers/drawing.js +++ b/src/helpers/drawing.js @@ -12,32 +12,57 @@ export function getBrushPositionForTool( shapes ) { let position = brushPosition; - if ( - tool === "drawing" && - (toolSettings.type === "rectangle" || - toolSettings.type === "circle" || - toolSettings.type === "triangle") - ) { - const snapped = Vector2.roundTo(position, gridSize); + const useGridSnappning = + (tool === "drawing" && + (toolSettings.type === "rectangle" || + toolSettings.type === "circle" || + toolSettings.type === "triangle")) || + (tool === "fog" && toolSettings.type === "polygon"); + + if (useGridSnappning) { + // Snap to corners of grid + const gridSnap = Vector2.roundTo(position, gridSize); + const gridDistance = Vector2.length(Vector2.subtract(gridSnap, position)); + + // Snap to center of grid + const centerSnap = Vector2.add( + Vector2.roundTo(position, gridSize), + Vector2.multiply(gridSize, 0.5) + ); + const centerDistance = Vector2.length( + Vector2.subtract(centerSnap, position) + ); const minGrid = Vector2.min(gridSize); - const distance = Vector2.length(Vector2.subtract(snapped, position)); - if (distance < minGrid * snappingThreshold) { - position = snapped; + if (gridDistance < minGrid * snappingThreshold) { + position = gridSnap; + } else if (centerDistance < minGrid * snappingThreshold) { + position = centerSnap; } } - if (tool === "fog" && toolSettings.type === "add") { - if (toolSettings.useGridSnapping) { - position = Vector2.roundTo(position, gridSize); - } - if (toolSettings.useEdgeSnapping) { - const minGrid = Vector2.min(gridSize); - let closestDistance = Number.MAX_VALUE; - let closestPosition = position; - // Find the closest point on all fog shapes - for (let shape of shapes) { - if (shape.type === "fog") { - const points = shape.data.points; - const isInShape = Vector2.pointInPolygon(position, points); + + const useEdgeSnapping = tool === "fog" && toolSettings.useEdgeSnapping; + + if (useEdgeSnapping) { + const minGrid = Vector2.min(gridSize); + let closestDistance = Number.MAX_VALUE; + let closestPosition = position; + // Find the closest point on all fog shapes + for (let shape of shapes) { + if (shape.type === "fog") { + // Include shape points and holes + let pointArray = [shape.data.points, ...shape.data.holes]; + + // Check whether the position is in the shape but not any holes + let isInShape = Vector2.pointInPolygon(position, shape.data.points); + if (shape.data.holes.length > 0) { + for (let hole of shape.data.holes) { + if (Vector2.pointInPolygon(position, hole)) { + isInShape = false; + } + } + } + + for (let points of pointArray) { // Find the closest point to each line of the shape for (let i = 0; i < points.length; i++) { const a = points[i]; @@ -59,8 +84,8 @@ export function getBrushPositionForTool( } } } - position = closestPosition; } + position = closestPosition; } return position; From 2f740efd7cb3d27b8dc7d5b5fe7e4f5ee3911c37 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 21 Jun 2020 16:09:37 +1000 Subject: [PATCH 08/50] Added UI element to allow accepting fog polygon editing --- src/components/map/MapFog.js | 102 ++++++++++++++++++++++++----------- src/helpers/konva.js | 38 ++++++++++++- 2 files changed, 106 insertions(+), 34 deletions(-) diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index cace424..39e759e 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -1,4 +1,4 @@ -import React, { useContext, useState, useEffect } from "react"; +import React, { useContext, useState, useEffect, useCallback } from "react"; import shortid from "shortid"; import { Group } from "react-konva"; import useImage from "use-image"; @@ -18,6 +18,7 @@ import colors from "../../helpers/colors"; import { HoleyLine, getRelativePointerPositionNormalized, + Tick, } from "../../helpers/konva"; function MapFog({ @@ -191,45 +192,14 @@ function MapFog({ setIsBrushDown(false); } - function handleKeyDown(event) { - if ( - event.key === "Enter" && - selectedToolSettings.type === "polygon" && - drawingShape - ) { - const subtract = selectedToolSettings.useFogSubtract; - const data = { - ...drawingShape.data, - // Remove the last point as it hasn't been placed yet - points: drawingShape.data.points.slice(0, -1), - }; - if (subtract) { - onShapeSubtract({ - id: drawingShape.id, - type: drawingShape.type, - data: data, - }); - } else { - onShapeAdd({ ...drawingShape, data: data }); - } - - setDrawingShape(null); - } - if (event.key === "Escape" && drawingShape) { - setDrawingShape(null); - } - } - mapStage.on("mousedown", handleBrushDown); mapStage.on("mousemove", handleBrushMove); mapStage.on("mouseup", handleBrushUp); - mapStage.container().addEventListener("keydown", handleKeyDown); return () => { mapStage.off("mousedown", handleBrushDown); mapStage.off("mousemove", handleBrushMove); mapStage.off("mouseup", handleBrushUp); - mapStage.container().removeEventListener("keydown", handleKeyDown); }; }, [ mapStageRef, @@ -248,6 +218,49 @@ function MapFog({ stageScale, ]); + const finishDrawingPolygon = useCallback(() => { + const subtract = selectedToolSettings.useFogSubtract; + const data = { + ...drawingShape.data, + // Remove the last point as it hasn't been placed yet + points: drawingShape.data.points.slice(0, -1), + }; + if (subtract) { + onShapeSubtract({ + id: drawingShape.id, + type: drawingShape.type, + data: data, + }); + } else { + onShapeAdd({ ...drawingShape, data: data }); + } + + setDrawingShape(null); + }, [selectedToolSettings, drawingShape, onShapeSubtract, onShapeAdd]); + + // Add keyboard shortcuts + useEffect(() => { + const mapStage = mapStageRef.current; + + function handleKeyDown(event) { + if ( + event.key === "Enter" && + selectedToolSettings.type === "polygon" && + drawingShape + ) { + finishDrawingPolygon(); + } + if (event.key === "Escape" && drawingShape) { + setDrawingShape(null); + } + } + + mapStage.container().addEventListener("keydown", handleKeyDown); + return () => { + mapStage.container().removeEventListener("keydown", handleKeyDown); + }; + }, [finishDrawingPolygon, mapStageRef, drawingShape, selectedToolSettings]); + function handleShapeOver(shape, isDown) { if (shouldHover && isDown) { if (editingShapes.findIndex((s) => s.id === shape.id) === -1) { @@ -300,10 +313,35 @@ function MapFog({ return renderShape(editingShape); } + function renderPolygonAcceptTick(shape) { + if (shape.data.points.length === 0) { + return; + } + return ( + { + // Check that there is enough points after clicking + if (shape.data.points.length < 5) { + setDrawingShape(null); + } else { + finishDrawingPolygon(); + } + }} + /> + ); + } + return ( {shapes.map(renderShape)} {drawingShape && renderShape(drawingShape)} + {drawingShape && + selectedToolSettings.type === "polygon" && + renderPolygonAcceptTick(drawingShape)} {editingShapes.length > 0 && editingShapes.map(renderEditingShape)} ); diff --git a/src/helpers/konva.js b/src/helpers/konva.js index dd799be..b648a7f 100644 --- a/src/helpers/konva.js +++ b/src/helpers/konva.js @@ -1,5 +1,5 @@ -import React from "react"; -import { Line } from "react-konva"; +import React, { useState } from "react"; +import { Line, Group, Path, Circle } from "react-konva"; // Holes should be wound in the opposite direction as the containing points array export function HoleyLine({ holes, ...props }) { @@ -106,6 +106,40 @@ export function HoleyLine({ holes, ...props }) { return ; } +export function Tick({ x, y, scale, onClick, cross }) { + const [fill, setFill] = useState("white"); + function handleEnter() { + setFill("hsl(260, 100%, 80%)"); + } + + function handleLeave() { + setFill("white"); + } + return ( + + + + + ); +} + export function getRelativePointerPosition(node) { let transform = node.getAbsoluteTransform().copy(); transform.invert(); From cb8b66e55f6d5a4576f6e6e34c860f3f0db6de75 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 21 Jun 2020 16:17:54 +1000 Subject: [PATCH 09/50] Added touch supprt to fog polygon --- src/components/map/MapFog.js | 12 ++++++------ src/helpers/konva.js | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index 39e759e..9a06e9b 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -192,14 +192,14 @@ function MapFog({ setIsBrushDown(false); } - mapStage.on("mousedown", handleBrushDown); - mapStage.on("mousemove", handleBrushMove); - mapStage.on("mouseup", handleBrushUp); + mapStage.on("mousedown touchstart", handleBrushDown); + mapStage.on("mousemove touchmove", handleBrushMove); + mapStage.on("mouseup touchend", handleBrushUp); return () => { - mapStage.off("mousedown", handleBrushDown); - mapStage.off("mousemove", handleBrushMove); - mapStage.off("mouseup", handleBrushUp); + mapStage.off("mousedown touchstart", handleBrushDown); + mapStage.off("mousemove touchmove", handleBrushMove); + mapStage.off("mouseup touchend", handleBrushUp); }; }, [ mapStageRef, diff --git a/src/helpers/konva.js b/src/helpers/konva.js index b648a7f..fad95a2 100644 --- a/src/helpers/konva.js +++ b/src/helpers/konva.js @@ -124,6 +124,7 @@ export function Tick({ x, y, scale, onClick, cross }) { onMouseEnter={handleEnter} onMouseLeave={handleLeave} onClick={onClick} + onTap={onClick} > Date: Sun, 21 Jun 2020 16:29:42 +1000 Subject: [PATCH 10/50] Moved drawing to use native konva events --- src/components/map/MapDrawing.js | 170 +++++++++++++++---------------- src/helpers/useMapBrush.js | 71 ------------- 2 files changed, 85 insertions(+), 156 deletions(-) delete mode 100644 src/helpers/useMapBrush.js diff --git a/src/components/map/MapDrawing.js b/src/components/map/MapDrawing.js index 9fb49b8..e83cac7 100644 --- a/src/components/map/MapDrawing.js +++ b/src/components/map/MapDrawing.js @@ -1,8 +1,9 @@ -import React, { useContext, useState, useCallback } from "react"; +import React, { useContext, useState, useEffect } from "react"; import shortid from "shortid"; import { Group, Line, Rect, Circle } from "react-konva"; import MapInteractionContext from "../../contexts/MapInteractionContext"; +import MapStageContext from "../../contexts/MapStageContext"; import { compare as comparePoints } from "../../helpers/vector2"; import { @@ -12,9 +13,9 @@ import { simplifyPoints, getStrokeWidth, } from "../../helpers/drawing"; +import { getRelativePointerPositionNormalized } from "../../helpers/konva"; import colors from "../../helpers/colors"; -import useMapBrush from "../../helpers/useMapBrush"; function MapDrawing({ shapes, @@ -25,6 +26,7 @@ function MapDrawing({ gridSize, }) { const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext); + const mapStageRef = useContext(MapStageContext); const [drawingShape, setDrawingShape] = useState(null); const [isBrushDown, setIsBrushDown] = useState(false); const [erasingShapes, setErasingShapes] = useState([]); @@ -42,57 +44,53 @@ function MapDrawing({ selectedToolSettings.type === "circle" || selectedToolSettings.type === "triangle"); - const handleBrushUp = useCallback(() => { - setIsBrushDown(false); - if (erasingShapes.length > 0) { - onShapesRemove(erasingShapes.map((shape) => shape.id)); - setErasingShapes([]); + useEffect(() => { + if (!isEditing) { + return; } - }, [erasingShapes, onShapesRemove]); + const mapStage = mapStageRef.current; - const handleShapeDraw = useCallback( - (brushState, mapBrushPosition) => { - function startShape() { - const brushPosition = getBrushPositionForTool( - mapBrushPosition, - selectedToolId, - selectedToolSettings, - gridSize, - shapes - ); - const commonShapeData = { - color: selectedToolSettings && selectedToolSettings.color, - blend: selectedToolSettings && selectedToolSettings.useBlending, - id: shortid.generate(), - }; - if (isBrush) { - setDrawingShape({ - type: "path", - pathType: selectedToolSettings.type === "brush" ? "stroke" : "fill", - data: { points: [brushPosition] }, - strokeWidth: selectedToolSettings.type === "brush" ? 1 : 0, - ...commonShapeData, - }); - } else if (isShape) { - setDrawingShape({ - type: "shape", - shapeType: selectedToolSettings.type, - data: getDefaultShapeData(selectedToolSettings.type, brushPosition), - strokeWidth: 0, - ...commonShapeData, - }); - } - setIsBrushDown(true); + function getBrushPosition() { + const mapImage = mapStage.findOne("#mapImage"); + return getBrushPositionForTool( + getRelativePointerPositionNormalized(mapImage), + selectedToolId, + selectedToolSettings, + gridSize, + shapes + ); + } + + function handleBrushDown() { + const brushPosition = getBrushPosition(); + const commonShapeData = { + color: selectedToolSettings && selectedToolSettings.color, + blend: selectedToolSettings && selectedToolSettings.useBlending, + id: shortid.generate(), + }; + if (isBrush) { + setDrawingShape({ + type: "path", + pathType: selectedToolSettings.type === "brush" ? "stroke" : "fill", + data: { points: [brushPosition] }, + strokeWidth: selectedToolSettings.type === "brush" ? 1 : 0, + ...commonShapeData, + }); + } else if (isShape) { + setDrawingShape({ + type: "shape", + shapeType: selectedToolSettings.type, + data: getDefaultShapeData(selectedToolSettings.type, brushPosition), + strokeWidth: 0, + ...commonShapeData, + }); } + setIsBrushDown(true); + } - function continueShape() { - const brushPosition = getBrushPositionForTool( - mapBrushPosition, - selectedToolId, - selectedToolSettings, - gridSize, - shapes - ); + function handleBrushMove() { + const brushPosition = getBrushPosition(); + if (isBrushDown && drawingShape) { if (isBrush) { setDrawingShape((prevShape) => { const prevPoints = prevShape.data.points; @@ -127,49 +125,51 @@ function MapDrawing({ })); } } + } - function endShape() { - if (isBrush && drawingShape) { - if (drawingShape.data.points.length > 1) { - onShapeAdd(drawingShape); - } - } else if (isShape && drawingShape) { + function handleBrushUp() { + if (isBrush && drawingShape) { + if (drawingShape.data.points.length > 1) { onShapeAdd(drawingShape); } - setDrawingShape(null); - handleBrushUp(); + } else if (isShape && drawingShape) { + onShapeAdd(drawingShape); } - switch (brushState) { - case "first": - startShape(); - return; - case "drawing": - continueShape(); - return; - case "last": - endShape(); - return; - default: - return; + if (erasingShapes.length > 0) { + onShapesRemove(erasingShapes.map((shape) => shape.id)); + setErasingShapes([]); } - }, - [ - selectedToolId, - selectedToolSettings, - gridSize, - stageScale, - onShapeAdd, - shapes, - drawingShape, - handleBrushUp, - isBrush, - isShape, - ] - ); - // Move away from this as it is too slow to respond - useMapBrush(isEditing, handleShapeDraw); + setDrawingShape(null); + setIsBrushDown(false); + } + + mapStage.on("mousedown touchstart", handleBrushDown); + mapStage.on("mousemove touchmove", handleBrushMove); + mapStage.on("mouseup touchend", handleBrushUp); + + return () => { + mapStage.off("mousedown touchstart", handleBrushDown); + mapStage.off("mousemove touchmove", handleBrushMove); + mapStage.off("mouseup touchend", handleBrushUp); + }; + }, [ + drawingShape, + erasingShapes, + gridSize, + isBrush, + isBrushDown, + isEditing, + isShape, + mapStageRef, + onShapeAdd, + onShapesRemove, + selectedToolId, + selectedToolSettings, + shapes, + stageScale, + ]); function handleShapeOver(shape, isDown) { if (shouldHover && isDown) { diff --git a/src/helpers/useMapBrush.js b/src/helpers/useMapBrush.js deleted file mode 100644 index 3c96f7d..0000000 --- a/src/helpers/useMapBrush.js +++ /dev/null @@ -1,71 +0,0 @@ -import { useContext, useRef, useEffect } from "react"; - -import MapInteractionContext from "../contexts/MapInteractionContext"; - -import { compare } from "./vector2"; - -import usePrevious from "./usePrevious"; - -/** - * @callback onBrushUpdate - * @param {string} drawState "first" | "drawing" | "last" - * @param {Object} brushPosition the normalized x and y coordinates of the brush on the map - */ - -/** - * Helper to get the maps drag position as it changes - * @param {boolean} shouldUpdate - * @param {onBrushUpdate} onBrushUpdate - */ -function useMapBrush(shouldUpdate, onBrushUpdate) { - const { stageDragState, mapDragPositionRef } = useContext( - MapInteractionContext - ); - - const requestRef = useRef(); - const previousDragState = usePrevious(stageDragState); - const previousBrushPositionRef = useRef(mapDragPositionRef.current); - - useEffect(() => { - function updateBrush(forceUpdate) { - const drawState = - stageDragState === "dragging" ? "drawing" : stageDragState; - const brushPosition = mapDragPositionRef.current; - const previousBrushPostition = previousBrushPositionRef.current; - // Only update brush when it has moved - if ( - !compare(brushPosition, previousBrushPostition, 0.0001) || - forceUpdate - ) { - onBrushUpdate(drawState, brushPosition); - previousBrushPositionRef.current = brushPosition; - } - } - - function animate() { - if (!shouldUpdate) { - return; - } - requestRef.current = requestAnimationFrame(animate); - updateBrush(false); - } - - requestRef.current = requestAnimationFrame(animate); - - if (stageDragState !== previousDragState && shouldUpdate) { - updateBrush(true); - } - - return () => { - cancelAnimationFrame(requestRef.current); - }; - }, [ - shouldUpdate, - onBrushUpdate, - stageDragState, - mapDragPositionRef, - previousDragState, - ]); -} - -export default useMapBrush; From aebc0f9267cfbdcbd3382050101e67c9e3dd40d4 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 21 Jun 2020 16:32:53 +1000 Subject: [PATCH 11/50] Added automatic setting change for drawing when erased all --- src/components/map/controls/DrawingToolSettings.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/map/controls/DrawingToolSettings.js b/src/components/map/controls/DrawingToolSettings.js index 349def6..8430fb8 100644 --- a/src/components/map/controls/DrawingToolSettings.js +++ b/src/components/map/controls/DrawingToolSettings.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import { Flex, IconButton } from "theme-ui"; import ColorControl from "./ColorControl"; @@ -24,6 +24,13 @@ function DrawingToolSettings({ onToolAction, disabledActions, }) { + // Change to brush if on erase and it gets disabled + useEffect(() => { + if (settings.type === "erase" && disabledActions.includes("erase")) { + onSettingChange({ type: "brush" }); + } + }, [disabledActions, settings, onSettingChange]); + return ( Date: Sun, 21 Jun 2020 16:55:58 +1000 Subject: [PATCH 12/50] Fix crash when selecting a tool while editing a fog polygon --- src/components/map/MapFog.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index 9a06e9b..c09b2ee 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -340,6 +340,7 @@ function MapFog({ {shapes.map(renderShape)} {drawingShape && renderShape(drawingShape)} {drawingShape && + selectedToolSettings && selectedToolSettings.type === "polygon" && renderPolygonAcceptTick(drawingShape)} {editingShapes.length > 0 && editingShapes.map(renderEditingShape)} From ccc0c2b89deba97001ce2bacdb19d6f19eed2e99 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 21 Jun 2020 16:58:41 +1000 Subject: [PATCH 13/50] Updated version to 1.4.0 preview --- package.json | 2 +- src/routes/Home.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dc10a84..a047826 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "owlbear-rodeo", - "version": "1.3.3", + "version": "1.4.0 (preview)", "private": true, "dependencies": { "@msgpack/msgpack": "^1.12.1", diff --git a/src/routes/Home.js b/src/routes/Home.js index 845478a..bc3d50f 100644 --- a/src/routes/Home.js +++ b/src/routes/Home.js @@ -51,7 +51,7 @@ function Home() { Join Game - Beta v1.3.3 + Beta v1.4.0 (preview) From 761bf8e80065aba9628970c76bc75808f125635a Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 28 Jun 2020 15:44:06 +1000 Subject: [PATCH 39/50] Removed async from select map modal setting change functions --- src/modals/SelectMapModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index ebf59e7..a053602 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -178,11 +178,11 @@ function SelectMapModal({ const [mapSettingChanges, setMapSettingChanges] = useState({}); const [mapStateSettingChanges, setMapStateSettingChanges] = useState({}); - async function handleMapSettingsChange(key, value) { + function handleMapSettingsChange(key, value) { setMapSettingChanges((prevChanges) => ({ ...prevChanges, [key]: value })); } - async function handleMapStateSettingsChange(key, value) { + function handleMapStateSettingsChange(key, value) { setMapStateSettingChanges((prevChanges) => ({ ...prevChanges, [key]: value, From 9f2ab861bbef88ebc136f42194ce9ee9e9960394 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 28 Jun 2020 15:44:36 +1000 Subject: [PATCH 40/50] Fixed bug with token and map settings modal values being edited with no item selected --- src/components/map/MapSettings.js | 28 ++++++++++++++++----------- src/components/token/TokenSettings.js | 11 ++++++----- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/map/MapSettings.js b/src/components/map/MapSettings.js index 79c6742..bfabeec 100644 --- a/src/components/map/MapSettings.js +++ b/src/components/map/MapSettings.js @@ -26,6 +26,9 @@ function MapSettings({ } } + const mapEmpty = !map || isEmpty(map); + const mapStateEmpty = !mapState || isEmpty(mapState); + return ( @@ -38,7 +41,7 @@ function MapSettings({ onChange={(e) => onSettingsChange("gridX", parseInt(e.target.value)) } - disabled={!map || map.type === "default"} + disabled={mapEmpty || map.type === "default"} min={1} my={1} /> @@ -52,7 +55,7 @@ function MapSettings({ onChange={(e) => onSettingsChange("gridY", parseInt(e.target.value)) } - disabled={!map || map.type === "default"} + disabled={mapEmpty || map.type === "default"} min={1} my={1} /> @@ -66,7 +69,7 @@ function MapSettings({ name="name" value={(map && map.name) || ""} onChange={(e) => onSettingsChange("name", e.target.value)} - disabled={!map || map.type === "default"} + disabled={mapEmpty || map.type === "default"} my={1} /> @@ -74,7 +77,7 @@ function MapSettings({