Changed grid scale and offset to inset and added grid editor

This commit is contained in:
Mitchell McCaffrey 2020-10-02 17:53:23 +10:00
parent 539f216cfe
commit 3e5478f7a8
8 changed files with 216 additions and 15 deletions

View File

@ -330,9 +330,7 @@ function Map({
/>
);
const mapGrid = map && map.showGrid && (
<MapGrid map={map} gridSize={gridSizeNormalized} />
);
const mapGrid = map && map.showGrid && <MapGrid map={map} />;
const mapMeasure = (
<MapMeasure

View File

@ -7,7 +7,12 @@ import useMapImage from "../../helpers/useMapImage";
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
import useStageInteraction from "../../helpers/useStageInteraction";
function MapEditor({ map }) {
import { MapInteractionProvider } from "../../contexts/MapInteractionContext";
import MapGrid from "./MapGrid";
import MapGridEditor from "./MapGridEditor";
function MapEditor({ map, onSettingsChange }) {
const [mapImageSource] = useMapImage(map);
const [stageWidth, setStageWidth] = useState(1);
@ -29,6 +34,7 @@ function MapEditor({ map }) {
const stageTranslateRef = useRef({ x: 0, y: 0 });
const mapLayerRef = useRef();
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
function handleResize(width, height) {
setStageWidth(width);
@ -66,12 +72,30 @@ function MapEditor({ map }) {
mapLayerRef.current,
stageScale,
setStageScale,
stageTranslateRef
stageTranslateRef,
"pan",
preventMapInteraction
);
const containerRef = useRef();
usePreventOverscroll(containerRef);
function handleGridChange(inset) {
onSettingsChange("grid", {
...map.grid,
inset,
});
}
const mapInteraction = {
stageScale,
stageWidth,
stageHeight,
setPreventMapInteraction,
mapWidth,
mapHeight,
};
return (
<Box
sx={{
@ -96,6 +120,10 @@ function MapEditor({ map }) {
>
<Layer ref={mapLayerRef}>
<Image image={mapImageSource} width={mapWidth} height={mapHeight} />
<MapInteractionProvider value={mapInteraction}>
<MapGrid map={map} strokeWidth={0.5} />
<MapGridEditor map={map} onGridChange={handleGridChange} />
</MapInteractionProvider>
</Layer>
</Stage>
</ReactResizeDetector>

View File

@ -10,7 +10,7 @@ import { mapSources as defaultMapSources } from "../../maps";
import { getStrokeWidth } from "../../helpers/drawing";
import { getImageLightness } from "../../helpers/image";
function MapGrid({ map, gridSize }) {
function MapGrid({ map, strokeWidth }) {
let mapSourceMap = map;
// Use lowest resolution for grid lightness
if (map && map.type === "file" && map.resolutions) {
@ -24,6 +24,10 @@ function MapGrid({ map, gridSize }) {
const gridX = map && map.grid.size.x;
const gridY = map && map.grid.size.y;
const gridSizeNormalized = {
x: gridX ? 1 / gridX : 0,
y: gridY ? 1 / gridY : 0,
};
const { mapWidth, mapHeight } = useContext(MapInteractionContext);
@ -35,6 +39,7 @@ function MapGrid({ map, gridSize }) {
// When the map changes find the average lightness of its pixels
useEffect(() => {
if (mapLoadingStatus === "loaded") {
console.log("getting lightness");
setIsImageLight(getImageLightness(mapImage));
}
}, [mapImage, mapLoadingStatus]);
@ -46,7 +51,12 @@ function MapGrid({ map, gridSize }) {
key={`grid_x_${x}`}
points={[x * lineSpacingX, 0, x * lineSpacingX, mapHeight]}
stroke={isImageLight ? "black" : "white"}
strokeWidth={getStrokeWidth(0.1, gridSize, mapWidth, mapHeight)}
strokeWidth={getStrokeWidth(
strokeWidth,
gridSizeNormalized,
mapWidth,
mapHeight
)}
opacity={0.5}
/>
);
@ -57,7 +67,12 @@ function MapGrid({ map, gridSize }) {
key={`grid_y_${y}`}
points={[0, y * lineSpacingY, mapWidth, y * lineSpacingY]}
stroke={isImageLight ? "black" : "white"}
strokeWidth={getStrokeWidth(0.1, gridSize, mapWidth, mapHeight)}
strokeWidth={getStrokeWidth(
strokeWidth,
gridSizeNormalized,
mapWidth,
mapHeight
)}
opacity={0.5}
/>
);
@ -66,4 +81,8 @@ function MapGrid({ map, gridSize }) {
return <Group>{lines}</Group>;
}
MapGrid.defaultProps = {
strokeWidth: 0.1,
};
export default MapGrid;

View File

@ -0,0 +1,156 @@
import React, { useContext, useRef } from "react";
import { Group, Circle, Rect } from "react-konva";
import MapInteractionContext from "../../contexts/MapInteractionContext";
import * as Vector2 from "../../helpers/vector2";
function MapGridEditor({ map, onGridChange }) {
const {
mapWidth,
mapHeight,
stageScale,
setPreventMapInteraction,
} = useContext(MapInteractionContext);
const mapSize = { x: mapWidth, y: mapHeight };
const topLeftHandleRef = useRef();
const topRightHandleRef = useRef();
const bottomRightHandleRef = useRef();
const bottomLeftHandleRef = useRef();
function handleScaleCircleDragStart() {}
function handleScaleCircleDragMove(event) {
onGridChange(getHandleInset(event.target));
}
function handleScaleCircleDragEnd(event) {
onGridChange(getHandleInset(event.target));
setPreventMapInteraction(false);
}
function handleInteractivePointerDown() {
setPreventMapInteraction(true);
}
function handleInteractivePointerUp() {
setPreventMapInteraction(false);
}
const editCircleRadius = Math.max(
(Math.min(mapWidth, mapHeight) / 30) * Math.max(1 / stageScale, 1),
1
);
const editCircleProps = {
radius: editCircleRadius,
fill: "rgba(0, 0, 0, 0.5)",
stroke: "white",
strokeWidth: editCircleRadius / 5,
draggable: true,
onDragStart: handleScaleCircleDragStart,
onDragMove: handleScaleCircleDragMove,
onDragEnd: handleScaleCircleDragEnd,
onMouseDown: handleInteractivePointerDown,
onMouseUp: handleInteractivePointerUp,
onTouchStart: handleInteractivePointerDown,
onTouchEnd: handleInteractivePointerUp,
};
const editRectProps = {
fill: "transparent",
stroke: "white",
strokeWidth: editCircleRadius / 5,
};
function getHandleInset(handle) {
const topLeftHandle = topLeftHandleRef.current;
const topRightHandle = topRightHandleRef.current;
const bottomRightHandle = bottomRightHandleRef.current;
const bottomLeftHandle = bottomLeftHandleRef.current;
const topLeft = Vector2.divide(
{ x: topLeftHandle.x(), y: topLeftHandle.y() },
mapSize
);
const topRight = Vector2.divide(
{ x: topRightHandle.x(), y: topRightHandle.y() },
mapSize
);
const bottomRight = Vector2.divide(
{ x: bottomRightHandle.x(), y: bottomRightHandle.y() },
mapSize
);
const bottomLeft = Vector2.divide(
{ x: bottomLeftHandle.x(), y: bottomLeftHandle.y() },
mapSize
);
if (handle === topLeftHandle || handle === bottomRightHandle) {
return { topLeft, bottomRight };
} else {
return {
topLeft: { x: bottomLeft.x, y: topRight.y },
bottomRight: { x: topRight.x, y: bottomLeft.y },
};
}
}
function getHandlePositions() {
const topLeft = Vector2.multiply(map.grid.inset.topLeft, mapSize);
const bottomRight = Vector2.multiply(map.grid.inset.bottomRight, mapSize);
const size = Vector2.subtract(bottomRight, topLeft);
const offset = Vector2.multiply(topLeft, -1);
return {
topLeft,
topRight: { x: bottomRight.x, y: topLeft.y },
bottomRight,
bottomLeft: { x: topLeft.x, y: bottomRight.y },
size,
offset,
};
}
const handlePositions = getHandlePositions();
return (
<Group>
<Rect
width={handlePositions.size.x}
height={handlePositions.size.y}
offset={handlePositions.offset}
{...editRectProps}
/>
<Circle
ref={topLeftHandleRef}
x={handlePositions.topLeft.x}
y={handlePositions.topLeft.y}
{...editCircleProps}
/>
<Circle
ref={topRightHandleRef}
x={handlePositions.topRight.x}
y={handlePositions.topRight.y}
{...editCircleProps}
/>
<Circle
ref={bottomRightHandleRef}
x={handlePositions.bottomRight.x}
y={handlePositions.bottomRight.y}
{...editCircleProps}
/>
<Circle
ref={bottomLeftHandleRef}
x={handlePositions.bottomLeft.x}
y={handlePositions.bottomLeft.y}
{...editCircleProps}
/>
</Group>
);
}
export default MapGridEditor;

View File

@ -217,8 +217,7 @@ function loadVersions(db) {
map.group = "";
map.grid = {
size: { x: map.gridX, y: map.gridY },
scale: { x: 1, y: 1 },
offset: { x: 0, y: 0 },
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
type: "square",
};
delete map.gridX;

View File

@ -23,8 +23,7 @@ export const maps = Object.keys(mapSources).map((key) => ({
name: Case.capital(key),
grid: {
size: { x: 22, y: 22 },
scale: { x: 1, y: 1 },
offset: { x: 0, y: 0 },
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
type: "square",
},
width: 1024,

View File

@ -87,7 +87,10 @@ function EditMapModal({ isOpen, onDone, map, mapState }) {
<Label pt={2} pb={1}>
Edit map
</Label>
<MapEditor map={selectedMapWithChanges} />
<MapEditor
map={selectedMapWithChanges}
onSettingsChange={handleMapSettingsChange}
/>
<MapSettings
map={selectedMapWithChanges}
mapState={selectedMapStateWithChanges}

View File

@ -167,8 +167,7 @@ function SelectMapModal({
type: "file",
grid: {
size: { x: fileGridX, y: fileGridY },
scale: { x: 1, y: 1 },
offset: { x: 0, y: 0 },
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
type: "square",
},
width: image.width,