Replaced fog subtraction with fog cutting
This commit is contained in:
parent
5ae6f0bc1b
commit
c24133813f
@ -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"}
|
||||
|
@ -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
|
||||
|
21
src/components/map/controls/FogCutToggle.js
Normal file
21
src/components/map/controls/FogCutToggle.js
Normal 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;
|
@ -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) =>
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
18
src/icons/FogCutOffIcon.js
Normal file
18
src/icons/FogCutOffIcon.js
Normal 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
18
src/icons/FogCutOnIcon.js
Normal 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;
|
@ -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;
|
Loading…
Reference in New Issue
Block a user