From 9eaece24e544de6567872c9de536bdd00355341e Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Tue, 9 Jun 2020 12:45:52 +1000 Subject: [PATCH] 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"