Files
grungnet/src/components/map/MapMeasure.js

174 lines
5.0 KiB
JavaScript
Raw Normal View History

import React, { useState, useEffect } from "react";
2020-06-26 12:23:06 +10:00
import { Group, Line, Text, Label, Tag } from "react-konva";
import { useMapInteraction } from "../../contexts/MapInteractionContext";
import { useMapStage } from "../../contexts/MapStageContext";
import { useGrid } from "../../contexts/GridContext";
2020-06-26 12:23:06 +10:00
import {
getDefaultShapeData,
getUpdatedShapeData,
} from "../../helpers/drawing";
import Vector2 from "../../helpers/Vector2";
import { getRelativePointerPosition } from "../../helpers/konva";
import useGridSnapping from "../../hooks/useGridSnapping";
2020-06-26 12:23:06 +10:00
function MapMeasure({ map, selectedToolSettings, active }) {
const {
stageScale,
mapWidth,
mapHeight,
interactionEmitter,
} = useMapInteraction();
const { gridCellNormalizedSize, gridStrokeWidth } = useGrid();
const mapStageRef = useMapStage();
2020-06-26 12:23:06 +10:00
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 && selectedToolSettings.scale);
const snapPositionToGrid = useGridSnapping();
2020-06-26 12:23:06 +10:00
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();
2020-06-26 12:23:06 +10:00
const { points } = getDefaultShapeData("line", brushPosition);
const length = 0;
setDrawingShapeData({ length, points });
setIsBrushDown(true);
}
function handleBrushMove() {
const brushPosition = getBrushPosition();
2020-06-26 12:23:06 +10:00
if (isBrushDown && drawingShapeData) {
const { points } = getUpdatedShapeData(
"line",
drawingShapeData,
brushPosition,
gridCellNormalizedSize
2020-06-26 12:23:06 +10:00
);
// Round the grid positions to the nearest 0.1 to aviod floating point issues
const precision = { x: 0.1, y: 0.1 };
2020-06-26 12:23:06 +10:00
const length = Vector2.distance(
Vector2.roundTo(
Vector2.divide(points[0], gridCellNormalizedSize),
precision
),
Vector2.roundTo(
Vector2.divide(points[1], gridCellNormalizedSize),
precision
),
2020-06-26 12:23:06 +10:00
selectedToolSettings.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);
};
});
2020-06-26 12:23:06 +10:00
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 (
<Group>
<Line
points={linePoints}
strokeWidth={1.5 * gridStrokeWidth}
2020-06-26 12:23:06 +10:00
stroke="hsla(230, 25%, 18%, 0.8)"
lineCap="round"
/>
<Line
points={linePoints}
strokeWidth={0.25 * gridStrokeWidth}
2020-06-26 12:23:06 +10:00
stroke="white"
lineCap="round"
/>
<Label
x={lineCenter.x}
y={lineCenter.y}
offsetX={26}
offsetY={26}
scaleX={1 / stageScale}
scaleY={1 / stageScale}
>
<Tag fill="hsla(230, 25%, 18%, 0.8)" cornerRadius={4} />
<Text
text={`${(shapeData.length * measureScale.multiplier).toFixed(
measureScale.digits
)}${measureScale.unit}`}
2020-06-26 12:23:06 +10:00
fill="white"
fontSize={24}
padding={4}
/>
</Label>
</Group>
);
}
return <Group>{drawingShapeData && renderShape(drawingShapeData)}</Group>;
}
export default MapMeasure;