diff --git a/src/components/map/MapControls.js b/src/components/map/MapControls.js
index fb13f6e..2d2da33 100644
--- a/src/components/map/MapControls.js
+++ b/src/components/map/MapControls.js
@@ -8,7 +8,6 @@ import SelectMapButton from "./SelectMapButton";
import FogToolSettings from "./controls/FogToolSettings";
import DrawingToolSettings from "./controls/DrawingToolSettings";
-import MeasureToolSettings from "./controls/MeasureToolSettings";
import PointerToolSettings from "./controls/PointerToolSettings";
import PanToolIcon from "../../icons/PanToolIcon";
@@ -61,7 +60,6 @@ function MapContols({
id: "measure",
icon: ,
title: "Measure Tool (M)",
- SettingsComponent: MeasureToolSettings,
},
pointer: {
id: "pointer",
diff --git a/src/components/map/MapMeasure.js b/src/components/map/MapMeasure.js
index 2914be7..95f1170 100644
--- a/src/components/map/MapMeasure.js
+++ b/src/components/map/MapMeasure.js
@@ -14,14 +14,14 @@ import { getRelativePointerPosition } from "../../helpers/konva";
import useGridSnapping from "../../hooks/useGridSnapping";
-function MapMeasure({ map, selectedToolSettings, active }) {
+function MapMeasure({ map, active }) {
const {
stageScale,
mapWidth,
mapHeight,
interactionEmitter,
} = useMapInteraction();
- const { gridCellNormalizedSize, gridStrokeWidth } = useGrid();
+ const { grid, gridCellNormalizedSize, gridStrokeWidth } = useGrid();
const mapStageRef = useMapStage();
const [drawingShapeData, setDrawingShapeData] = useState(null);
const [isBrushDown, setIsBrushDown] = useState(false);
@@ -45,7 +45,7 @@ function MapMeasure({ map, selectedToolSettings, active }) {
return { multiplier: 1, unit: "", digits: 0 };
}
- const measureScale = parseToolScale(active && selectedToolSettings.scale);
+ const measureScale = parseToolScale(active && grid.measurement.scale);
const snapPositionToGrid = useGridSnapping();
@@ -95,7 +95,7 @@ function MapMeasure({ map, selectedToolSettings, active }) {
Vector2.divide(points[1], gridCellNormalizedSize),
precision
),
- selectedToolSettings.type
+ grid.measurement.type
);
setDrawingShapeData({
length,
diff --git a/src/components/map/MapSettings.js b/src/components/map/MapSettings.js
index 66306d7..c8f3276 100644
--- a/src/components/map/MapSettings.js
+++ b/src/components/map/MapSettings.js
@@ -23,6 +23,18 @@ const gridTypeSettings = [
{ value: "hexHorizontal", label: "Hex Horizontal" },
];
+const gridSquareMeasurementTypeSettings = [
+ { value: "chebyshev", label: "Chessboard (D&D 5e)" },
+ { value: "alternating", label: "Alternating Diagonal (D&D 3.5e)" },
+ { value: "euclidean", label: "Euclidean" },
+ { value: "manhattan", label: "Manhattan" },
+];
+
+const gridHexMeasurementTypeSettings = [
+ { value: "manhattan", label: "Manhattan" },
+ { value: "euclidean", label: "Euclidean" },
+];
+
function MapSettings({
map,
mapState,
@@ -69,11 +81,41 @@ function MapSettings({
}
function handleGridTypeChange(option) {
- let grid = { ...map.grid, type: option.value };
+ const type = option.value;
+ let grid = {
+ ...map.grid,
+ type,
+ measurement: {
+ ...map.grid.measurement,
+ type: type === "square" ? "chebyshev" : "manhattan",
+ },
+ };
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
onSettingsChange("grid", grid);
}
+ function handleGridMeasurementTypeChange(option) {
+ const grid = {
+ ...map.grid,
+ measurement: {
+ ...map.grid.measurement,
+ type: option.value,
+ },
+ };
+ onSettingsChange("grid", grid);
+ }
+
+ function handleGridMeasurementScaleChange(event) {
+ const grid = {
+ ...map.grid,
+ measurement: {
+ ...map.grid.measurement,
+ scale: event.target.value,
+ },
+ };
+ onSettingsChange("grid", grid);
+ }
+
function getMapSize() {
let size = 0;
if (map.quality === "original") {
@@ -131,42 +173,77 @@ function MapSettings({
-
-
-
-
-
-
-
- onSettingsChange("snapToGrid", e.target.checked)
+
+
+
+
+ onSettingsChange("showGrid", e.target.checked)
+ }
+ />
+ Draw Grid
+
+
+
+ onSettingsChange("snapToGrid", e.target.checked)
+ }
+ />
+ Snap to Grid
+
+
+
+
+
+ Grid Measurement
+
+
+ Grid Scale
+
+
{!mapEmpty && map.type !== "default" && (
diff --git a/src/components/map/controls/MeasureToolSettings.js b/src/components/map/controls/MeasureToolSettings.js
deleted file mode 100644
index e1a7a18..0000000
--- a/src/components/map/controls/MeasureToolSettings.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from "react";
-import { Flex, Input, Text } from "theme-ui";
-
-import ToolSection from "./ToolSection";
-import MeasureChebyshevIcon from "../../../icons/MeasureChebyshevIcon";
-import MeasureEuclideanIcon from "../../../icons/MeasureEuclideanIcon";
-import MeasureManhattanIcon from "../../../icons/MeasureManhattanIcon";
-import MeasureAlternatingIcon from "../../../icons/MeasureAlternatingIcon";
-
-import Divider from "../../Divider";
-
-import { useKeyboard } from "../../../contexts/KeyboardContext";
-
-function MeasureToolSettings({ settings, onSettingChange }) {
- // Keyboard shortcuts
- function handleKeyDown({ key }) {
- if (key === "g") {
- onSettingChange({ type: "chebyshev" });
- } else if (key === "l") {
- onSettingChange({ type: "euclidean" });
- } else if (key === "c") {
- onSettingChange({ type: "manhattan" });
- } else if (key === "a") {
- onSettingChange({ type: "alternating" });
- }
- }
-
- useKeyboard(handleKeyDown);
-
- const tools = [
- {
- id: "chebyshev",
- title: "Grid Distance (G)",
- isSelected: settings.type === "chebyshev",
- icon: ,
- },
- {
- id: "alternating",
- title: "Alternating Diagonal Distance (A)",
- isSelected: settings.type === "alternating",
- icon: ,
- },
- {
- id: "euclidean",
- title: "Line Distance (L)",
- isSelected: settings.type === "euclidean",
- icon: ,
- },
- {
- id: "manhattan",
- title: "City Block Distance (C)",
- isSelected: settings.type === "manhattan",
- icon: ,
- },
- ];
-
- return (
-
- onSettingChange({ type: tool.id })}
- />
-
-
- Scale:
-
- onSettingChange({ scale: e.target.value })}
- autoComplete="off"
- />
-
- );
-}
-
-export default MeasureToolSettings;
diff --git a/src/contexts/GridContext.js b/src/contexts/GridContext.js
index 88f872c..183b0b0 100644
--- a/src/contexts/GridContext.js
+++ b/src/contexts/GridContext.js
@@ -51,8 +51,8 @@ export function GridProvider({ grid, width, height, children }) {
gridPixelSize.height
);
const gridCellNormalizedSize = new Size(
- gridCellPixelSize.width / gridPixelSize.width,
- gridCellPixelSize.height / gridPixelSize.height
+ gridCellPixelSize.width / width,
+ gridCellPixelSize.height / height
);
const gridOffset = Vector2.multiply(grid.inset.topLeft, {
x: width,
diff --git a/src/database.js b/src/database.js
index 87fdd3c..6eef6ad 100644
--- a/src/database.js
+++ b/src/database.js
@@ -348,7 +348,7 @@ function loadVersions(db) {
);
}
- // 1.8.0 - Add thumbnail to maps
+ // 1.8.0 - Add thumbnail to maps and add measurement to grid
db.version(19)
.stores({})
.upgrade(async (tx) => {
@@ -362,6 +362,7 @@ function loadVersions(db) {
.toCollection()
.modify((map) => {
map.thumbnail = thumbnails[map.id];
+ map.grid.measurement = { type: "chebyshev", scale: "5ft" };
});
});
diff --git a/src/helpers/grid.js b/src/helpers/grid.js
index f09d5e0..cdc4b78 100644
--- a/src/helpers/grid.js
+++ b/src/helpers/grid.js
@@ -14,11 +14,18 @@ const GRID_TYPE_NOT_IMPLEMENTED = new Error("Grid type not implemented");
* @property {Vector2} bottomRight Bottom right position of the inset
*/
+/**
+ * @typedef GridMeasurement
+ * @property {("chebyshev"|"alternating"|"euclidean"|"manhattan")} type
+ * @property {string} scale
+ */
+
/**
* @typedef Grid
* @property {GridInset} inset The inset of the grid from the map
* @property {Vector2} size The number of columns and rows of the grid as `x` and `y`
* @property {("square"|"hexVertical"|"hexHorizontal")} type
+ * @property {GridMeasurement} measurement
*/
/**
diff --git a/src/icons/MeasureAlternatingIcon.js b/src/icons/MeasureAlternatingIcon.js
deleted file mode 100644
index a65e504..0000000
--- a/src/icons/MeasureAlternatingIcon.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-
-function MeasureAlternatingIcon() {
- return (
-
- );
-}
-
-export default MeasureAlternatingIcon;
diff --git a/src/icons/MeasureChebyshevIcon.js b/src/icons/MeasureChebyshevIcon.js
deleted file mode 100644
index 5073dd2..0000000
--- a/src/icons/MeasureChebyshevIcon.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-
-function MeasureChebyshevIcon() {
- return (
-
- );
-}
-
-export default MeasureChebyshevIcon;
diff --git a/src/icons/MeasureEuclideanIcon.js b/src/icons/MeasureEuclideanIcon.js
deleted file mode 100644
index 6b29bf5..0000000
--- a/src/icons/MeasureEuclideanIcon.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from "react";
-
-function MeasureEuclideanIcon() {
- return (
-
- );
-}
-
-export default MeasureEuclideanIcon;
diff --git a/src/icons/MeasureManhattanIcon.js b/src/icons/MeasureManhattanIcon.js
deleted file mode 100644
index 5371f99..0000000
--- a/src/icons/MeasureManhattanIcon.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-
-function MeasureManhattanIcon() {
- return (
-
- );
-}
-
-export default MeasureManhattanIcon;
diff --git a/src/maps/index.js b/src/maps/index.js
index 0aaa7ea..2b55878 100644
--- a/src/maps/index.js
+++ b/src/maps/index.js
@@ -25,6 +25,7 @@ export const maps = Object.keys(mapSources).map((key) => ({
size: { x: 22, y: 22 },
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
type: "square",
+ measurement: { type: "chebyshev", scale: "5ft" },
},
width: 1024,
height: 1024,
diff --git a/src/modals/EditMapModal.js b/src/modals/EditMapModal.js
index e140e1b..37399f0 100644
--- a/src/modals/EditMapModal.js
+++ b/src/modals/EditMapModal.js
@@ -14,7 +14,13 @@ import { getGridDefaultInset } from "../helpers/grid";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
function EditMapModal({ isOpen, onDone, mapId }) {
- const { updateMap, updateMapState, getMapFromDB, mapStates } = useMapData();
+ const {
+ updateMap,
+ updateMapState,
+ getMap,
+ getMapFromDB,
+ mapStates,
+ } = useMapData();
const [isLoading, setIsLoading] = useState(true);
const [map, setMap] = useState();
@@ -23,7 +29,12 @@ function EditMapModal({ isOpen, onDone, mapId }) {
useEffect(() => {
async function loadMap() {
setIsLoading(true);
- setMap(await getMapFromDB(mapId));
+ let loadingMap = getMap(mapId);
+ // Ensure file is loaded for map
+ if (loadingMap?.type === "file" && !loadingMap?.file) {
+ loadingMap = await getMapFromDB(mapId);
+ }
+ setMap(loadingMap);
setMapState(mapStates.find((state) => state.mapId === mapId));
setIsLoading(false);
}
diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js
index 488eb59..8f03924 100644
--- a/src/modals/SelectMapModal.js
+++ b/src/modals/SelectMapModal.js
@@ -213,11 +213,15 @@ function SelectMapModal({
grid: {
size: gridSize,
inset: getGridDefaultInset(
- { size: { x: gridSize.x, y: gridSize.y }, type: "square" },
+ { size: gridSize, type: "square" },
image.width,
image.height
),
type: "square",
+ measurement: {
+ type: "chebyshev",
+ scale: "5ft",
+ },
},
width: image.width,
height: image.height,
diff --git a/src/settings.js b/src/settings.js
index d1c1028..e59cfaf 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -37,12 +37,16 @@ function loadVersions(settings) {
...prev,
game: { usePassword: true },
}));
- // v1.8.0 - Added pointer color and grid snapping sensitivity
- settings.version(4, (prev) => ({
- ...prev,
- pointer: { color: "red" },
- map: { ...prev.map, gridSnappingSensitivity: 0.2 },
- }));
+ // v1.8.0 - Added pointer color, grid snapping sensitivity and remove measure
+ settings.version(4, (prev) => {
+ let newSettings = {
+ ...prev,
+ pointer: { color: "red" },
+ map: { ...prev.map, gridSnappingSensitivity: 0.2 },
+ };
+ delete newSettings.measure;
+ return newSettings;
+ });
}
export function getSettings() {