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