Replaced fog subtraction with fog cutting

This commit is contained in:
Mitchell McCaffrey 2020-12-03 16:52:24 +11:00
parent 5ae6f0bc1b
commit c24133813f
9 changed files with 144 additions and 109 deletions

View File

@ -105,8 +105,8 @@ function Map({
onFogDraw({ type: "add", shapes: [shape] });
}
function handleFogShapeSubtract(shape) {
onFogDraw({ type: "subtract", shapes: [shape] });
function handleFogShapeCut(shape) {
onFogDraw({ type: "cut", shapes: [shape] });
}
function handleFogShapesRemove(shapeIds) {
@ -327,7 +327,7 @@ function Map({
map={map}
shapes={fogShapes}
onShapeAdd={handleFogShapeAdd}
onShapeSubtract={handleFogShapeSubtract}
onShapeCut={handleFogShapeCut}
onShapesRemove={handleFogShapesRemove}
onShapesEdit={handleFogShapesEdit}
active={selectedToolId === "fog"}

View File

@ -26,7 +26,7 @@ function MapFog({
map,
shapes,
onShapeAdd,
onShapeSubtract,
onShapeCut,
onShapesRemove,
onShapesEdit,
active,
@ -77,7 +77,7 @@ function MapFog({
holes: [],
},
strokeWidth: 0.5,
color: toolSettings.useFogSubtract ? "red" : "black",
color: toolSettings.useFogCut ? "red" : "black",
blend: false,
id: shortid.generate(),
visible: true,
@ -113,11 +113,10 @@ function MapFog({
function handleBrushUp() {
if (toolSettings.type === "brush" && drawingShape) {
const subtract = toolSettings.useFogSubtract;
const cut = toolSettings.useFogCut;
if (drawingShape.data.points.length > 1) {
let shapeData = {};
if (subtract) {
if (cut) {
shapeData = { id: drawingShape.id, type: drawingShape.type };
} else {
shapeData = { ...drawingShape, color: "black" };
@ -134,8 +133,8 @@ function MapFog({
),
},
};
if (subtract) {
onShapeSubtract(shape);
if (cut) {
onShapeCut(shape);
} else {
onShapeAdd(shape);
}
@ -168,7 +167,7 @@ function MapFog({
holes: [],
},
strokeWidth: 0.5,
color: toolSettings.useFogSubtract ? "red" : "black",
color: toolSettings.useFogCut ? "red" : "black",
blend: false,
id: shortid.generate(),
visible: true,
@ -215,14 +214,14 @@ function MapFog({
});
const finishDrawingPolygon = useCallback(() => {
const subtract = toolSettings.useFogSubtract;
const cut = toolSettings.useFogCut;
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({
if (cut) {
onShapeCut({
id: drawingShape.id,
type: drawingShape.type,
data: data,
@ -232,7 +231,7 @@ function MapFog({
}
setDrawingShape(null);
}, [toolSettings, drawingShape, onShapeSubtract, onShapeAdd]);
}, [toolSettings, drawingShape, onShapeCut, onShapeAdd]);
// Add keyboard shortcuts
function handleKeyDown({ key }) {
@ -242,30 +241,22 @@ function MapFog({
if (key === "Escape" && drawingShape) {
setDrawingShape(null);
}
if (key === "Alt" && drawingShape) {
updateShapeColor();
}
}
function handleKeyUp({ key }) {
if (key === "Alt" && drawingShape) {
updateShapeColor();
}
}
useKeyboard(handleKeyDown);
function updateShapeColor() {
// Update shape color when useFogCut changes
useEffect(() => {
setDrawingShape((prevShape) => {
if (!prevShape) {
return;
}
return {
...prevShape,
color: toolSettings.useFogSubtract ? "black" : "red",
color: toolSettings.useFogCut ? "red" : "black",
};
});
}
useKeyboard(handleKeyDown, handleKeyUp);
}, [toolSettings.useFogCut]);
function eraseHoveredShapes() {
// Erase

View File

@ -0,0 +1,21 @@
import React from "react";
import { IconButton } from "theme-ui";
import CutOnIcon from "../../../icons/FogCutOnIcon";
import CutOffIcon from "../../../icons/FogCutOffIcon";
function FogCutToggle({ useFogCut, onFogCutChange }) {
return (
<IconButton
aria-label={
useFogCut ? "Disable Fog Cutting (C)" : "Enable Fog Cutting (C)"
}
title={useFogCut ? "Disable Fog Cutting (C)" : "Enable Fog Cutting (C)"}
onClick={() => onFogCutChange(!useFogCut)}
>
{useFogCut ? <CutOnIcon /> : <CutOffIcon />}
</IconButton>
);
}
export default FogCutToggle;

View File

@ -6,13 +6,12 @@ import RadioIconButton from "../../RadioIconButton";
import EdgeSnappingToggle from "./EdgeSnappingToggle";
import FogPreviewToggle from "./FogPreviewToggle";
import FogCutToggle from "./FogCutToggle";
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";
@ -31,7 +30,7 @@ function BrushToolSettings({
// Keyboard shortcuts
function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) {
if (key === "Alt") {
onSettingChange({ useFogSubtract: !settings.useFogSubtract });
onSettingChange({ useFogCut: !settings.useFogCut });
} else if (key === "p") {
onSettingChange({ type: "polygon" });
} else if (key === "b") {
@ -44,6 +43,8 @@ function BrushToolSettings({
onSettingChange({ useEdgeSnapping: !settings.useEdgeSnapping });
} else if (key === "f") {
onSettingChange({ preview: !settings.preview });
} else if (key === "c") {
onSettingChange({ useFogCut: !settings.useFogCut });
} else if (
(key === "z" || key === "Z") &&
(ctrlKey || metaKey) &&
@ -63,7 +64,7 @@ function BrushToolSettings({
function handleKeyUp({ key }) {
if (key === "Alt") {
onSettingChange({ useFogSubtract: !settings.useFogSubtract });
onSettingChange({ useFogCut: !settings.useFogCut });
}
}
@ -85,21 +86,6 @@ function BrushToolSettings({
},
];
const modeTools = [
{
id: "add",
title: "Add Fog",
isSelected: !settings.useFogSubtract,
icon: <FogAddIcon />,
},
{
id: "subtract",
title: "Subtract Fog",
isSelected: settings.useFogSubtract,
icon: <FogSubtractIcon />,
},
];
return (
<Flex sx={{ alignItems: "center" }}>
<ToolSection
@ -123,14 +109,10 @@ function BrushToolSettings({
<FogRemoveIcon />
</RadioIconButton>
<Divider vertical />
<ToolSection
tools={modeTools}
onToolClick={(tool) =>
onSettingChange({ useFogSubtract: tool.id === "subtract" })
}
collapse={isSmallScreen}
<FogCutToggle
useFogCut={settings.useFogCut}
onFogCutChange={(useFogCut) => onSettingChange({ useFogCut })}
/>
<Divider vertical />
<EdgeSnappingToggle
useEdgeSnapping={settings.useEdgeSnapping}
onEdgeSnappingChange={(useEdgeSnapping) =>

View File

@ -228,28 +228,69 @@ export function drawActionsToShapes(actions, actionIndex) {
);
let shapeGeom = [[shapePoints, ...shapeHoles]];
const difference = polygonClipping.difference(shapeGeom, actionGeom);
for (let i = 0; i < difference.length; i++) {
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,
},
};
}
addPolygonDifferenceToShapes(shape, difference, subtractedShapes);
}
shapesById = subtractedShapes;
}
if (action.type === "cut") {
const actionGeom = action.shapes.map((actionShape) => [
actionShape.data.points.map(({ x, y }) => [x, y]),
]);
let cutShapes = {};
for (let shape of Object.values(shapesById)) {
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);
const intersection = polygonClipping.intersection(
shapeGeom,
actionGeom
);
addPolygonDifferenceToShapes(shape, difference, cutShapes);
addPolygonIntersectionToShapes(shape, intersection, cutShapes);
}
shapesById = cutShapes;
}
}
return Object.values(shapesById);
}
function addPolygonDifferenceToShapes(shape, difference, shapes) {
for (let i = 0; i < difference.length; i++) {
let newId = `${shape.id}-dif-${i}`;
// 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 })));
}
}
shapes[newId] = {
...shape,
id: newId,
data: {
points: difference[i][0].map(([x, y]) => ({ x, y })),
holes,
},
};
}
}
function addPolygonIntersectionToShapes(shape, intersection, shapes) {
for (let i = 0; i < intersection.length; i++) {
let newId = `${shape.id}-int-${i}`;
shapes[newId] = {
...shape,
id: newId,
data: {
points: intersection[i][0].map(([x, y]) => ({ x, y })),
holes: [],
},
// Default intersection visibility to false
visible: false,
};
}
}

View File

@ -1,18 +0,0 @@
import React from "react";
function FogAddIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M19.35 10.04A7.49 7.49 0 0012 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 000 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM16 14h-3v3c0 .55-.45 1-1 1s-1-.45-1-1v-3H8c-.55 0-1-.45-1-1s.45-1 1-1h3V9c0-.55.45-1 1-1s1 .45 1 1v3h3c.55 0 1 .45 1 1s-.45 1-1 1z" />
</svg>
);
}
export default FogAddIcon;

View File

@ -0,0 +1,18 @@
import React from "react";
function FogCutOffIcon() {
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="M4.33 3.84l17.62 14.79a1 1 0 11-1.28 1.53l-7.28-6.1-.76 1.57a4 4 0 11-6.17 2.14A4 4 0 118.6 11.6l1.19-.57-6.74-5.66a1 1 0 011.28-1.53zM8.9 17.47a2 2 0 102.82 2.83 2 2 0 00-2.82-2.83zm-4.95-4.95a2 2 0 102.83 2.83 2 2 0 00-2.83-2.83zm6.01 1.76l-.65.32a4 4 0 01-.08.35l-.03.09a4 4 0 01.44-.1l.32-.66zM22.9 6.31l.02.04.02.05c.33.74.01 1.6-.72 1.96l-6.1 2.94-2.51-2.12 7.35-3.54c.72-.35 1.6-.05 1.94.67zM17.84 1.3l.04.01a1.45 1.45 0 01.72 1.97l-1.54 3.18-4.25 1.9L15.88 2a1.5 1.5 0 011.96-.71z" />
</svg>
);
}
export default FogCutOffIcon;

18
src/icons/FogCutOnIcon.js Normal file
View File

@ -0,0 +1,18 @@
import React from "react";
function FogCutOnIcon() {
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="M2.52 11.1a4 4 0 016.08.5l12.36-5.96c.72-.35 1.6-.05 1.94.67l.02.04.02.05c.33.74.01 1.6-.72 1.96l-7.93 3.83-1.66 3.44a4 4 0 11-6.17 2.14 4 4 0 01-3.94-6.67zm6.37 6.37a2 2 0 102.82 2.83 2 2 0 00-2.82-2.83zm2.82-5.66c-.2.2-.2.51 0 .7.2.2.51.2.71 0 .2-.19.2-.5 0-.7a.5.5 0 00-.7 0zM9.3 14.6a4 4 0 01-.08.35l-.03.09a4 4 0 01.44-.1l.32-.66-.65.32zm8.54-13.3l.04.01a1.45 1.45 0 01.72 1.97l-1.54 3.18-4.25 1.9L15.88 2a1.5 1.5 0 011.96-.71zM3.94 12.52a2 2 0 102.83 2.83 2 2 0 00-2.83-2.83z" />
</svg>
);
}
export default FogCutOnIcon;

View File

@ -1,18 +0,0 @@
import React from "react";
function FogRemoveIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 4a7.49 7.49 0 017.35 6.04c2.6.18 4.65 2.32 4.65 4.96 0 2.76-2.24 5-5 5H6c-3.31 0-6-2.69-6-6 0-3.09 2.34-5.64 5.35-5.96A7.496 7.496 0 0112 4zm4 8H8c-.55 0-1 .45-1 1s.45 1 1 1h8c.55 0 1-.45 1-1s-.45-1-1-1z" />
</svg>
);
}
export default FogRemoveIcon;