import React, { useState, useEffect } from "react"; import { Group, Line, Text, Label, Tag } from "react-konva"; import { useMapInteraction } from "../../contexts/MapInteractionContext"; import { useMapStage } from "../../contexts/MapStageContext"; import { useGrid } from "../../contexts/GridContext"; import { getDefaultShapeData, getUpdatedShapeData, } from "../../helpers/drawing"; import Vector2 from "../../helpers/Vector2"; import { getRelativePointerPosition } from "../../helpers/konva"; import useGridSnapping from "../../hooks/useGridSnapping"; function MapMeasure({ map, active }) { const { stageScale, mapWidth, mapHeight, interactionEmitter, } = useMapInteraction(); const { grid, gridCellNormalizedSize, gridStrokeWidth } = useGrid(); const mapStageRef = useMapStage(); const [drawingShapeData, setDrawingShapeData] = useState(null); const [isBrushDown, setIsBrushDown] = useState(false); function parseToolScale(scale) { if (typeof scale === "string") { const match = scale.match(/(\d*)(\.\d*)?([a-zA-Z]*)/); const integer = parseFloat(match[1]); const fractional = parseFloat(match[2]); const unit = match[3] || ""; if (!isNaN(integer) && !isNaN(fractional)) { return { multiplier: integer + fractional, unit: unit, digits: match[2].length - 1, }; } else if (!isNaN(integer) && isNaN(fractional)) { return { multiplier: integer, unit: unit, digits: 0 }; } } return { multiplier: 1, unit: "", digits: 0 }; } const measureScale = parseToolScale(active && grid.measurement.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(); const { points } = getDefaultShapeData("line", brushPosition); const length = 0; setDrawingShapeData({ length, points }); setIsBrushDown(true); } function handleBrushMove() { const brushPosition = getBrushPosition(); if (isBrushDown && drawingShapeData) { const { points } = getUpdatedShapeData( "line", drawingShapeData, brushPosition, gridCellNormalizedSize ); // Round the grid positions to the nearest 0.1 to aviod floating point issues const precision = { x: 0.1, y: 0.1 }; const length = Vector2.distance( Vector2.roundTo( Vector2.divide(points[0], gridCellNormalizedSize), precision ), Vector2.roundTo( Vector2.divide(points[1], gridCellNormalizedSize), precision ), grid.measurement.type ); setDrawingShapeData({ length, points, }); } } function handleBrushUp() { setDrawingShapeData(null); setIsBrushDown(false); } interactionEmitter.on("dragStart", handleBrushDown); interactionEmitter.on("drag", handleBrushMove); interactionEmitter.on("dragEnd", handleBrushUp); return () => { interactionEmitter.off("dragStart", handleBrushDown); interactionEmitter.off("drag", handleBrushMove); interactionEmitter.off("dragEnd", handleBrushUp); }; }); function renderShape(shapeData) { const linePoints = shapeData.points.reduce( (acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight], [] ); const lineCenter = Vector2.multiply( Vector2.divide(Vector2.add(shapeData.points[0], shapeData.points[1]), 2), { x: mapWidth, y: mapHeight } ); return ( ); } return {drawingShapeData && renderShape(drawingShapeData)}; } export default MapMeasure;