Moved all grid snapping to useGridSnapping hook

This commit is contained in:
Mitchell McCaffrey 2021-02-09 14:13:08 +11:00
parent cdb91eed60
commit 846afe5495
9 changed files with 103 additions and 227 deletions

View File

@ -8,13 +8,14 @@ import { useGrid } from "../../contexts/GridContext";
import Vector2 from "../../helpers/Vector2";
import {
getBrushPosition,
getDefaultShapeData,
getUpdatedShapeData,
simplifyPoints,
} from "../../helpers/drawing";
import colors from "../../helpers/colors";
import { getRelativePointerPosition } from "../../helpers/konva";
import useGridSnapping from "../../hooks/useGridSnapping";
function MapDrawing({
map,
@ -45,19 +46,28 @@ function MapDrawing({
toolSettings.type === "circle" ||
toolSettings.type === "triangle";
const snapPositionToGrid = useGridSnapping();
useEffect(() => {
if (!active) {
return;
}
const mapStage = mapStageRef.current;
function getBrushPosition() {
const mapImage = mapStage.findOne("#mapImage");
let position = getRelativePointerPosition(mapImage);
if (map.snapToGrid && isShape) {
position = snapPositionToGrid(position);
}
return Vector2.divide(position, {
x: mapImage.width(),
y: mapImage.height(),
});
}
function handleBrushDown() {
const brushPosition = getBrushPosition(
map,
mapStage,
map.snapToGrid && isShape,
gridCellNormalizedSize
);
const brushPosition = getBrushPosition();
const commonShapeData = {
color: toolSettings.color,
blend: toolSettings.useBlending,
@ -84,12 +94,7 @@ function MapDrawing({
}
function handleBrushMove() {
const brushPosition = getBrushPosition(
map,
mapStage,
map.snapToGrid && isShape,
gridCellNormalizedSize
);
const brushPosition = getBrushPosition();
if (isBrushDown && drawingShape) {
if (isBrush) {
setDrawingShape((prevShape) => {

View File

@ -11,15 +11,16 @@ import { useGrid } from "../../contexts/GridContext";
import { useKeyboard } from "../../contexts/KeyboardContext";
import Vector2 from "../../helpers/Vector2";
import {
getFogBrushPosition,
simplifyPoints,
mergeShapes,
} from "../../helpers/drawing";
import { simplifyPoints, mergeShapes } from "../../helpers/drawing";
import colors from "../../helpers/colors";
import { HoleyLine, Tick } from "../../helpers/konva";
import {
HoleyLine,
Tick,
getRelativePointerPosition,
} from "../../helpers/konva";
import useDebounce from "../../hooks/useDebounce";
import useGridSnapping from "../../hooks/useGridSnapping";
function MapFog({
map,
@ -52,6 +53,8 @@ function MapFog({
const [patternImage] = useImage(diagonalPattern);
const snapPositionToGrid = useGridSnapping();
useEffect(() => {
if (!active || !editable) {
return;
@ -59,19 +62,23 @@ function MapFog({
const mapStage = mapStageRef.current;
const useGridSnapping =
map.snapToGrid &&
(toolSettings.type === "polygon" || toolSettings.type === "rectangle");
function getBrushPosition() {
const mapImage = mapStage.findOne("#mapImage");
let position = getRelativePointerPosition(mapImage);
if (
map.snapToGrid &&
(toolSettings.type === "polygon" || toolSettings.type === "rectangle")
) {
position = snapPositionToGrid(position);
}
return Vector2.divide(position, {
x: mapImage.width(),
y: mapImage.height(),
});
}
function handleBrushDown() {
const brushPosition = getFogBrushPosition(
map,
mapStage,
useGridSnapping,
gridCellNormalizedSize,
toolSettings.useEdgeSnapping,
shapes
);
const brushPosition = getBrushPosition();
if (toolSettings.type === "brush") {
setDrawingShape({
type: "fog",
@ -108,14 +115,7 @@ function MapFog({
function handleBrushMove() {
if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
const brushPosition = getFogBrushPosition(
map,
mapStage,
useGridSnapping,
gridCellNormalizedSize,
toolSettings.useEdgeSnapping,
shapes
);
const brushPosition = getBrushPosition();
setDrawingShape((prevShape) => {
const prevPoints = prevShape.data.points;
if (
@ -138,15 +138,7 @@ function MapFog({
}
if (toolSettings.type === "rectangle" && isBrushDown && drawingShape) {
const prevPoints = drawingShape.data.points;
const brushPosition = getFogBrushPosition(
map,
mapStage,
useGridSnapping,
gridCellNormalizedSize,
toolSettings.useEdgeSnapping,
shapes,
prevPoints
);
const brushPosition = getBrushPosition();
setDrawingShape((prevShape) => {
return {
...prevShape,
@ -205,14 +197,7 @@ function MapFog({
function handlePolygonClick() {
if (toolSettings.type === "polygon") {
const brushPosition = getFogBrushPosition(
map,
mapStage,
useGridSnapping,
gridCellNormalizedSize,
toolSettings.useEdgeSnapping,
shapes
);
const brushPosition = getBrushPosition();
setDrawingShape((prevDrawingShape) => {
if (prevDrawingShape) {
return {
@ -241,14 +226,7 @@ function MapFog({
function handlePolygonMove() {
if (toolSettings.type === "polygon" && drawingShape) {
const brushPosition = getFogBrushPosition(
map,
mapStage,
useGridSnapping,
gridCellNormalizedSize,
toolSettings.useEdgeSnapping,
shapes
);
const brushPosition = getBrushPosition();
setDrawingShape((prevShape) => {
if (!prevShape) {
return;

View File

@ -6,11 +6,13 @@ import { useMapStage } from "../../contexts/MapStageContext";
import { useGrid } from "../../contexts/GridContext";
import {
getBrushPosition,
getDefaultShapeData,
getUpdatedShapeData,
} from "../../helpers/drawing";
import Vector2 from "../../helpers/Vector2";
import { getRelativePointerPosition } from "../../helpers/konva";
import useGridSnapping from "../../hooks/useGridSnapping";
function MapMeasure({ map, selectedToolSettings, active }) {
const {
@ -45,19 +47,28 @@ function MapMeasure({ map, selectedToolSettings, active }) {
const measureScale = parseToolScale(active && selectedToolSettings.scale);
const snapPositionToGrid = useGridSnapping();
useEffect(() => {
if (!active) {
return;
}
const mapStage = mapStageRef.current;
function getBrushPosition() {
const mapImage = mapStage.findOne("#mapImage");
let position = getRelativePointerPosition(mapImage);
if (map.snapToGrid) {
position = snapPositionToGrid(position);
}
return Vector2.divide(position, {
x: mapImage.width(),
y: mapImage.height(),
});
}
function handleBrushDown() {
const brushPosition = getBrushPosition(
map,
mapStage,
map.snapToGrid,
gridCellNormalizedSize
);
const brushPosition = getBrushPosition();
const { points } = getDefaultShapeData("line", brushPosition);
const length = 0;
setDrawingShapeData({ length, points });
@ -65,12 +76,7 @@ function MapMeasure({ map, selectedToolSettings, active }) {
}
function handleBrushMove() {
const brushPosition = getBrushPosition(
map,
mapStage,
map.snapToGrid,
gridCellNormalizedSize
);
const brushPosition = getBrushPosition();
if (isBrushDown && drawingShapeData) {
const { points } = getUpdatedShapeData(
"line",

View File

@ -5,9 +5,11 @@ import { Group } from "react-konva";
import { useMapInteraction } from "../../contexts/MapInteractionContext";
import { useMapStage } from "../../contexts/MapStageContext";
import { useAuth } from "../../contexts/AuthContext";
import { useGrid } from "../../contexts/GridContext";
import { getBrushPosition } from "../../helpers/drawing";
import Vector2 from "../../helpers/Vector2";
import { getRelativePointerPosition } from "../../helpers/konva";
import useGridSnapping from "../../hooks/useGridSnapping";
import Note from "../note/Note";
@ -27,26 +29,34 @@ function MapNotes({
}) {
const { interactionEmitter } = useMapInteraction();
const { userId } = useAuth();
const { gridCellNormalizedSize } = useGrid();
const mapStageRef = useMapStage();
const [isBrushDown, setIsBrushDown] = useState(false);
const [noteData, setNoteData] = useState(null);
const creatingNoteRef = useRef();
const snapPositionToGrid = useGridSnapping();
useEffect(() => {
if (!active) {
return;
}
const mapStage = mapStageRef.current;
function getBrushPosition() {
const mapImage = mapStage.findOne("#mapImage");
let position = getRelativePointerPosition(mapImage);
if (map.snapToGrid) {
position = snapPositionToGrid(position);
}
return Vector2.divide(position, {
x: mapImage.width(),
y: mapImage.height(),
});
}
function handleBrushDown() {
const brushPosition = getBrushPosition(
map,
mapStage,
map.snapToGrid,
gridCellNormalizedSize
);
const brushPosition = getBrushPosition();
setNoteData({
x: brushPosition.x,
y: brushPosition.y,
@ -65,12 +75,7 @@ function MapNotes({
function handleBrushMove() {
if (noteData) {
const brushPosition = getBrushPosition(
map,
mapStage,
map.snapToGrid,
gridCellNormalizedSize
);
const brushPosition = getBrushPosition();
setNoteData((prev) => ({
...prev,
x: brushPosition.x,

View File

@ -49,7 +49,7 @@ function MapToken({
}
}, [tokenSourceImage]);
const snapNodeToGrid = useGridSnapping();
const snapPositionToGrid = useGridSnapping();
function handleDragStart(event) {
const tokenGroup = event.target;
@ -87,7 +87,7 @@ function MapToken({
const tokenGroup = event.target;
// Snap to corners of grid
if (map.snapToGrid) {
snapNodeToGrid(tokenGroup);
tokenGroup.position(snapPositionToGrid(tokenGroup.position()));
}
}

View File

@ -33,7 +33,7 @@ function Note({
const noteHeight = noteWidth;
const notePadding = noteWidth / 10;
const snapNodeToGrid = useGridSnapping();
const snapPositionToGrid = useGridSnapping();
function handleDragStart(event) {
onNoteDragStart && onNoteDragStart(event, note.id);
@ -43,7 +43,7 @@ function Note({
const noteGroup = event.target;
// Snap to corners of grid
if (map.snapToGrid) {
snapNodeToGrid(noteGroup);
noteGroup.position(snapPositionToGrid(noteGroup.position()));
}
}

View File

@ -3,127 +3,8 @@ import polygonClipping from "polygon-clipping";
import Vector2 from "./Vector2";
import { toDegrees } from "./shared";
import { getRelativePointerPositionNormalized } from "./konva";
import { logError } from "./logging";
const snappingThreshold = 1 / 5;
export function getBrushPosition(
map,
mapStage,
useGridSnappning,
gridCellNormalizedSize
) {
const mapImage = mapStage.findOne("#mapImage");
let position = getRelativePointerPositionNormalized(mapImage);
if (useGridSnappning) {
// Snap to corners of grid
// Subtract offset to transform into offset space then add it back transform back
const offset = map.grid.inset.topLeft;
const gridSnap = Vector2.add(
Vector2.roundTo(
Vector2.subtract(position, offset),
gridCellNormalizedSize
),
offset
);
const gridDistance = Vector2.length(Vector2.subtract(gridSnap, position));
// Snap to center of grid
// Subtract offset and half size to transform it into offset half space then transform it back
const halfSize = Vector2.multiply(gridCellNormalizedSize, 0.5);
const centerSnap = Vector2.add(
Vector2.add(
Vector2.roundTo(
Vector2.subtract(Vector2.subtract(position, offset), halfSize),
gridCellNormalizedSize
),
halfSize
),
offset
);
const centerDistance = Vector2.length(
Vector2.subtract(centerSnap, position)
);
const minGrid = Vector2.min(gridCellNormalizedSize);
if (gridDistance < minGrid * snappingThreshold) {
position = gridSnap;
} else if (centerDistance < minGrid * snappingThreshold) {
position = centerSnap;
}
}
return position;
}
export function getFogBrushPosition(
map,
mapStage,
useGridSnappning,
gridCellNormalizedSize,
useEdgeSnapping,
fogShapes,
rectPoints
) {
let position = getBrushPosition(
map,
mapStage,
useGridSnappning,
gridCellNormalizedSize
);
if (useEdgeSnapping) {
const minGrid = Vector2.min(gridCellNormalizedSize);
let closestDistance = Number.MAX_VALUE;
let closestPosition = position;
// Find the closest point on all fog shapes
for (let shape of fogShapes) {
// Include shape points and holes
let pointArray = [shape.data.points, ...shape.data.holes];
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;
}
}
const isCloseToShape = distanceToLine < minGrid * snappingThreshold;
if (isCloseToShape && distanceToLine < closestDistance) {
closestPosition = pointOnLine;
closestDistance = distanceToLine;
}
}
}
}
position = closestPosition;
}
return position;
}
export function getDefaultShapeData(type, brushPosition) {
if (type === "line") {
return {

View File

@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from "react";
import { Line, Group, Path, Circle } from "react-konva";
import Konva from "konva";
import Color from "color";
import Vector2 from "./Vector2";
@ -291,11 +292,15 @@ Trail.defaultProps = {
segments: 20,
};
/**
* @param {Konva.Node} node
* @returns {Vector2}
*/
export function getRelativePointerPosition(node) {
let transform = node.getAbsoluteTransform().copy();
transform.invert();
let posision = node.getStage().getPointerPosition();
return transform.point(posision);
let position = node.getStage().getPointerPosition();
return transform.point(position);
}
export function getRelativePointerPositionNormalized(node) {

View File

@ -1,7 +1,3 @@
// Load Konva for auto complete
// eslint-disable-next-line no-unused-vars
import Konva from "konva";
import Vector2 from "../helpers/Vector2";
import {
getCellLocation,
@ -26,10 +22,9 @@ function useGridSnapping(snappingSensitivity) {
const { grid, gridOffset, gridCellPixelSize } = useGrid();
/**
* @param {Konva.Node} node The node to snap
* @param {Vector2} node The node to snap
*/
function snapNodeToGrid(node) {
const position = node.position();
function snapPositionToGrid(position) {
// Account for grid offset
let offsetPosition = Vector2.subtract(position, gridOffset);
// Move hex tiles to top left
@ -75,13 +70,14 @@ function useGridSnapping(snappingSensitivity) {
Vector2.multiply(gridCellPixelSize, 0.5)
);
}
node.position(offsetSnapPoint);
return;
return offsetSnapPoint;
}
}
return position;
}
return snapNodeToGrid;
return snapPositionToGrid;
}
export default useGridSnapping;