diff --git a/src/components/map/MapDrawing.js b/src/components/map/MapDrawing.js index 66012c3..eadd4b5 100644 --- a/src/components/map/MapDrawing.js +++ b/src/components/map/MapDrawing.js @@ -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) => { diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index 16a82a9..ab205dc 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -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]); diff --git a/src/components/map/MapMeasure.js b/src/components/map/MapMeasure.js index 54ef535..ae6b82b 100644 --- a/src/components/map/MapMeasure.js +++ b/src/components/map/MapMeasure.js @@ -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", diff --git a/src/components/map/MapNotes.js b/src/components/map/MapNotes.js index 1f31893..a95713c 100644 --- a/src/components/map/MapNotes.js +++ b/src/components/map/MapNotes.js @@ -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, diff --git a/src/helpers/drawing.js b/src/helpers/drawing.js index 4731f4e..1c7ff53 100644 --- a/src/helpers/drawing.js +++ b/src/helpers/drawing.js @@ -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; }