Added default map inset and grid size heuristic
This commit is contained in:
parent
f9a69a422b
commit
cce339224b
@ -6,6 +6,7 @@ import ReactResizeDetector from "react-resize-detector";
|
||||
import useMapImage from "../../helpers/useMapImage";
|
||||
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
|
||||
import useStageInteraction from "../../helpers/useStageInteraction";
|
||||
import { getMapDefaultInset } from "../../helpers/map";
|
||||
|
||||
import { MapInteractionProvider } from "../../contexts/MapInteractionContext";
|
||||
|
||||
@ -36,6 +37,13 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
mapHeight = map ? stageWidth * (map.height / map.width) : stageHeight;
|
||||
}
|
||||
|
||||
const defaultInset = getMapDefaultInset(
|
||||
map.width,
|
||||
map.height,
|
||||
map.grid.size.x,
|
||||
map.grid.size.y
|
||||
);
|
||||
|
||||
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
||||
const mapLayerRef = useRef();
|
||||
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
||||
@ -94,7 +102,7 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
function handleMapReset() {
|
||||
onSettingsChange("grid", {
|
||||
...map.grid,
|
||||
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
|
||||
inset: defaultInset,
|
||||
});
|
||||
}
|
||||
|
||||
@ -110,10 +118,10 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
};
|
||||
|
||||
const gridChanged =
|
||||
map.grid.inset.topLeft.x !== 0 ||
|
||||
map.grid.inset.topLeft.y !== 0 ||
|
||||
map.grid.inset.bottomRight.x !== 1 ||
|
||||
map.grid.inset.bottomRight.y !== 1;
|
||||
map.grid.inset.topLeft.x !== defaultInset.topLeft.x ||
|
||||
map.grid.inset.topLeft.y !== defaultInset.topLeft.y ||
|
||||
map.grid.inset.bottomRight.x !== defaultInset.bottomRight.x ||
|
||||
map.grid.inset.bottomRight.y !== defaultInset.bottomRight.y;
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
@ -35,13 +35,42 @@ function MapSettings({
|
||||
}
|
||||
}
|
||||
|
||||
function handleGridSizeChange(event, dimension) {
|
||||
function handleGridSizeXChange(event) {
|
||||
const value = parseInt(event.target.value);
|
||||
const gridY = map.grid.size.y;
|
||||
|
||||
let inset = map.grid.inset;
|
||||
|
||||
const gridScale =
|
||||
((inset.bottomRight.x - inset.topLeft.x) * map.width) / value;
|
||||
inset.bottomRight.y = (gridY * gridScale) / map.height;
|
||||
|
||||
onSettingsChange("grid", {
|
||||
...map.grid,
|
||||
inset,
|
||||
size: {
|
||||
...map.grid.size,
|
||||
[dimension]: value,
|
||||
x: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleGridSizeYChange(event) {
|
||||
const value = parseInt(event.target.value);
|
||||
const gridX = map.grid.size.x;
|
||||
|
||||
let inset = map.grid.inset;
|
||||
|
||||
const gridScale =
|
||||
((inset.bottomRight.x - inset.topLeft.x) * map.width) / gridX;
|
||||
inset.bottomRight.y = (value * gridScale) / map.height;
|
||||
|
||||
onSettingsChange("grid", {
|
||||
...map.grid,
|
||||
inset,
|
||||
size: {
|
||||
...map.grid.size,
|
||||
y: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -69,7 +98,7 @@ function MapSettings({
|
||||
type="number"
|
||||
name="gridX"
|
||||
value={`${(map && map.grid.size.x) || 0}`}
|
||||
onChange={(e) => handleGridSizeChange(e, "x")}
|
||||
onChange={handleGridSizeXChange}
|
||||
disabled={mapEmpty || map.type === "default"}
|
||||
min={1}
|
||||
my={1}
|
||||
@ -81,7 +110,7 @@ function MapSettings({
|
||||
type="number"
|
||||
name="gridY"
|
||||
value={`${(map && map.grid.size.y) || 0}`}
|
||||
onChange={(e) => handleGridSizeChange(e, "y")}
|
||||
onChange={handleGridSizeYChange}
|
||||
disabled={mapEmpty || map.type === "default"}
|
||||
min={1}
|
||||
my={1}
|
||||
|
53
src/helpers/map.js
Normal file
53
src/helpers/map.js
Normal file
@ -0,0 +1,53 @@
|
||||
export function getMapDefaultInset(width, height, gridX, gridY) {
|
||||
// Max the width
|
||||
const gridScale = width / gridX;
|
||||
const y = gridY * gridScale;
|
||||
const yNorm = y / height;
|
||||
return { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: yNorm } };
|
||||
}
|
||||
|
||||
// Get all factors of a number
|
||||
function factors(n) {
|
||||
const numbers = Array.from(Array(n + 1), (_, i) => i);
|
||||
return numbers.filter((i) => n % i === 0);
|
||||
}
|
||||
|
||||
// Greatest common divisor
|
||||
// Euclidean algorithm https://en.wikipedia.org/wiki/Euclidean_algorithm
|
||||
function gcd(a, b) {
|
||||
while (b !== 0) {
|
||||
const t = b;
|
||||
b = a % b;
|
||||
a = t;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// Find all dividers that fit into two numbers
|
||||
function dividers(a, b) {
|
||||
const d = gcd(a, b);
|
||||
return factors(d);
|
||||
}
|
||||
|
||||
const commonGridScales = [70, 111, 140, 300];
|
||||
|
||||
export function gridSizeHeuristic(width, height) {
|
||||
const div = dividers(width, height);
|
||||
if (div.length > 0) {
|
||||
// default to middle divider
|
||||
let scale = div[Math.floor(div.length / 2)];
|
||||
for (let common of commonGridScales) {
|
||||
// Check common but make sure the grid size is above 10
|
||||
if (div.includes(common) && width / common > 10 && height / common > 10) {
|
||||
scale = common;
|
||||
}
|
||||
}
|
||||
const x = Math.floor(width / scale);
|
||||
const y = Math.floor(height / scale);
|
||||
// Check grid size is below 100
|
||||
if (x < 100 && y < 100) {
|
||||
return { x, y };
|
||||
}
|
||||
}
|
||||
return { x: 22, y: 22 };
|
||||
}
|
@ -14,11 +14,11 @@ import blobToBuffer from "../helpers/blobToBuffer";
|
||||
import useKeyboard from "../helpers/useKeyboard";
|
||||
import { resizeImage } from "../helpers/image";
|
||||
import { useSearch, useGroup, handleItemSelect } from "../helpers/select";
|
||||
import { getMapDefaultInset, gridSizeHeuristic } from "../helpers/map";
|
||||
|
||||
import MapDataContext from "../contexts/MapDataContext";
|
||||
import AuthContext from "../contexts/AuthContext";
|
||||
|
||||
const defaultMapSize = 22;
|
||||
const defaultMapProps = {
|
||||
// Grid type
|
||||
showGrid: false,
|
||||
@ -99,34 +99,6 @@ function SelectMapModal({
|
||||
if (!file) {
|
||||
return Promise.reject();
|
||||
}
|
||||
let fileGridX = defaultMapSize;
|
||||
let fileGridY = defaultMapSize;
|
||||
let name = "Unknown Map";
|
||||
if (file.name) {
|
||||
// TODO: match all not supported on safari, find alternative
|
||||
if (file.name.matchAll) {
|
||||
// Match against a regex to find the grid size in the file name
|
||||
// e.g. Cave 22x23 will return [["22x22", "22", "x", "23"]]
|
||||
const gridMatches = [...file.name.matchAll(/(\d+) ?(x|X) ?(\d+)/g)];
|
||||
if (gridMatches.length > 0) {
|
||||
const lastMatch = gridMatches[gridMatches.length - 1];
|
||||
const matchX = parseInt(lastMatch[1]);
|
||||
const matchY = parseInt(lastMatch[3]);
|
||||
if (!isNaN(matchX) && !isNaN(matchY)) {
|
||||
fileGridX = matchX;
|
||||
fileGridY = matchY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove file extension
|
||||
name = file.name.replace(/\.[^/.]+$/, "");
|
||||
// Removed grid size expression
|
||||
name = name.replace(/(\[ ?|\( ?)?\d+ ?(x|X) ?\d+( ?\]| ?\))?/, "");
|
||||
// Clean string
|
||||
name = name.replace(/ +/g, " ");
|
||||
name = name.trim();
|
||||
}
|
||||
let image = new Image();
|
||||
setImageLoading(true);
|
||||
|
||||
@ -138,6 +110,37 @@ function SelectMapModal({
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
image.onload = async function () {
|
||||
// Find name and grid size
|
||||
let gridSize;
|
||||
let name = "Unknown Map";
|
||||
if (file.name) {
|
||||
if (file.name.matchAll) {
|
||||
// Match against a regex to find the grid size in the file name
|
||||
// e.g. Cave 22x23 will return [["22x22", "22", "x", "23"]]
|
||||
const gridMatches = [...file.name.matchAll(/(\d+) ?(x|X) ?(\d+)/g)];
|
||||
if (gridMatches.length > 0) {
|
||||
const lastMatch = gridMatches[gridMatches.length - 1];
|
||||
const matchX = parseInt(lastMatch[1]);
|
||||
const matchY = parseInt(lastMatch[3]);
|
||||
if (!isNaN(matchX) && !isNaN(matchY)) {
|
||||
gridSize = { x: matchX, y: matchY };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!gridSize) {
|
||||
gridSize = gridSizeHeuristic(image.width, image.height);
|
||||
}
|
||||
|
||||
// Remove file extension
|
||||
name = file.name.replace(/\.[^/.]+$/, "");
|
||||
// Removed grid size expression
|
||||
name = name.replace(/(\[ ?|\( ?)?\d+ ?(x|X) ?\d+( ?\]| ?\))?/, "");
|
||||
// Clean string
|
||||
name = name.replace(/ +/g, " ");
|
||||
name = name.trim();
|
||||
}
|
||||
|
||||
// Create resolutions
|
||||
const resolutions = {};
|
||||
for (let resolution of mapResolutions) {
|
||||
@ -166,8 +169,13 @@ function SelectMapModal({
|
||||
name,
|
||||
type: "file",
|
||||
grid: {
|
||||
size: { x: fileGridX, y: fileGridY },
|
||||
inset: { topLeft: { x: 0, y: 0 }, bottomRight: { x: 1, y: 1 } },
|
||||
size: gridSize,
|
||||
inset: getMapDefaultInset(
|
||||
image.width,
|
||||
image.height,
|
||||
gridSize.x,
|
||||
gridSize.y
|
||||
),
|
||||
type: "square",
|
||||
},
|
||||
width: image.width,
|
||||
|
Loading…
Reference in New Issue
Block a user