Added line tool
This commit is contained in:
parent
80b711296b
commit
cac2af1608
@ -1,5 +1,4 @@
|
||||
import React, { useState, useContext, useEffect } from "react";
|
||||
import polygonClipping from "polygon-clipping";
|
||||
|
||||
import MapControls from "./MapControls";
|
||||
import MapInteraction from "./MapInteraction";
|
||||
@ -16,7 +15,7 @@ import TokenMenu from "../token/TokenMenu";
|
||||
import TokenDragOverlay from "../token/TokenDragOverlay";
|
||||
import LoadingOverlay from "../LoadingOverlay";
|
||||
|
||||
import { omit } from "../../helpers/shared";
|
||||
import { drawActionsToShapes } from "../../helpers/drawing";
|
||||
|
||||
function Map({
|
||||
map,
|
||||
@ -121,64 +120,11 @@ function Map({
|
||||
if (!mapState) {
|
||||
return;
|
||||
}
|
||||
function actionsToShapes(actions, actionIndex) {
|
||||
let shapesById = {};
|
||||
for (let i = 0; i <= actionIndex; i++) {
|
||||
const action = actions[i];
|
||||
if (action.type === "add" || action.type === "edit") {
|
||||
for (let shape of action.shapes) {
|
||||
shapesById[shape.id] = shape;
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return Object.values(shapesById);
|
||||
}
|
||||
|
||||
setMapShapes(
|
||||
actionsToShapes(mapState.mapDrawActions, mapState.mapDrawActionIndex)
|
||||
drawActionsToShapes(mapState.mapDrawActions, mapState.mapDrawActionIndex)
|
||||
);
|
||||
setFogShapes(
|
||||
actionsToShapes(mapState.fogDrawActions, mapState.fogDrawActionIndex)
|
||||
drawActionsToShapes(mapState.fogDrawActions, mapState.fogDrawActionIndex)
|
||||
);
|
||||
}, [mapState]);
|
||||
|
||||
|
@ -42,7 +42,8 @@ function MapDrawing({
|
||||
selectedToolSettings.type === "paint");
|
||||
const isShape =
|
||||
selectedToolSettings &&
|
||||
(selectedToolSettings.type === "rectangle" ||
|
||||
(selectedToolSettings.type === "line" ||
|
||||
selectedToolSettings.type === "rectangle" ||
|
||||
selectedToolSettings.type === "circle" ||
|
||||
selectedToolSettings.type === "triangle");
|
||||
|
||||
@ -83,7 +84,7 @@ function MapDrawing({
|
||||
type: "shape",
|
||||
shapeType: selectedToolSettings.type,
|
||||
data: getDefaultShapeData(selectedToolSettings.type, brushPosition),
|
||||
strokeWidth: 0,
|
||||
strokeWidth: selectedToolSettings.type === "line" ? 1 : 0,
|
||||
...commonShapeData,
|
||||
});
|
||||
}
|
||||
@ -246,6 +247,24 @@ function MapDrawing({
|
||||
{...defaultProps}
|
||||
/>
|
||||
);
|
||||
} else if (shape.shapeType === "line") {
|
||||
return (
|
||||
<Line
|
||||
points={shape.data.points.reduce(
|
||||
(acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight],
|
||||
[]
|
||||
)}
|
||||
strokeWidth={getStrokeWidth(
|
||||
shape.strokeWidth,
|
||||
gridSize,
|
||||
mapWidth,
|
||||
mapHeight
|
||||
)}
|
||||
stroke={colors[shape.color] || shape.color}
|
||||
lineCap="round"
|
||||
{...defaultProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ import AlphaBlendToggle from "./AlphaBlendToggle";
|
||||
import RadioIconButton from "./RadioIconButton";
|
||||
|
||||
import BrushIcon from "../../../icons/BrushToolIcon";
|
||||
import BrushFillIcon from "../../../icons/BrushPaintIcon";
|
||||
import BrushPaintIcon from "../../../icons/BrushPaintIcon";
|
||||
import BrushLineIcon from "../../../icons/BrushLineIcon";
|
||||
import BrushRectangleIcon from "../../../icons/BrushRectangleIcon";
|
||||
import BrushCircleIcon from "../../../icons/BrushCircleIcon";
|
||||
import BrushTriangleIcon from "../../../icons/BrushTriangleIcon";
|
||||
@ -35,6 +36,8 @@ function DrawingToolSettings({
|
||||
onSettingChange({ type: "brush" });
|
||||
} else if (key === "p") {
|
||||
onSettingChange({ type: "paint" });
|
||||
} else if (key === "l") {
|
||||
onSettingChange({ type: "line" });
|
||||
} else if (key === "r") {
|
||||
onSettingChange({ type: "rectangle" });
|
||||
} else if (key === "c") {
|
||||
@ -94,7 +97,14 @@ function DrawingToolSettings({
|
||||
onClick={() => onSettingChange({ type: "paint" })}
|
||||
isSelected={settings.type === "paint"}
|
||||
>
|
||||
<BrushFillIcon />
|
||||
<BrushPaintIcon />
|
||||
</RadioIconButton>
|
||||
<RadioIconButton
|
||||
title="Line"
|
||||
onClick={() => onSettingChange({ type: "line" })}
|
||||
isSelected={settings.type === "line"}
|
||||
>
|
||||
<BrushLineIcon />
|
||||
</RadioIconButton>
|
||||
<RadioIconButton
|
||||
title="Rectangle"
|
||||
|
@ -1,7 +1,8 @@
|
||||
import simplify from "simplify-js";
|
||||
import polygonClipping from "polygon-clipping";
|
||||
|
||||
import * as Vector2 from "./vector2";
|
||||
import { toDegrees } from "./shared";
|
||||
import { toDegrees, omit } from "./shared";
|
||||
|
||||
const snappingThreshold = 1 / 5;
|
||||
export function getBrushPositionForTool(
|
||||
@ -14,7 +15,8 @@ export function getBrushPositionForTool(
|
||||
let position = brushPosition;
|
||||
const useGridSnappning =
|
||||
(tool === "drawing" &&
|
||||
(toolSettings.type === "rectangle" ||
|
||||
(toolSettings.type === "line" ||
|
||||
toolSettings.type === "rectangle" ||
|
||||
toolSettings.type === "circle" ||
|
||||
toolSettings.type === "triangle")) ||
|
||||
(tool === "fog" && toolSettings.type === "polygon");
|
||||
@ -92,7 +94,14 @@ export function getBrushPositionForTool(
|
||||
}
|
||||
|
||||
export function getDefaultShapeData(type, brushPosition) {
|
||||
if (type === "circle") {
|
||||
if (type === "line") {
|
||||
return {
|
||||
points: [
|
||||
{ x: brushPosition.x, y: brushPosition.y },
|
||||
{ x: brushPosition.x, y: brushPosition.y },
|
||||
],
|
||||
};
|
||||
} else if (type === "circle") {
|
||||
return { x: brushPosition.x, y: brushPosition.y, radius: 0 };
|
||||
} else if (type === "rectangle") {
|
||||
return {
|
||||
@ -124,7 +133,11 @@ export function getGridScale(gridSize) {
|
||||
|
||||
export function getUpdatedShapeData(type, data, brushPosition, gridSize) {
|
||||
const gridScale = getGridScale(gridSize);
|
||||
if (type === "circle") {
|
||||
if (type === "line") {
|
||||
return {
|
||||
points: [data.points[0], { x: brushPosition.x, y: brushPosition.y }],
|
||||
};
|
||||
} else if (type === "circle") {
|
||||
const dif = Vector2.subtract(brushPosition, {
|
||||
x: data.x,
|
||||
y: data.y,
|
||||
@ -185,3 +198,53 @@ export function simplifyPoints(points, gridSize, scale) {
|
||||
(Vector2.min(gridSize) * defaultSimplifySize) / scale
|
||||
);
|
||||
}
|
||||
|
||||
export function drawActionsToShapes(actions, actionIndex) {
|
||||
let shapesById = {};
|
||||
for (let i = 0; i <= actionIndex; i++) {
|
||||
const action = actions[i];
|
||||
if (action.type === "add" || action.type === "edit") {
|
||||
for (let shape of action.shapes) {
|
||||
shapesById[shape.id] = shape;
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return Object.values(shapesById);
|
||||
}
|
||||
|
18
src/icons/BrushLineIcon.js
Normal file
18
src/icons/BrushLineIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function BrushLineIcon() {
|
||||
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="M4.222 16.95L16.95 4.222a2 2 0 112.828 2.828L7.05 19.778a2 2 0 11-2.828-2.828z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default BrushLineIcon;
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
function BrushFillIcon() {
|
||||
function BrushPaintIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -15,4 +15,4 @@ function BrushFillIcon() {
|
||||
);
|
||||
}
|
||||
|
||||
export default BrushFillIcon;
|
||||
export default BrushPaintIcon;
|
||||
|
Loading…
Reference in New Issue
Block a user