Changed grid scale and offset to inset and added grid editor
This commit is contained in:
parent
539f216cfe
commit
3e5478f7a8
@ -330,9 +330,7 @@ function Map({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const mapGrid = map && map.showGrid && (
|
const mapGrid = map && map.showGrid && <MapGrid map={map} />;
|
||||||
<MapGrid map={map} gridSize={gridSizeNormalized} />
|
|
||||||
);
|
|
||||||
|
|
||||||
const mapMeasure = (
|
const mapMeasure = (
|
||||||
<MapMeasure
|
<MapMeasure
|
||||||
|
@ -7,7 +7,12 @@ import useMapImage from "../../helpers/useMapImage";
|
|||||||
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
|
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
|
||||||
import useStageInteraction from "../../helpers/useStageInteraction";
|
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 [mapImageSource] = useMapImage(map);
|
||||||
|
|
||||||
const [stageWidth, setStageWidth] = useState(1);
|
const [stageWidth, setStageWidth] = useState(1);
|
||||||
@ -29,6 +34,7 @@ function MapEditor({ map }) {
|
|||||||
|
|
||||||
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
||||||
const mapLayerRef = useRef();
|
const mapLayerRef = useRef();
|
||||||
|
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
||||||
|
|
||||||
function handleResize(width, height) {
|
function handleResize(width, height) {
|
||||||
setStageWidth(width);
|
setStageWidth(width);
|
||||||
@ -66,12 +72,30 @@ function MapEditor({ map }) {
|
|||||||
mapLayerRef.current,
|
mapLayerRef.current,
|
||||||
stageScale,
|
stageScale,
|
||||||
setStageScale,
|
setStageScale,
|
||||||
stageTranslateRef
|
stageTranslateRef,
|
||||||
|
"pan",
|
||||||
|
preventMapInteraction
|
||||||
);
|
);
|
||||||
|
|
||||||
const containerRef = useRef();
|
const containerRef = useRef();
|
||||||
usePreventOverscroll(containerRef);
|
usePreventOverscroll(containerRef);
|
||||||
|
|
||||||
|
function handleGridChange(inset) {
|
||||||
|
onSettingsChange("grid", {
|
||||||
|
...map.grid,
|
||||||
|
inset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapInteraction = {
|
||||||
|
stageScale,
|
||||||
|
stageWidth,
|
||||||
|
stageHeight,
|
||||||
|
setPreventMapInteraction,
|
||||||
|
mapWidth,
|
||||||
|
mapHeight,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -96,6 +120,10 @@ function MapEditor({ map }) {
|
|||||||
>
|
>
|
||||||
<Layer ref={mapLayerRef}>
|
<Layer ref={mapLayerRef}>
|
||||||
<Image image={mapImageSource} width={mapWidth} height={mapHeight} />
|
<Image image={mapImageSource} width={mapWidth} height={mapHeight} />
|
||||||
|
<MapInteractionProvider value={mapInteraction}>
|
||||||
|
<MapGrid map={map} strokeWidth={0.5} />
|
||||||
|
<MapGridEditor map={map} onGridChange={handleGridChange} />
|
||||||
|
</MapInteractionProvider>
|
||||||
</Layer>
|
</Layer>
|
||||||
</Stage>
|
</Stage>
|
||||||
</ReactResizeDetector>
|
</ReactResizeDetector>
|
||||||
|
@ -10,7 +10,7 @@ import { mapSources as defaultMapSources } from "../../maps";
|
|||||||
import { getStrokeWidth } from "../../helpers/drawing";
|
import { getStrokeWidth } from "../../helpers/drawing";
|
||||||
import { getImageLightness } from "../../helpers/image";
|
import { getImageLightness } from "../../helpers/image";
|
||||||
|
|
||||||
function MapGrid({ map, gridSize }) {
|
function MapGrid({ map, strokeWidth }) {
|
||||||
let mapSourceMap = map;
|
let mapSourceMap = map;
|
||||||
// Use lowest resolution for grid lightness
|
// Use lowest resolution for grid lightness
|
||||||
if (map && map.type === "file" && map.resolutions) {
|
if (map && map.type === "file" && map.resolutions) {
|
||||||
@ -24,6 +24,10 @@ function MapGrid({ map, gridSize }) {
|
|||||||
|
|
||||||
const gridX = map && map.grid.size.x;
|
const gridX = map && map.grid.size.x;
|
||||||
const gridY = map && map.grid.size.y;
|
const gridY = map && map.grid.size.y;
|
||||||
|
const gridSizeNormalized = {
|
||||||
|
x: gridX ? 1 / gridX : 0,
|
||||||
|
y: gridY ? 1 / gridY : 0,
|
||||||
|
};
|
||||||
|
|
||||||
const { mapWidth, mapHeight } = useContext(MapInteractionContext);
|
const { mapWidth, mapHeight } = useContext(MapInteractionContext);
|
||||||
|
|
||||||
@ -35,6 +39,7 @@ function MapGrid({ map, gridSize }) {
|
|||||||
// When the map changes find the average lightness of its pixels
|
// When the map changes find the average lightness of its pixels
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mapLoadingStatus === "loaded") {
|
if (mapLoadingStatus === "loaded") {
|
||||||
|
console.log("getting lightness");
|
||||||
setIsImageLight(getImageLightness(mapImage));
|
setIsImageLight(getImageLightness(mapImage));
|
||||||
}
|
}
|
||||||
}, [mapImage, mapLoadingStatus]);
|
}, [mapImage, mapLoadingStatus]);
|
||||||
@ -46,7 +51,12 @@ function MapGrid({ map, gridSize }) {
|
|||||||
key={`grid_x_${x}`}
|
key={`grid_x_${x}`}
|
||||||
points={[x * lineSpacingX, 0, x * lineSpacingX, mapHeight]}
|
points={[x * lineSpacingX, 0, x * lineSpacingX, mapHeight]}
|
||||||
stroke={isImageLight ? "black" : "white"}
|
stroke={isImageLight ? "black" : "white"}
|
||||||
strokeWidth={getStrokeWidth(0.1, gridSize, mapWidth, mapHeight)}
|
strokeWidth={getStrokeWidth(
|
||||||
|
strokeWidth,
|
||||||
|
gridSizeNormalized,
|
||||||
|
mapWidth,
|
||||||
|
mapHeight
|
||||||
|
)}
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -57,7 +67,12 @@ function MapGrid({ map, gridSize }) {
|
|||||||
key={`grid_y_${y}`}
|
key={`grid_y_${y}`}
|
||||||
points={[0, y * lineSpacingY, mapWidth, y * lineSpacingY]}
|
points={[0, y * lineSpacingY, mapWidth, y * lineSpacingY]}
|
||||||
stroke={isImageLight ? "black" : "white"}
|
stroke={isImageLight ? "black" : "white"}
|
||||||
strokeWidth={getStrokeWidth(0.1, gridSize, mapWidth, mapHeight)}
|
strokeWidth={getStrokeWidth(
|
||||||
|
strokeWidth,
|
||||||
|
gridSizeNormalized,
|
||||||
|
mapWidth,
|
||||||
|
mapHeight
|
||||||
|
)}
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -66,4 +81,8 @@ function MapGrid({ map, gridSize }) {
|
|||||||
return <Group>{lines}</Group>;
|
return <Group>{lines}</Group>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MapGrid.defaultProps = {
|
||||||
|
strokeWidth: 0.1,
|
||||||
|
};
|
||||||
|
|
||||||
export default MapGrid;
|
export default MapGrid;
|
||||||
|
156
src/components/map/MapGridEditor.js
Normal file
156
src/components/map/MapGridEditor.js
Normal 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;
|
@ -217,8 +217,7 @@ function loadVersions(db) {
|
|||||||
map.group = "";
|
map.group = "";
|
||||||
map.grid = {
|
map.grid = {
|
||||||
size: { x: map.gridX, y: map.gridY },
|
size: { x: map.gridX, y: map.gridY },
|
||||||
scale: { x: 1, y: 1 },
|
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
|
||||||
offset: { x: 0, y: 0 },
|
|
||||||
type: "square",
|
type: "square",
|
||||||
};
|
};
|
||||||
delete map.gridX;
|
delete map.gridX;
|
||||||
|
@ -23,8 +23,7 @@ export const maps = Object.keys(mapSources).map((key) => ({
|
|||||||
name: Case.capital(key),
|
name: Case.capital(key),
|
||||||
grid: {
|
grid: {
|
||||||
size: { x: 22, y: 22 },
|
size: { x: 22, y: 22 },
|
||||||
scale: { x: 1, y: 1 },
|
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
|
||||||
offset: { x: 0, y: 0 },
|
|
||||||
type: "square",
|
type: "square",
|
||||||
},
|
},
|
||||||
width: 1024,
|
width: 1024,
|
||||||
|
@ -87,7 +87,10 @@ function EditMapModal({ isOpen, onDone, map, mapState }) {
|
|||||||
<Label pt={2} pb={1}>
|
<Label pt={2} pb={1}>
|
||||||
Edit map
|
Edit map
|
||||||
</Label>
|
</Label>
|
||||||
<MapEditor map={selectedMapWithChanges} />
|
<MapEditor
|
||||||
|
map={selectedMapWithChanges}
|
||||||
|
onSettingsChange={handleMapSettingsChange}
|
||||||
|
/>
|
||||||
<MapSettings
|
<MapSettings
|
||||||
map={selectedMapWithChanges}
|
map={selectedMapWithChanges}
|
||||||
mapState={selectedMapStateWithChanges}
|
mapState={selectedMapStateWithChanges}
|
||||||
|
@ -167,8 +167,7 @@ function SelectMapModal({
|
|||||||
type: "file",
|
type: "file",
|
||||||
grid: {
|
grid: {
|
||||||
size: { x: fileGridX, y: fileGridY },
|
size: { x: fileGridX, y: fileGridY },
|
||||||
scale: { x: 1, y: 1 },
|
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
|
||||||
offset: { x: 0, y: 0 },
|
|
||||||
type: "square",
|
type: "square",
|
||||||
},
|
},
|
||||||
width: image.width,
|
width: image.width,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user