diff --git a/src/components/map/Map.js b/src/components/map/Map.js index 3cdf25b..9fc32f0 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -60,7 +60,7 @@ function Map({ color: "red", type: "rectangle", useBlending: true, - useGridSnapping: true, + useGridSnapping: false, }, }); function handleToolSettingChange(tool, change) { diff --git a/src/components/map/MapDrawing.js b/src/components/map/MapDrawing.js index 16a58f1..2c90837 100644 --- a/src/components/map/MapDrawing.js +++ b/src/components/map/MapDrawing.js @@ -3,7 +3,13 @@ import simplify from "simplify-js"; import shortid from "shortid"; import colors from "../../helpers/colors"; -import { snapPositionToGrid } from "../../helpers/shared"; +import { + getBrushPositionForTool, + getDefaultShapeData, + getUpdatedShapeData, + getStrokeSize, + shapeHasFill, +} from "../../helpers/drawing"; function MapDrawing({ width, @@ -18,12 +24,9 @@ function MapDrawing({ const canvasRef = useRef(); const containerRef = useRef(); - const toolColor = toolSettings && toolSettings.color; - const useToolBlending = toolSettings && toolSettings.useBlending; - const useGridSnapping = toolSettings && toolSettings.useGridSnapping; - - const [brushPoints, setBrushPoints] = useState([]); + // const [brushPoints, setBrushPoints] = useState([]); const [isDrawing, setIsDrawing] = useState(false); + const [drawingShape, setDrawingShape] = useState(null); const [pointerPosition, setPointerPosition] = useState({ x: -1, y: -1 }); // Reset pointer position when tool changes @@ -44,18 +47,40 @@ function MapDrawing({ function handleStart(event) { if (event.touches && event.touches.length !== 1) { setIsDrawing(false); - setBrushPoints([]); + setDrawingShape(null); return; } const pointer = event.touches ? event.touches[0] : event; const position = getRelativePointerPosition(pointer); setPointerPosition(position); setIsDrawing(true); + const brushPosition = getBrushPositionForTool( + position, + toolSettings, + gridSize, + shapes + ); + const commonShapeData = { + id: shortid.generate(), + color: toolSettings && toolSettings.color, + blend: toolSettings && toolSettings.useBlending, + }; if (selectedTool === "brush") { - const brushPosition = useGridSnapping - ? snapPositionToGrid(position, gridSize) - : position; - setBrushPoints([brushPosition]); + setDrawingShape({ + type: "path", + pathType: toolSettings.type, + data: { points: [brushPosition] }, + strokeWidth: toolSettings.type === "stroke" ? 1 : 0, + ...commonShapeData, + }); + } else if (selectedTool === "shape") { + setDrawingShape({ + type: "shape", + shapeType: toolSettings.type, + data: getDefaultShapeData(toolSettings.type, brushPosition), + strokeWidth: 0, + ...commonShapeData, + }); } } @@ -68,17 +93,39 @@ function MapDrawing({ if (selectedTool === "erase") { setPointerPosition(position); } - if (isDrawing && selectedTool === "brush") { + if (isDrawing) { setPointerPosition(position); - const brushPosition = useGridSnapping - ? snapPositionToGrid(position, gridSize) - : position; - setBrushPoints((prevPoints) => { - if (prevPoints[prevPoints.length - 1] === brushPosition) { - return prevPoints; - } - return [...prevPoints, brushPosition]; - }); + const brushPosition = getBrushPositionForTool( + position, + toolSettings, + gridSize, + shapes + ); + if (selectedTool === "brush") { + setDrawingShape((prevShape) => { + const prevPoints = prevShape.data.points; + if (prevPoints[prevPoints.length - 1] === brushPosition) { + return prevPoints; + } + const simplified = simplify( + [...prevPoints, brushPosition], + getStrokeSize(drawingShape.strokeWidth, gridSize, 1, 1) * 0.1 + ); + return { + ...prevShape, + data: { points: simplified }, + }; + }); + } else if (selectedTool === "shape") { + setDrawingShape((prevShape) => ({ + ...prevShape, + data: getUpdatedShapeData( + prevShape.shapeType, + prevShape.data, + brushPosition + ), + })); + } } } @@ -88,24 +135,21 @@ function MapDrawing({ } setIsDrawing(false); if (selectedTool === "brush") { - if (brushPoints.length > 1) { - const simplifiedPoints = simplify(brushPoints, 0.001); - const type = "path"; + if (drawingShape.data.points.length > 1) { + // const simplifiedPoints = simplify( + // drawingShape.data.points, + // getStrokeSize(drawingShape.strokeWidth, gridSize, 1, 1) * 0.1 + // ); - if (type !== null) { - const data = { points: simplifiedPoints }; - onShapeAdd({ - type, - data, - id: shortid.generate(), - color: toolColor, - blend: useToolBlending, - }); - } - - setBrushPoints([]); + // const data = { points: simplifiedPoints }; + // onShapeAdd({ ...drawingShape, data }); + onShapeAdd(drawingShape); } + } else if (selectedTool === "shape") { + onShapeAdd(drawingShape); } + + setDrawingShape(null); if (selectedTool === "erase" && hoveredShapeRef.current) { onShapeRemove(hoveredShapeRef.current.id); } @@ -134,13 +178,15 @@ function MapDrawing({ const hoveredShapeRef = useRef(null); useEffect(() => { - function pointsToPath(points) { + function pointsToPath(points, close) { const path = new Path2D(); path.moveTo(points[0].x * width, points[0].y * height); for (let point of points.slice(1)) { path.lineTo(point.x * width, point.y * height); } - path.closePath(); + if (close) { + path.closePath(); + } return path; } @@ -160,22 +206,30 @@ function MapDrawing({ function shapeToPath(shape) { const data = shape.data; if (shape.type === "path") { - return pointsToPath(data.points); - } else if (shape.type === "circle") { - return circleToPath(data.x, data.y, data.radius); - } else if (shape.type === "rectangle") { - return rectangleToPath(data.x, data.y, data.width, data.height); - } else if (shape.type === "triangle") { - return pointsToPath(data.points); + return pointsToPath(data.points, shape.pathType === "fill"); + } else if (shape.type === "shape") { + if (shape.shapeType === "circle") { + return circleToPath(data.x, data.y, data.radius); + } else if (shape.shapeType === "rectangle") { + return rectangleToPath(data.x, data.y, data.width, data.height); + } else if (shape.shapeType === "triangle") { + return pointsToPath(data.points, true); + } } } - function drawPath(path, color, blend, context) { + function drawPath(path, color, fill, strokeWidth, blend, context) { context.globalAlpha = blend ? 0.5 : 1.0; context.fillStyle = color; context.strokeStyle = color; - context.stroke(path); - context.fill(path); + if (strokeWidth > 0) { + context.lineCap = "round"; + context.lineWidth = getStrokeSize(strokeWidth, gridSize, width, height); + context.stroke(path); + } + if (fill) { + context.fill(path); + } } const canvas = canvasRef.current; @@ -198,15 +252,30 @@ function MapDrawing({ hoveredShape = shape; } } - drawPath(path, colors[shape.color], shape.blend, context); + + drawPath( + path, + colors[shape.color], + shapeHasFill(shape), + shape.strokeWidth, + shape.blend, + context + ); } - if (selectedTool === "brush" && brushPoints.length > 0) { - const path = pointsToPath(brushPoints); - drawPath(path, colors[toolColor], useToolBlending, context); + if (drawingShape) { + const path = shapeToPath(drawingShape); + drawPath( + path, + colors[drawingShape.color], + shapeHasFill(drawingShape), + drawingShape.strokeWidth, + drawingShape.blend, + context + ); } if (hoveredShape) { const path = shapeToPath(hoveredShape); - drawPath(path, "#BB99FF", true, context); + drawPath(path, "#BB99FF", true, 1, true, context); } hoveredShapeRef.current = hoveredShape; } @@ -217,9 +286,8 @@ function MapDrawing({ pointerPosition, isDrawing, selectedTool, - brushPoints, - toolColor, - useToolBlending, + drawingShape, + gridSize, ]); return ( diff --git a/src/components/map/controls/ShapeToolSettings.js b/src/components/map/controls/ShapeToolSettings.js index 386331c..bf50aa8 100644 --- a/src/components/map/controls/ShapeToolSettings.js +++ b/src/components/map/controls/ShapeToolSettings.js @@ -30,16 +30,16 @@ function ShapeToolSettings({ settings, onSettingChange }) { onSettingChange({ type: "circle" })} - isSelected={settings.type === "cricle"} + isSelected={settings.type === "circle"} > - + onSettingChange({ type: "triangle" })} isSelected={settings.type === "triangle"} > - + a.y ? a.x : a.y; }