Separated fog snapping and drawing snapping, added square edge snapping
This commit is contained in:
parent
3abf9c62fa
commit
5219815a1f
@ -7,13 +7,12 @@ import MapStageContext from "../../contexts/MapStageContext";
|
|||||||
|
|
||||||
import { compare as comparePoints } from "../../helpers/vector2";
|
import { compare as comparePoints } from "../../helpers/vector2";
|
||||||
import {
|
import {
|
||||||
getBrushPositionForTool,
|
getBrushPosition,
|
||||||
getDefaultShapeData,
|
getDefaultShapeData,
|
||||||
getUpdatedShapeData,
|
getUpdatedShapeData,
|
||||||
simplifyPoints,
|
simplifyPoints,
|
||||||
getStrokeWidth,
|
getStrokeWidth,
|
||||||
} from "../../helpers/drawing";
|
} from "../../helpers/drawing";
|
||||||
import { getRelativePointerPositionNormalized } from "../../helpers/konva";
|
|
||||||
|
|
||||||
import colors from "../../helpers/colors";
|
import colors from "../../helpers/colors";
|
||||||
|
|
||||||
@ -49,20 +48,13 @@ function MapDrawing({
|
|||||||
}
|
}
|
||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
|
||||||
return getBrushPositionForTool(
|
|
||||||
map,
|
|
||||||
getRelativePointerPositionNormalized(mapImage),
|
|
||||||
map.snapToGrid && isShape,
|
|
||||||
false,
|
|
||||||
gridSize,
|
|
||||||
shapes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
map.snapToGrid && isShape,
|
||||||
|
gridSize
|
||||||
|
);
|
||||||
const commonShapeData = {
|
const commonShapeData = {
|
||||||
color: toolSettings.color,
|
color: toolSettings.color,
|
||||||
blend: toolSettings.useBlending,
|
blend: toolSettings.useBlending,
|
||||||
@ -89,7 +81,12 @@ function MapDrawing({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
map.snapToGrid && isShape,
|
||||||
|
gridSize
|
||||||
|
);
|
||||||
if (isBrushDown && drawingShape) {
|
if (isBrushDown && drawingShape) {
|
||||||
if (isBrush) {
|
if (isBrush) {
|
||||||
setDrawingShape((prevShape) => {
|
setDrawingShape((prevShape) => {
|
||||||
|
@ -16,17 +16,13 @@ import MapStageContext from "../../contexts/MapStageContext";
|
|||||||
|
|
||||||
import { compare as comparePoints } from "../../helpers/vector2";
|
import { compare as comparePoints } from "../../helpers/vector2";
|
||||||
import {
|
import {
|
||||||
getBrushPositionForTool,
|
getFogBrushPosition,
|
||||||
simplifyPoints,
|
simplifyPoints,
|
||||||
getStrokeWidth,
|
getStrokeWidth,
|
||||||
mergeShapes,
|
mergeShapes,
|
||||||
} from "../../helpers/drawing";
|
} from "../../helpers/drawing";
|
||||||
import colors from "../../helpers/colors";
|
import colors from "../../helpers/colors";
|
||||||
import {
|
import { HoleyLine, Tick } from "../../helpers/konva";
|
||||||
HoleyLine,
|
|
||||||
getRelativePointerPositionNormalized,
|
|
||||||
Tick,
|
|
||||||
} from "../../helpers/konva";
|
|
||||||
import useKeyboard from "../../helpers/useKeyboard";
|
import useKeyboard from "../../helpers/useKeyboard";
|
||||||
import useDebounce from "../../helpers/useDebounce";
|
import useDebounce from "../../helpers/useDebounce";
|
||||||
|
|
||||||
@ -64,22 +60,19 @@ function MapFog({
|
|||||||
|
|
||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
const useGridSnapping =
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
map.snapToGrid &&
|
||||||
return getBrushPositionForTool(
|
(toolSettings.type === "polygon" || toolSettings.type === "rectangle");
|
||||||
map,
|
|
||||||
getRelativePointerPositionNormalized(mapImage),
|
|
||||||
map.snapToGrid &&
|
|
||||||
(toolSettings.type === "polygon" ||
|
|
||||||
toolSettings.type === "rectangle"),
|
|
||||||
toolSettings.useEdgeSnapping,
|
|
||||||
gridSize,
|
|
||||||
shapes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getFogBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
useGridSnapping,
|
||||||
|
gridSize,
|
||||||
|
toolSettings.useEdgeSnapping,
|
||||||
|
shapes
|
||||||
|
);
|
||||||
if (toolSettings.type === "brush") {
|
if (toolSettings.type === "brush") {
|
||||||
setDrawingShape({
|
setDrawingShape({
|
||||||
type: "fog",
|
type: "fog",
|
||||||
@ -116,7 +109,14 @@ function MapFog({
|
|||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
|
if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getFogBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
useGridSnapping,
|
||||||
|
gridSize,
|
||||||
|
toolSettings.useEdgeSnapping,
|
||||||
|
shapes
|
||||||
|
);
|
||||||
setDrawingShape((prevShape) => {
|
setDrawingShape((prevShape) => {
|
||||||
const prevPoints = prevShape.data.points;
|
const prevPoints = prevShape.data.points;
|
||||||
if (
|
if (
|
||||||
@ -138,9 +138,17 @@ function MapFog({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (toolSettings.type === "rectangle" && isBrushDown && drawingShape) {
|
if (toolSettings.type === "rectangle" && isBrushDown && drawingShape) {
|
||||||
const brushPosition = getBrushPosition();
|
const prevPoints = drawingShape.data.points;
|
||||||
|
const brushPosition = getFogBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
useGridSnapping,
|
||||||
|
gridSize,
|
||||||
|
toolSettings.useEdgeSnapping,
|
||||||
|
shapes,
|
||||||
|
prevPoints
|
||||||
|
);
|
||||||
setDrawingShape((prevShape) => {
|
setDrawingShape((prevShape) => {
|
||||||
const prevPoints = prevShape.data.points;
|
|
||||||
return {
|
return {
|
||||||
...prevShape,
|
...prevShape,
|
||||||
data: {
|
data: {
|
||||||
@ -198,7 +206,14 @@ function MapFog({
|
|||||||
|
|
||||||
function handlePolygonClick() {
|
function handlePolygonClick() {
|
||||||
if (toolSettings.type === "polygon") {
|
if (toolSettings.type === "polygon") {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getFogBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
useGridSnapping,
|
||||||
|
gridSize,
|
||||||
|
toolSettings.useEdgeSnapping,
|
||||||
|
shapes
|
||||||
|
);
|
||||||
setDrawingShape((prevDrawingShape) => {
|
setDrawingShape((prevDrawingShape) => {
|
||||||
if (prevDrawingShape) {
|
if (prevDrawingShape) {
|
||||||
return {
|
return {
|
||||||
@ -227,7 +242,14 @@ function MapFog({
|
|||||||
|
|
||||||
function handlePolygonMove() {
|
function handlePolygonMove() {
|
||||||
if (toolSettings.type === "polygon" && drawingShape) {
|
if (toolSettings.type === "polygon" && drawingShape) {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getFogBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
useGridSnapping,
|
||||||
|
gridSize,
|
||||||
|
toolSettings.useEdgeSnapping,
|
||||||
|
shapes
|
||||||
|
);
|
||||||
setDrawingShape((prevShape) => {
|
setDrawingShape((prevShape) => {
|
||||||
if (!prevShape) {
|
if (!prevShape) {
|
||||||
return;
|
return;
|
||||||
@ -350,7 +372,7 @@ function MapFog({
|
|||||||
onMouseUp={eraseHoveredShapes}
|
onMouseUp={eraseHoveredShapes}
|
||||||
onTouchEnd={eraseHoveredShapes}
|
onTouchEnd={eraseHoveredShapes}
|
||||||
points={points}
|
points={points}
|
||||||
stroke={colors[shape.color] || shape.color}
|
stroke={editable ? colors.white : colors[shape.color] || shape.color}
|
||||||
fill={colors[shape.color] || shape.color}
|
fill={colors[shape.color] || shape.color}
|
||||||
closed
|
closed
|
||||||
lineCap="round"
|
lineCap="round"
|
||||||
@ -425,22 +447,27 @@ function MapFog({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fogGroup = fogGroupRef.current;
|
const fogGroup = fogGroupRef.current;
|
||||||
|
|
||||||
const canvas = fogGroup.getChildren()[0].getCanvas();
|
if (!editable) {
|
||||||
const pixelRatio = canvas.pixelRatio || 1;
|
const canvas = fogGroup.getChildren()[0].getCanvas();
|
||||||
|
const pixelRatio = canvas.pixelRatio || 1;
|
||||||
|
|
||||||
// Constrain fog buffer to the map resolution
|
// Constrain fog buffer to the map resolution
|
||||||
const fogRect = fogGroup.getClientRect();
|
const fogRect = fogGroup.getClientRect();
|
||||||
const maxMapSize = map ? Math.max(map.width, map.height) : 4096; // Default to 4096
|
const maxMapSize = map ? Math.max(map.width, map.height) : 4096; // Default to 4096
|
||||||
const maxFogSize =
|
const maxFogSize =
|
||||||
Math.max(fogRect.width, fogRect.height) / debouncedStageScale;
|
Math.max(fogRect.width, fogRect.height) / debouncedStageScale;
|
||||||
const maxPixelRatio = maxMapSize / maxFogSize;
|
const maxPixelRatio = maxMapSize / maxFogSize;
|
||||||
|
|
||||||
|
fogGroup.cache({
|
||||||
|
pixelRatio: Math.min(
|
||||||
|
Math.max(debouncedStageScale * pixelRatio, 1),
|
||||||
|
maxPixelRatio
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fogGroup.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
fogGroup.cache({
|
|
||||||
pixelRatio: Math.min(
|
|
||||||
Math.max(debouncedStageScale * pixelRatio, 1),
|
|
||||||
maxPixelRatio
|
|
||||||
),
|
|
||||||
});
|
|
||||||
fogGroup.getLayer().draw();
|
fogGroup.getLayer().draw();
|
||||||
}, [fogShapes, editable, active, debouncedStageScale, mapWidth, map]);
|
}, [fogShapes, editable, active, debouncedStageScale, mapWidth, map]);
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import MapInteractionContext from "../../contexts/MapInteractionContext";
|
|||||||
import MapStageContext from "../../contexts/MapStageContext";
|
import MapStageContext from "../../contexts/MapStageContext";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getBrushPositionForTool,
|
getBrushPosition,
|
||||||
getDefaultShapeData,
|
getDefaultShapeData,
|
||||||
getUpdatedShapeData,
|
getUpdatedShapeData,
|
||||||
getStrokeWidth,
|
getStrokeWidth,
|
||||||
@ -48,20 +48,13 @@ function MapMeasure({ map, selectedToolSettings, active, gridSize }) {
|
|||||||
}
|
}
|
||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
|
||||||
return getBrushPositionForTool(
|
|
||||||
map,
|
|
||||||
getRelativePointerPositionNormalized(mapImage),
|
|
||||||
map.snapToGrid,
|
|
||||||
false,
|
|
||||||
gridSize,
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
map.snapToGrid,
|
||||||
|
gridSize
|
||||||
|
);
|
||||||
const { points } = getDefaultShapeData("line", brushPosition);
|
const { points } = getDefaultShapeData("line", brushPosition);
|
||||||
const length = 0;
|
const length = 0;
|
||||||
setDrawingShapeData({ length, points });
|
setDrawingShapeData({ length, points });
|
||||||
@ -69,7 +62,12 @@ function MapMeasure({ map, selectedToolSettings, active, gridSize }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
map.snapToGrid,
|
||||||
|
gridSize
|
||||||
|
);
|
||||||
if (isBrushDown && drawingShapeData) {
|
if (isBrushDown && drawingShapeData) {
|
||||||
const { points } = getUpdatedShapeData(
|
const { points } = getUpdatedShapeData(
|
||||||
"line",
|
"line",
|
||||||
|
@ -6,7 +6,7 @@ import MapInteractionContext from "../../contexts/MapInteractionContext";
|
|||||||
import MapStageContext from "../../contexts/MapStageContext";
|
import MapStageContext from "../../contexts/MapStageContext";
|
||||||
import AuthContext from "../../contexts/AuthContext";
|
import AuthContext from "../../contexts/AuthContext";
|
||||||
|
|
||||||
import { getBrushPositionForTool } from "../../helpers/drawing";
|
import { getBrushPosition } from "../../helpers/drawing";
|
||||||
import { getRelativePointerPositionNormalized } from "../../helpers/konva";
|
import { getRelativePointerPositionNormalized } from "../../helpers/konva";
|
||||||
|
|
||||||
import Note from "../note/Note";
|
import Note from "../note/Note";
|
||||||
@ -39,20 +39,13 @@ function MapNotes({
|
|||||||
}
|
}
|
||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
|
||||||
return getBrushPositionForTool(
|
|
||||||
map,
|
|
||||||
getRelativePointerPositionNormalized(mapImage),
|
|
||||||
map.snapToGrid,
|
|
||||||
false,
|
|
||||||
gridSize,
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
map.snapToGrid,
|
||||||
|
gridSize
|
||||||
|
);
|
||||||
setNoteData({
|
setNoteData({
|
||||||
x: brushPosition.x,
|
x: brushPosition.x,
|
||||||
y: brushPosition.y,
|
y: brushPosition.y,
|
||||||
@ -70,7 +63,12 @@ function MapNotes({
|
|||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
if (noteData) {
|
if (noteData) {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
map.snapToGrid,
|
||||||
|
gridSize
|
||||||
|
);
|
||||||
setNoteData((prev) => ({
|
setNoteData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
x: brushPosition.x,
|
x: brushPosition.x,
|
||||||
|
@ -3,17 +3,12 @@ import polygonClipping from "polygon-clipping";
|
|||||||
|
|
||||||
import * as Vector2 from "./vector2";
|
import * as Vector2 from "./vector2";
|
||||||
import { toDegrees, omit } from "./shared";
|
import { toDegrees, omit } from "./shared";
|
||||||
|
import { getRelativePointerPositionNormalized } from "./konva";
|
||||||
|
|
||||||
const snappingThreshold = 1 / 5;
|
const snappingThreshold = 1 / 5;
|
||||||
export function getBrushPositionForTool(
|
export function getBrushPosition(map, mapStage, useGridSnappning, gridSize) {
|
||||||
map,
|
const mapImage = mapStage.findOne("#mapImage");
|
||||||
brushPosition,
|
let position = getRelativePointerPositionNormalized(mapImage);
|
||||||
useGridSnappning,
|
|
||||||
useEdgeSnapping,
|
|
||||||
gridSize,
|
|
||||||
shapes
|
|
||||||
) {
|
|
||||||
let position = brushPosition;
|
|
||||||
|
|
||||||
if (useGridSnappning) {
|
if (useGridSnappning) {
|
||||||
// Snap to corners of grid
|
// Snap to corners of grid
|
||||||
@ -48,53 +43,72 @@ export function getBrushPositionForTool(
|
|||||||
position = centerSnap;
|
position = centerSnap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFogBrushPosition(
|
||||||
|
map,
|
||||||
|
mapStage,
|
||||||
|
useGridSnappning,
|
||||||
|
gridSize,
|
||||||
|
useEdgeSnapping,
|
||||||
|
fogShapes,
|
||||||
|
rectPoints
|
||||||
|
) {
|
||||||
|
let position = getBrushPosition(map, mapStage, useGridSnappning, gridSize);
|
||||||
if (useEdgeSnapping) {
|
if (useEdgeSnapping) {
|
||||||
const minGrid = Vector2.min(gridSize);
|
const minGrid = Vector2.min(gridSize);
|
||||||
let closestDistance = Number.MAX_VALUE;
|
let closestDistance = Number.MAX_VALUE;
|
||||||
let closestPosition = position;
|
let closestPosition = position;
|
||||||
// Find the closest point on all fog shapes
|
// Find the closest point on all fog shapes
|
||||||
for (let shape of shapes) {
|
for (let shape of fogShapes) {
|
||||||
if (shape.type === "fog") {
|
// Include shape points and holes
|
||||||
// Include shape points and holes
|
let pointArray = [shape.data.points, ...shape.data.holes];
|
||||||
let pointArray = [shape.data.points, ...shape.data.holes];
|
|
||||||
|
|
||||||
// Check whether the position is in the shape but not any holes
|
for (let points of pointArray) {
|
||||||
let isInShape = Vector2.pointInPolygon(position, shape.data.points);
|
// Find the closest point to each line of the shape
|
||||||
if (shape.data.holes.length > 0) {
|
for (let i = 0; i < points.length; i++) {
|
||||||
for (let hole of shape.data.holes) {
|
const a = points[i];
|
||||||
if (Vector2.pointInPolygon(position, hole)) {
|
// Wrap around points to the start to account for closed shape
|
||||||
isInShape = false;
|
const b = points[(i + 1) % points.length];
|
||||||
|
|
||||||
|
let {
|
||||||
|
distance: distanceToLine,
|
||||||
|
point: pointOnLine,
|
||||||
|
} = Vector2.distanceToLine(position, a, b);
|
||||||
|
|
||||||
|
if (rectPoints) {
|
||||||
|
const { distance: d1, point: p1 } = Vector2.distanceToLine(
|
||||||
|
{ x: position.x, y: rectPoints[1].y },
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
);
|
||||||
|
const { distance: d3, point: p3 } = Vector2.distanceToLine(
|
||||||
|
{ x: rectPoints[3].x, y: position.y },
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
);
|
||||||
|
|
||||||
|
if (d1 < minGrid * snappingThreshold) {
|
||||||
|
distanceToLine = d1;
|
||||||
|
pointOnLine.x = p1.x;
|
||||||
|
}
|
||||||
|
if (d3 < minGrid * snappingThreshold) {
|
||||||
|
distanceToLine = d3;
|
||||||
|
pointOnLine.y = p3.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (let points of pointArray) {
|
const isCloseToShape = distanceToLine < minGrid * snappingThreshold;
|
||||||
// Find the closest point to each line of the shape
|
if (isCloseToShape && distanceToLine < closestDistance) {
|
||||||
for (let i = 0; i < points.length; i++) {
|
closestPosition = pointOnLine;
|
||||||
const a = points[i];
|
closestDistance = distanceToLine;
|
||||||
// Wrap around points to the start to account for closed shape
|
|
||||||
const b = points[(i + 1) % points.length];
|
|
||||||
|
|
||||||
const {
|
|
||||||
distance: distanceToLine,
|
|
||||||
point: pointOnLine,
|
|
||||||
} = Vector2.distanceToLine(position, a, b);
|
|
||||||
const isCloseToShape = distanceToLine < minGrid * snappingThreshold;
|
|
||||||
if (
|
|
||||||
(isInShape || isCloseToShape) &&
|
|
||||||
distanceToLine < closestDistance
|
|
||||||
) {
|
|
||||||
closestPosition = pointOnLine;
|
|
||||||
closestDistance = distanceToLine;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
position = closestPosition;
|
position = closestPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user