Removed edge snapping in favour of single layer fog system
This commit is contained in:
parent
1a2260515d
commit
7639297510
@ -92,12 +92,12 @@ function Map({
|
|||||||
onMapDraw(new RemoveShapeAction(shapeIds));
|
onMapDraw(new RemoveShapeAction(shapeIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapeAdd(shape) {
|
function handleFogShapesAdd(shapes) {
|
||||||
onFogDraw(new AddShapeAction([shape]));
|
onFogDraw(new AddShapeAction(shapes));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapeCut(shape) {
|
function handleFogShapesCut(shapes) {
|
||||||
onFogDraw(new CutShapeAction([shape]));
|
onFogDraw(new CutShapeAction(shapes));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFogShapesRemove(shapeIds) {
|
function handleFogShapesRemove(shapeIds) {
|
||||||
@ -228,8 +228,8 @@ function Map({
|
|||||||
<MapFog
|
<MapFog
|
||||||
map={map}
|
map={map}
|
||||||
shapes={fogShapes}
|
shapes={fogShapes}
|
||||||
onShapeAdd={handleFogShapeAdd}
|
onShapesAdd={handleFogShapesAdd}
|
||||||
onShapeCut={handleFogShapeCut}
|
onShapesCut={handleFogShapesCut}
|
||||||
onShapesRemove={handleFogShapesRemove}
|
onShapesRemove={handleFogShapesRemove}
|
||||||
onShapesEdit={handleFogShapesEdit}
|
onShapesEdit={handleFogShapesEdit}
|
||||||
active={selectedToolId === "fog"}
|
active={selectedToolId === "fog"}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
import { Group, Rect, Line, Circle } from "react-konva";
|
import { Group, Rect, Line } from "react-konva";
|
||||||
import useImage from "use-image";
|
import useImage from "use-image";
|
||||||
|
|
||||||
import diagonalPattern from "../../images/DiagonalPattern.png";
|
import diagonalPattern from "../../images/DiagonalPattern.png";
|
||||||
@ -18,7 +18,6 @@ import {
|
|||||||
getGuidesFromBoundingBoxes,
|
getGuidesFromBoundingBoxes,
|
||||||
getGuidesFromGridCell,
|
getGuidesFromGridCell,
|
||||||
findBestGuides,
|
findBestGuides,
|
||||||
getSnappingVertex,
|
|
||||||
} from "../../helpers/drawing";
|
} from "../../helpers/drawing";
|
||||||
import colors from "../../helpers/colors";
|
import colors from "../../helpers/colors";
|
||||||
import {
|
import {
|
||||||
@ -27,13 +26,15 @@ import {
|
|||||||
getRelativePointerPosition,
|
getRelativePointerPosition,
|
||||||
} from "../../helpers/konva";
|
} from "../../helpers/konva";
|
||||||
|
|
||||||
|
import SubtractShapeAction from "../../actions/SubtractShapeAction";
|
||||||
|
|
||||||
import useSetting from "../../hooks/useSetting";
|
import useSetting from "../../hooks/useSetting";
|
||||||
|
|
||||||
function MapFog({
|
function MapFog({
|
||||||
map,
|
map,
|
||||||
shapes,
|
shapes,
|
||||||
onShapeAdd,
|
onShapesAdd,
|
||||||
onShapeCut,
|
onShapesCut,
|
||||||
onShapesRemove,
|
onShapesRemove,
|
||||||
onShapesEdit,
|
onShapesEdit,
|
||||||
active,
|
active,
|
||||||
@ -66,7 +67,6 @@ function MapFog({
|
|||||||
// Bounding boxes for guides
|
// Bounding boxes for guides
|
||||||
const [fogShapeBoundingBoxes, setFogShapeBoundingBoxes] = useState([]);
|
const [fogShapeBoundingBoxes, setFogShapeBoundingBoxes] = useState([]);
|
||||||
const [guides, setGuides] = useState([]);
|
const [guides, setGuides] = useState([]);
|
||||||
const [vertexSnapping, setVertexSnapping] = useState();
|
|
||||||
|
|
||||||
const shouldHover =
|
const shouldHover =
|
||||||
active &&
|
active &&
|
||||||
@ -76,16 +76,7 @@ function MapFog({
|
|||||||
const shouldRenderGuides =
|
const shouldRenderGuides =
|
||||||
active &&
|
active &&
|
||||||
editable &&
|
editable &&
|
||||||
(toolSettings.type === "rectangle" || toolSettings.type === "polygon") &&
|
(toolSettings.type === "rectangle" || toolSettings.type === "polygon");
|
||||||
!vertexSnapping;
|
|
||||||
const shouldRenderVertexSnapping =
|
|
||||||
active &&
|
|
||||||
editable &&
|
|
||||||
(toolSettings.type === "rectangle" ||
|
|
||||||
toolSettings.type === "polygon" ||
|
|
||||||
toolSettings.type === "brush") &&
|
|
||||||
toolSettings.useEdgeSnapping &&
|
|
||||||
vertexSnapping;
|
|
||||||
|
|
||||||
const [patternImage] = useImage(diagonalPattern);
|
const [patternImage] = useImage(diagonalPattern);
|
||||||
|
|
||||||
@ -99,20 +90,13 @@ function MapFog({
|
|||||||
function getBrushPosition(snapping = true) {
|
function getBrushPosition(snapping = true) {
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
const mapImage = mapStage.findOne("#mapImage");
|
||||||
let position = getRelativePointerPosition(mapImage);
|
let position = getRelativePointerPosition(mapImage);
|
||||||
if (snapping) {
|
if (snapping && shouldRenderGuides) {
|
||||||
if (shouldRenderVertexSnapping) {
|
for (let guide of guides) {
|
||||||
position = Vector2.multiply(vertexSnapping, {
|
if (guide.orientation === "vertical") {
|
||||||
x: mapWidth,
|
position.x = guide.start.x * mapWidth;
|
||||||
y: mapHeight,
|
}
|
||||||
});
|
if (guide.orientation === "horizontal") {
|
||||||
} else if (shouldRenderGuides) {
|
position.y = guide.start.y * mapHeight;
|
||||||
for (let guide of guides) {
|
|
||||||
if (guide.orientation === "vertical") {
|
|
||||||
position.x = guide.start.x * mapWidth;
|
|
||||||
}
|
|
||||||
if (guide.orientation === "horizontal") {
|
|
||||||
position.y = guide.start.y * mapHeight;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,33 +188,53 @@ function MapFog({
|
|||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp() {
|
||||||
if (
|
if (
|
||||||
toolSettings.type === "brush" ||
|
(toolSettings.type === "brush" || toolSettings.type === "rectangle") &&
|
||||||
(toolSettings.type === "rectangle" && drawingShape)
|
drawingShape
|
||||||
) {
|
) {
|
||||||
const cut = toolSettings.useFogCut;
|
const cut = toolSettings.useFogCut;
|
||||||
if (drawingShape.data.points.length > 1) {
|
|
||||||
let shapeData = {};
|
let drawingShapes = [drawingShape];
|
||||||
|
if (!toolSettings.multilayer) {
|
||||||
|
const shapesToSubtract = shapes.filter((shape) =>
|
||||||
|
cut ? !shape.visible : shape.visible
|
||||||
|
);
|
||||||
|
const subtractAction = new SubtractShapeAction(
|
||||||
|
mergeFogShapes(shapesToSubtract, !cut)
|
||||||
|
);
|
||||||
|
const state = subtractAction.execute({
|
||||||
|
[drawingShape.id]: drawingShape,
|
||||||
|
});
|
||||||
|
drawingShapes = Object.values(state)
|
||||||
|
.filter((shape) => shape.data.points.length > 2)
|
||||||
|
.map((shape) => ({ ...shape, id: shortid.generate() }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawingShapes.length > 0) {
|
||||||
|
drawingShapes = drawingShapes.map((shape) => {
|
||||||
|
let shapeData = {};
|
||||||
|
if (cut) {
|
||||||
|
shapeData = { id: shape.id, type: shape.type };
|
||||||
|
} else {
|
||||||
|
shapeData = { ...shape, color: "black" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...shapeData,
|
||||||
|
data: {
|
||||||
|
...shape.data,
|
||||||
|
points: simplifyPoints(
|
||||||
|
shape.data.points,
|
||||||
|
gridCellNormalizedSize,
|
||||||
|
// Downscale fog as smoothing doesn't currently work with edge snapping
|
||||||
|
Math.max(stageScale, 1) / 2
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
if (cut) {
|
if (cut) {
|
||||||
shapeData = { id: drawingShape.id, type: drawingShape.type };
|
onShapesCut(drawingShapes);
|
||||||
} else {
|
} else {
|
||||||
shapeData = { ...drawingShape, color: "black" };
|
onShapesAdd(drawingShapes);
|
||||||
}
|
|
||||||
const shape = {
|
|
||||||
...shapeData,
|
|
||||||
data: {
|
|
||||||
...drawingShape.data,
|
|
||||||
points: simplifyPoints(
|
|
||||||
drawingShape.data.points,
|
|
||||||
gridCellNormalizedSize,
|
|
||||||
// Downscale fog as smoothing doesn't currently work with edge snapping
|
|
||||||
stageScale / 2
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (cut) {
|
|
||||||
onShapeCut(shape);
|
|
||||||
} else {
|
|
||||||
onShapeAdd(shape);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setDrawingShape(null);
|
setDrawingShape(null);
|
||||||
@ -273,9 +277,7 @@ function MapFog({
|
|||||||
function handlePolygonMove() {
|
function handlePolygonMove() {
|
||||||
if (
|
if (
|
||||||
active &&
|
active &&
|
||||||
(toolSettings.type === "polygon" ||
|
(toolSettings.type === "polygon" || toolSettings.type === "rectangle")
|
||||||
toolSettings.type === "rectangle") &&
|
|
||||||
!shouldRenderVertexSnapping
|
|
||||||
) {
|
) {
|
||||||
let guides = [];
|
let guides = [];
|
||||||
const brushPosition = getBrushPosition(false);
|
const brushPosition = getBrushPosition(false);
|
||||||
@ -308,24 +310,6 @@ function MapFog({
|
|||||||
|
|
||||||
setGuides(findBestGuides(brushPosition, guides));
|
setGuides(findBestGuides(brushPosition, guides));
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
active &&
|
|
||||||
toolSettings.useEdgeSnapping &&
|
|
||||||
(toolSettings.type === "polygon" ||
|
|
||||||
toolSettings.type === "rectangle" ||
|
|
||||||
toolSettings.type === "brush")
|
|
||||||
) {
|
|
||||||
const brushPosition = getBrushPosition(false);
|
|
||||||
setVertexSnapping(
|
|
||||||
getSnappingVertex(
|
|
||||||
brushPosition,
|
|
||||||
fogShapes,
|
|
||||||
fogShapeBoundingBoxes,
|
|
||||||
gridCellNormalizedSize,
|
|
||||||
Math.min(0.4 / stageScale, 0.4)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (toolSettings.type === "polygon") {
|
if (toolSettings.type === "polygon") {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
if (toolSettings.type === "polygon" && drawingShape) {
|
if (toolSettings.type === "polygon" && drawingShape) {
|
||||||
@ -365,23 +349,50 @@ function MapFog({
|
|||||||
|
|
||||||
const finishDrawingPolygon = useCallback(() => {
|
const finishDrawingPolygon = useCallback(() => {
|
||||||
const cut = toolSettings.useFogCut;
|
const cut = toolSettings.useFogCut;
|
||||||
const data = {
|
|
||||||
...drawingShape.data,
|
let polygonShape = {
|
||||||
// Remove the last point as it hasn't been placed yet
|
id: drawingShape.id,
|
||||||
points: drawingShape.data.points.slice(0, -1),
|
type: drawingShape.type,
|
||||||
|
data: {
|
||||||
|
...drawingShape.data,
|
||||||
|
// Remove the last point as it hasn't been placed yet
|
||||||
|
points: drawingShape.data.points.slice(0, -1),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (cut) {
|
|
||||||
onShapeCut({
|
let polygonShapes = [polygonShape];
|
||||||
id: drawingShape.id,
|
if (!toolSettings.multilayer) {
|
||||||
type: drawingShape.type,
|
const shapesToSubtract = shapes.filter((shape) =>
|
||||||
data: data,
|
cut ? !shape.visible : shape.visible
|
||||||
|
);
|
||||||
|
const subtractAction = new SubtractShapeAction(
|
||||||
|
mergeFogShapes(shapesToSubtract, !cut)
|
||||||
|
);
|
||||||
|
const state = subtractAction.execute({
|
||||||
|
[polygonShape.id]: polygonShape,
|
||||||
});
|
});
|
||||||
} else {
|
polygonShapes = Object.values(state)
|
||||||
onShapeAdd({ ...drawingShape, data: data, color: "black" });
|
.filter((shape) => shape.data.points.length > 2)
|
||||||
|
.map((shape) => ({ ...shape, id: shortid.generate() }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polygonShapes.length > 0) {
|
||||||
|
if (cut) {
|
||||||
|
onShapesCut(polygonShapes);
|
||||||
|
} else {
|
||||||
|
onShapesAdd(
|
||||||
|
polygonShapes.map((shape) => ({
|
||||||
|
...drawingShape,
|
||||||
|
data: shape.data,
|
||||||
|
id: shape.id,
|
||||||
|
color: "black",
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setDrawingShape(null);
|
setDrawingShape(null);
|
||||||
}, [toolSettings, drawingShape, onShapeCut, onShapeAdd]);
|
}, [toolSettings, drawingShape, onShapesCut, onShapesAdd, shapes]);
|
||||||
|
|
||||||
// Add keyboard shortcuts
|
// Add keyboard shortcuts
|
||||||
function handleKeyDown({ key }) {
|
function handleKeyDown({ key }) {
|
||||||
@ -467,9 +478,6 @@ function MapFog({
|
|||||||
// Disable collision if the fog is transparent and we're not editing it
|
// Disable collision if the fog is transparent and we're not editing it
|
||||||
// This allows tokens to be moved under the fog
|
// This allows tokens to be moved under the fog
|
||||||
hitFunc={editable && !active ? () => {} : undefined}
|
hitFunc={editable && !active ? () => {} : undefined}
|
||||||
// shadowColor={editable ? "rgba(0, 0, 0, 0)" : "rgba(34, 34, 34, 1)"}
|
|
||||||
// shadowOffset={{ x: 0, y: 5 }}
|
|
||||||
// shadowBlur={10}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -523,18 +531,6 @@ function MapFog({
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSnappingVertex() {
|
|
||||||
return (
|
|
||||||
<Circle
|
|
||||||
x={vertexSnapping.x * mapWidth}
|
|
||||||
y={vertexSnapping.y * mapHeight}
|
|
||||||
radius={gridStrokeWidth}
|
|
||||||
stroke="hsl(260, 100%, 80%)"
|
|
||||||
strokeWidth={gridStrokeWidth * 0.25}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function shapeVisible(shape) {
|
function shapeVisible(shape) {
|
||||||
return (active && !toolSettings.preview) || shape.visible;
|
return (active && !toolSettings.preview) || shape.visible;
|
||||||
@ -559,7 +555,6 @@ function MapFog({
|
|||||||
{fogShapes.map(renderShape)}
|
{fogShapes.map(renderShape)}
|
||||||
</Group>
|
</Group>
|
||||||
{shouldRenderGuides && renderGuides()}
|
{shouldRenderGuides && renderGuides()}
|
||||||
{shouldRenderVertexSnapping && renderSnappingVertex()}
|
|
||||||
{drawingShape && renderShape(drawingShape)}
|
{drawingShape && renderShape(drawingShape)}
|
||||||
{drawingShape &&
|
{drawingShape &&
|
||||||
toolSettings &&
|
toolSettings &&
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { IconButton } from "theme-ui";
|
|
||||||
|
|
||||||
import SnappingOnIcon from "../../../icons/SnappingOnIcon";
|
|
||||||
import SnappingOffIcon from "../../../icons/SnappingOffIcon";
|
|
||||||
|
|
||||||
function EdgeSnappingToggle({
|
|
||||||
useEdgeSnapping,
|
|
||||||
onEdgeSnappingChange,
|
|
||||||
disabled,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
aria-label={
|
|
||||||
useEdgeSnapping
|
|
||||||
? "Disable Edge Snapping (S)"
|
|
||||||
: "Enable Edge Snapping (S)"
|
|
||||||
}
|
|
||||||
title={
|
|
||||||
useEdgeSnapping
|
|
||||||
? "Disable Edge Snapping (S)"
|
|
||||||
: "Enable Edge Snapping (S)"
|
|
||||||
}
|
|
||||||
onClick={() => onEdgeSnappingChange(!useEdgeSnapping)}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{useEdgeSnapping ? <SnappingOnIcon /> : <SnappingOffIcon />}
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EdgeSnappingToggle;
|
|
@ -4,7 +4,7 @@ import { useMedia } from "react-media";
|
|||||||
|
|
||||||
import RadioIconButton from "../../RadioIconButton";
|
import RadioIconButton from "../../RadioIconButton";
|
||||||
|
|
||||||
import EdgeSnappingToggle from "./EdgeSnappingToggle";
|
import MultilayerToggle from "./MultilayerToggle";
|
||||||
import FogPreviewToggle from "./FogPreviewToggle";
|
import FogPreviewToggle from "./FogPreviewToggle";
|
||||||
import FogCutToggle from "./FogCutToggle";
|
import FogCutToggle from "./FogCutToggle";
|
||||||
|
|
||||||
@ -40,8 +40,8 @@ function BrushToolSettings({
|
|||||||
onSettingChange({ type: "toggle" });
|
onSettingChange({ type: "toggle" });
|
||||||
} else if (key === "e") {
|
} else if (key === "e") {
|
||||||
onSettingChange({ type: "remove" });
|
onSettingChange({ type: "remove" });
|
||||||
} else if (key === "s") {
|
} else if (key === "l") {
|
||||||
onSettingChange({ useEdgeSnapping: !settings.useEdgeSnapping });
|
onSettingChange({ multilayer: !settings.multilayer });
|
||||||
} else if (key === "f") {
|
} else if (key === "f") {
|
||||||
onSettingChange({ preview: !settings.preview });
|
onSettingChange({ preview: !settings.preview });
|
||||||
} else if (key === "c") {
|
} else if (key === "c") {
|
||||||
@ -128,11 +128,9 @@ function BrushToolSettings({
|
|||||||
onFogCutChange={(useFogCut) => onSettingChange({ useFogCut })}
|
onFogCutChange={(useFogCut) => onSettingChange({ useFogCut })}
|
||||||
disabled={settings.preview}
|
disabled={settings.preview}
|
||||||
/>
|
/>
|
||||||
<EdgeSnappingToggle
|
<MultilayerToggle
|
||||||
useEdgeSnapping={settings.useEdgeSnapping}
|
multilayer={settings.multilayer}
|
||||||
onEdgeSnappingChange={(useEdgeSnapping) =>
|
onMultilayerChange={(multilayer) => onSettingChange({ multilayer })}
|
||||||
onSettingChange({ useEdgeSnapping })
|
|
||||||
}
|
|
||||||
disabled={settings.preview}
|
disabled={settings.preview}
|
||||||
/>
|
/>
|
||||||
<FogPreviewToggle
|
<FogPreviewToggle
|
||||||
|
22
src/components/map/controls/MultilayerToggle.js
Normal file
22
src/components/map/controls/MultilayerToggle.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IconButton } from "theme-ui";
|
||||||
|
|
||||||
|
import MultilayerOnIcon from "../../../icons/FogMultilayerOnIcon";
|
||||||
|
import MultilayerOffIcon from "../../../icons/FogMultilayerOffIcon";
|
||||||
|
|
||||||
|
function MultilayerToggle({ multilayer, onMultilayerChange, disabled }) {
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
aria-label={
|
||||||
|
multilayer ? "Disable Multilayer (L)" : "Enable Multilayer (L)"
|
||||||
|
}
|
||||||
|
title={multilayer ? "Disable Multilayer (L)" : "Enable Multilayer (L)"}
|
||||||
|
onClick={() => onMultilayerChange(!multilayer)}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{multilayer ? <MultilayerOnIcon /> : <MultilayerOffIcon />}
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MultilayerToggle;
|
@ -9,11 +9,13 @@ export function addPolygonDifferenceToShapes(shape, difference, shapes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const points = difference[i][0].map(([x, y]) => ({ x, y }));
|
||||||
|
|
||||||
shapes[newId] = {
|
shapes[newId] = {
|
||||||
...shape,
|
...shape,
|
||||||
id: newId,
|
id: newId,
|
||||||
data: {
|
data: {
|
||||||
points: difference[i][0].map(([x, y]) => ({ x, y })),
|
points,
|
||||||
holes,
|
holes,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -23,11 +25,14 @@ export function addPolygonDifferenceToShapes(shape, difference, shapes) {
|
|||||||
export function addPolygonIntersectionToShapes(shape, intersection, shapes) {
|
export function addPolygonIntersectionToShapes(shape, intersection, shapes) {
|
||||||
for (let i = 0; i < intersection.length; i++) {
|
for (let i = 0; i < intersection.length; i++) {
|
||||||
let newId = `${shape.id}-int-${i}`;
|
let newId = `${shape.id}-int-${i}`;
|
||||||
|
|
||||||
|
const points = intersection[i][0].map(([x, y]) => ({ x, y }));
|
||||||
|
|
||||||
shapes[newId] = {
|
shapes[newId] = {
|
||||||
...shape,
|
...shape,
|
||||||
id: newId,
|
id: newId,
|
||||||
data: {
|
data: {
|
||||||
points: intersection[i][0].map(([x, y]) => ({ x, y })),
|
points,
|
||||||
holes: [],
|
holes: [],
|
||||||
},
|
},
|
||||||
// Default intersection visibility to false
|
// Default intersection visibility to false
|
||||||
|
@ -210,15 +210,16 @@ export function simplifyPoints(points, gridCellSize, scale) {
|
|||||||
/**
|
/**
|
||||||
* Merges overlapping fog shapes
|
* Merges overlapping fog shapes
|
||||||
* @param {Fog[]} shapes
|
* @param {Fog[]} shapes
|
||||||
|
* @param {boolean} ignoreHidden
|
||||||
* @returns {Fog[]}
|
* @returns {Fog[]}
|
||||||
*/
|
*/
|
||||||
export function mergeFogShapes(shapes) {
|
export function mergeFogShapes(shapes, ignoreHidden = true) {
|
||||||
if (shapes.length === 0) {
|
if (shapes.length === 0) {
|
||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
let geometries = [];
|
let geometries = [];
|
||||||
for (let shape of shapes) {
|
for (let shape of shapes) {
|
||||||
if (!shape.visible) {
|
if (ignoreHidden && !shape.visible) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const shapePoints = shape.data.points.map(({ x, y }) => [x, y]);
|
const shapePoints = shape.data.points.map(({ x, y }) => [x, y]);
|
||||||
@ -243,7 +244,7 @@ export function mergeFogShapes(shapes) {
|
|||||||
}
|
}
|
||||||
merged.push({
|
merged.push({
|
||||||
// Use the data of the first visible shape as the merge
|
// Use the data of the first visible shape as the merge
|
||||||
...shapes.find((shape) => shape.visible),
|
...shapes.find((shape) => ignoreHidden || shape.visible),
|
||||||
id: `merged-${i}`,
|
id: `merged-${i}`,
|
||||||
data: {
|
data: {
|
||||||
points: union[i][0].map(([x, y]) => ({ x, y })),
|
points: union[i][0].map(([x, y]) => ({ x, y })),
|
||||||
@ -253,7 +254,7 @@ export function mergeFogShapes(shapes) {
|
|||||||
}
|
}
|
||||||
return merged;
|
return merged;
|
||||||
} catch {
|
} catch {
|
||||||
logError(new Error(`Unable to merge shapes ${JSON.stringify(shapes)}`));
|
console.error("Unable to merge shapes");
|
||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,69 +465,3 @@ export function findBestGuides(brushPosition, guides) {
|
|||||||
}
|
}
|
||||||
return bestGuides;
|
return bestGuides;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Vector2} brushPosition
|
|
||||||
* @param {Fog[]} shapes
|
|
||||||
* @param {Vector2.BoundingBox} boundingBoxes
|
|
||||||
* @param {Vector2} gridCellSize
|
|
||||||
* @param {number} snappingSensitivity
|
|
||||||
*/
|
|
||||||
export function getSnappingVertex(
|
|
||||||
brushPosition,
|
|
||||||
shapes,
|
|
||||||
boundingBoxes,
|
|
||||||
gridCellSize,
|
|
||||||
snappingSensitivity
|
|
||||||
) {
|
|
||||||
const minGrid = Vector2.min(gridCellSize);
|
|
||||||
const snappingDistance = minGrid * snappingSensitivity;
|
|
||||||
|
|
||||||
let closestDistance = Number.MAX_VALUE;
|
|
||||||
let closestPosition;
|
|
||||||
for (let i = 0; i < shapes.length; i++) {
|
|
||||||
// Check bounds before checking all points
|
|
||||||
const bounds = boundingBoxes[i];
|
|
||||||
const offsetMin = Vector2.subtract(bounds.min, gridCellSize);
|
|
||||||
const offsetMax = Vector2.add(bounds.max, gridCellSize);
|
|
||||||
if (
|
|
||||||
brushPosition.x < offsetMin.x ||
|
|
||||||
brushPosition.x > offsetMax.x ||
|
|
||||||
brushPosition.y < offsetMin.y ||
|
|
||||||
brushPosition.y > offsetMax.y
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const shape = shapes[i];
|
|
||||||
// Include shape points and holes
|
|
||||||
let pointArray = [shape.data.points, ...shape.data.holes];
|
|
||||||
|
|
||||||
for (let points of pointArray) {
|
|
||||||
// Find the closest point to each edge of the shape
|
|
||||||
for (let i = 0; i < points.length; i++) {
|
|
||||||
const a = points[i];
|
|
||||||
// Wrap around points to the start to account for closed shape
|
|
||||||
const b = points[(i + 1) % points.length];
|
|
||||||
|
|
||||||
let { distance, point } = Vector2.distanceToLine(brushPosition, a, b);
|
|
||||||
// Bias towards vertices
|
|
||||||
distance += snappingDistance / 2;
|
|
||||||
const isCloseToShape = distance < snappingDistance;
|
|
||||||
if (isCloseToShape && distance < closestDistance) {
|
|
||||||
closestPosition = point;
|
|
||||||
closestDistance = distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Find cloest vertex
|
|
||||||
for (let point of points) {
|
|
||||||
const distance = Vector2.distance(point, brushPosition);
|
|
||||||
const isCloseToShape = distance < snappingDistance;
|
|
||||||
if (isCloseToShape && distance < closestDistance) {
|
|
||||||
closestPosition = point;
|
|
||||||
closestDistance = distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return closestPosition;
|
|
||||||
}
|
|
||||||
|
20
src/icons/FogMultilayerOffIcon.js
Normal file
20
src/icons/FogMultilayerOffIcon.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function FogMultilayerOffIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
fill="currentcolor"
|
||||||
|
>
|
||||||
|
<rect fill="none" height="24" width="24" y="0" />
|
||||||
|
<path d="M19.97,13.2c-0.36-0.28-0.86-0.28-1.22,0l-1.23,0.96l1.43,1.43l1.03-0.8c0.51-0.4,0.51-1.17,0-1.57L19.97,13.2z" />
|
||||||
|
<path d="M19.99,9.71c0.51-0.4,0.51-1.18,0-1.58l-6.76-5.26c-0.72-0.56-1.73-0.56-2.46,0L8.23,4.86l7.88,7.88L19.99,9.71z" />
|
||||||
|
<path d="M20.48,19.94L3.51,2.97c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41l2.95,2.95L4.01,8.14 c-0.51,0.4-0.51,1.18,0,1.58l6.76,5.26c0.61,0.48,1.43,0.52,2.11,0.18l1.47,1.47l-2.36,1.84l-6.77-5.26 c-0.36-0.28-0.86-0.28-1.22,0c-0.51,0.4-0.51,1.17,0,1.57l6.76,5.26c0.72,0.56,1.73,0.56,2.46,0l2.55-1.98l3.3,3.3 c0.39,0.39,1.02,0.39,1.41,0C20.88,20.97,20.88,20.33,20.48,19.94z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FogMultilayerOffIcon;
|
18
src/icons/FogMultilayerOnIcon.js
Normal file
18
src/icons/FogMultilayerOnIcon.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function FogMultilayerOnIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
fill="currentcolor"
|
||||||
|
>
|
||||||
|
<rect fill="none" height="24" width="24" y="0" />
|
||||||
|
<path d="M11.99,18.47l-6.77-5.26c-0.36-0.28-0.86-0.28-1.22,0l0,0c-0.51,0.4-0.51,1.17,0,1.57l6.76,5.26 c0.72,0.56,1.73,0.56,2.46,0l6.76-5.26c0.51-0.4,0.51-1.17,0-1.57l-0.01-0.01c-0.36-0.28-0.86-0.28-1.22,0L11.99,18.47z M13.23,14.97l6.76-5.26c0.51-0.4,0.51-1.18,0-1.58l-6.76-5.26c-0.72-0.56-1.73-0.56-2.46,0L4.01,8.14c-0.51,0.4-0.51,1.18,0,1.58 l6.76,5.26C11.49,15.54,12.51,15.54,13.23,14.97z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FogMultilayerOnIcon;
|
@ -1,18 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
function SnappingOffIcon() {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="24"
|
|
||||||
fill="currentcolor"
|
|
||||||
>
|
|
||||||
<path d="M24 24H0V0h24z" fill="none" />
|
|
||||||
<path d="M10 5.59L6.62 2.2a1 1 0 00-.2-.15c.14-.03.27-.05.4-.05L7 2h.48c1.06 0 2.4.83 2.51 1.86L10 4v1.59zm10.88 10.88L16 11.59V4a2 2 0 011.85-2H19a3 3 0 013 2.82v6.87c0 1.69-.37 3.34-1.12 4.78zM13 21c-5.36 0-8.87-3.7-9-8.72V4.83l-.9-.9A1 1 0 014.51 2.5l.37.37a3 3 0 01.04-.04v.08l16.15 16.15a1 1 0 01-1.41 1.42l-1.17-1.17A8.98 8.98 0 0113 21zm5-12.97h.28c.55 0 1.72-.01 1.72-.03V5a1 1 0 00-.88-1H18v4.03zM6 8h.06L7.17 8 6 6.83V8zM13 15a3 3 0 00.98-.18L10 10.83V12c0 1.98 1.72 2.99 3 2.99z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SnappingOffIcon;
|
|
@ -1,18 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
function SnappingOnIcon() {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="24"
|
|
||||||
fill="currentcolor"
|
|
||||||
>
|
|
||||||
<path d="M24 24H0V0h24z" fill="none" />
|
|
||||||
<path d="M13 21c-5.46 0-9-3.83-9-9V5a3 3 0 013-3h.48C8.58 2 10 2.9 10 4v8c0 1.98 1.72 2.99 3 2.99S16 14 16 12V4c0-1.1.9-2 2-2h1a3 3 0 013 3v6.69c0 4.79-3 9.31-9 9.31zm5-12.97h.28c.55 0 1.72-.01 1.72-.03V5a1 1 0 00-.88-1H18v4.03zM6 8h.06L8 8V4.2h-.01l-.02-.01c-.13-.1-.3-.17-.41-.2H7a1 1 0 00-1 .88v3.13z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SnappingOnIcon;
|
|
@ -47,6 +47,13 @@ function loadVersions(settings) {
|
|||||||
delete newSettings.measure;
|
delete newSettings.measure;
|
||||||
return newSettings;
|
return newSettings;
|
||||||
});
|
});
|
||||||
|
// v1.8.0 - Removed edge snapping for multilayer
|
||||||
|
settings.version(5, (prev) => {
|
||||||
|
let newSettings = { ...prev };
|
||||||
|
delete newSettings.fog.useEdgeSnapping;
|
||||||
|
newSettings.fog.multilayer = false;
|
||||||
|
return newSettings;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSettings() {
|
export function getSettings() {
|
||||||
|
Loading…
Reference in New Issue
Block a user