Optimise grid and interaction context

This commit is contained in:
Mitchell McCaffrey
2021-03-12 11:02:58 +11:00
parent 01d738b9c6
commit e6fd10a904
14 changed files with 406 additions and 135 deletions

View File

@@ -1,4 +1,4 @@
import React, { useContext } from "react";
import React, { useContext, useState, useEffect } from "react";
import Vector2 from "../helpers/Vector2";
import Size from "../helpers/Size";
@@ -37,55 +37,103 @@ const defaultValue = {
gridCellPixelOffset: new Vector2(0, 0),
};
const GridContext = React.createContext(defaultValue);
const GridContext = React.createContext(defaultValue.grid);
const GridPixelSizeContext = React.createContext(defaultValue.gridPixelSize);
const GridCellPixelSizeContext = React.createContext(
defaultValue.gridCellPixelSize
);
const GridCellNormalizedSizeContext = React.createContext(
defaultValue.gridCellNormalizedSize
);
const GridOffsetContext = React.createContext(defaultValue.gridOffset);
const GridStrokeWidthContext = React.createContext(
defaultValue.gridStrokeWidth
);
const GridCellPixelOffsetContext = React.createContext(
defaultValue.gridCellPixelOffset
);
const defaultStrokeWidth = 1 / 10;
export function GridProvider({ grid, width, height, children }) {
export function GridProvider({ grid: inputGrid, width, height, children }) {
let grid = inputGrid;
if (!grid?.size.x || !grid?.size.y) {
return (
<GridContext.Provider value={defaultValue}>
{children}
</GridContext.Provider>
grid = defaultValue.grid;
}
const [gridPixelSize, setGridPixelSize] = useState(
defaultValue.gridCellPixelSize
);
const [gridCellPixelSize, setGridCellPixelSize] = useState(
defaultValue.gridCellPixelSize
);
const [gridCellNormalizedSize, setGridCellNormalizedSize] = useState(
defaultValue.gridCellNormalizedSize
);
const [gridOffset, setGridOffset] = useState(defaultValue.gridOffset);
const [gridStrokeWidth, setGridStrokeWidth] = useState(
defaultValue.gridStrokeWidth
);
const [gridCellPixelOffset, setGridCellPixelOffset] = useState(
defaultValue.gridCellPixelOffset
);
useEffect(() => {
const _gridPixelSize = getGridPixelSize(grid, width, height);
const _gridCellPixelSize = getCellPixelSize(
grid,
_gridPixelSize.width,
_gridPixelSize.height
);
}
const _gridCellNormalizedSize = new Size(
_gridCellPixelSize.width / width,
_gridCellPixelSize.height / height
);
const _gridOffset = Vector2.multiply(grid.inset.topLeft, {
x: width,
y: height,
});
const _gridStrokeWidth =
(_gridCellPixelSize.width < _gridCellPixelSize.height
? _gridCellPixelSize.width
: _gridCellPixelSize.height) * defaultStrokeWidth;
const gridPixelSize = getGridPixelSize(grid, width, height);
const gridCellPixelSize = getCellPixelSize(
grid,
gridPixelSize.width,
gridPixelSize.height
let _gridCellPixelOffset = { x: 0, y: 0 };
// Move hex tiles to top left
if (grid.type === "hexVertical" || grid.type === "hexHorizontal") {
_gridCellPixelOffset = Vector2.multiply(_gridCellPixelSize, 0.5);
}
setGridPixelSize(_gridPixelSize);
setGridCellPixelSize(_gridCellPixelSize);
setGridCellNormalizedSize(_gridCellNormalizedSize);
setGridOffset(_gridOffset);
setGridStrokeWidth(_gridStrokeWidth);
setGridCellPixelOffset(_gridCellPixelOffset);
}, [grid, width, height]);
return (
<GridContext.Provider value={grid}>
<GridPixelSizeContext.Provider value={gridPixelSize}>
<GridCellPixelSizeContext.Provider value={gridCellPixelSize}>
<GridCellNormalizedSizeContext.Provider
value={gridCellNormalizedSize}
>
<GridOffsetContext.Provider value={gridOffset}>
<GridStrokeWidthContext.Provider value={gridStrokeWidth}>
<GridCellPixelOffsetContext.Provider
value={gridCellPixelOffset}
>
{children}
</GridCellPixelOffsetContext.Provider>
</GridStrokeWidthContext.Provider>
</GridOffsetContext.Provider>
</GridCellNormalizedSizeContext.Provider>
</GridCellPixelSizeContext.Provider>
</GridPixelSizeContext.Provider>
</GridContext.Provider>
);
const gridCellNormalizedSize = new Size(
gridCellPixelSize.width / width,
gridCellPixelSize.height / height
);
const gridOffset = Vector2.multiply(grid.inset.topLeft, {
x: width,
y: height,
});
const gridStrokeWidth =
(gridCellPixelSize.width < gridCellPixelSize.height
? gridCellPixelSize.width
: gridCellPixelSize.height) * defaultStrokeWidth;
let gridCellPixelOffset = { x: 0, y: 0 };
// Move hex tiles to top left
if (grid.type === "hexVertical" || grid.type === "hexHorizontal") {
gridCellPixelOffset = Vector2.multiply(gridCellPixelSize, 0.5);
}
const value = {
grid,
gridPixelSize,
gridCellPixelSize,
gridCellNormalizedSize,
gridOffset,
gridStrokeWidth,
gridCellPixelOffset,
};
return <GridContext.Provider value={value}>{children}</GridContext.Provider>;
}
export function useGrid() {
@@ -96,4 +144,54 @@ export function useGrid() {
return context;
}
export default GridContext;
export function useGridPixelSize() {
const context = useContext(GridPixelSizeContext);
if (context === undefined) {
throw new Error("useGridPixelSize must be used within a GridProvider");
}
return context;
}
export function useGridCellPixelSize() {
const context = useContext(GridCellPixelSizeContext);
if (context === undefined) {
throw new Error("useGridCellPixelSize must be used within a GridProvider");
}
return context;
}
export function useGridCellNormalizedSize() {
const context = useContext(GridCellNormalizedSizeContext);
if (context === undefined) {
throw new Error(
"useGridCellNormalizedSize must be used within a GridProvider"
);
}
return context;
}
export function useGridOffset() {
const context = useContext(GridOffsetContext);
if (context === undefined) {
throw new Error("useGridOffset must be used within a GridProvider");
}
return context;
}
export function useGridStrokeWidth() {
const context = useContext(GridStrokeWidthContext);
if (context === undefined) {
throw new Error("useGridStrokeWidth must be used within a GridProvider");
}
return context;
}
export function useGridCellPixelOffset() {
const context = useContext(GridCellPixelOffsetContext);
if (context === undefined) {
throw new Error(
"useGridCellPixelOffset must be used within a GridProvider"
);
}
return context;
}

View File

@@ -1,24 +1,125 @@
import React, { useContext } from "react";
import useDebounce from "../hooks/useDebounce";
const MapInteractionContext = React.createContext({
stageScale: 1,
stageWidth: 1,
stageHeight: 1,
setPreventMapInteraction: () => {},
mapWidth: 1,
mapHeight: 1,
interactionEmitter: null,
});
export const MapInteractionProvider = MapInteractionContext.Provider;
const StageScaleContext = React.createContext();
const DebouncedStageScaleContext = React.createContext();
const StageWidthContext = React.createContext();
const StageHeightContext = React.createContext();
const SetPreventMapInteractionContext = React.createContext();
const MapWidthContext = React.createContext();
const MapHeightContext = React.createContext();
const InteractionEmitterContext = React.createContext();
export function useMapInteraction() {
const context = useContext(MapInteractionContext);
export function MapInteractionProvider({ value, children }) {
const {
stageScale,
stageWidth,
stageHeight,
setPreventMapInteraction,
mapWidth,
mapHeight,
interactionEmitter,
} = value;
const debouncedStageScale = useDebounce(stageScale, 200);
return (
<InteractionEmitterContext.Provider value={interactionEmitter}>
<SetPreventMapInteractionContext.Provider
value={setPreventMapInteraction}
>
<StageWidthContext.Provider value={stageWidth}>
<StageHeightContext.Provider value={stageHeight}>
<MapWidthContext.Provider value={mapWidth}>
<MapHeightContext.Provider value={mapHeight}>
<StageScaleContext.Provider value={stageScale}>
<DebouncedStageScaleContext.Provider
value={debouncedStageScale}
>
{children}
</DebouncedStageScaleContext.Provider>
</StageScaleContext.Provider>
</MapHeightContext.Provider>
</MapWidthContext.Provider>
</StageHeightContext.Provider>
</StageWidthContext.Provider>
</SetPreventMapInteractionContext.Provider>
</InteractionEmitterContext.Provider>
);
}
export function useInteractionEmitter() {
const context = useContext(InteractionEmitterContext);
if (context === undefined) {
throw new Error(
"useMapInteraction must be used within a MapInteractionProvider"
"useInteractionEmitter must be used within a MapInteractionProvider"
);
}
return context;
}
export default MapInteractionContext;
export function useSetPreventMapInteraction() {
const context = useContext(SetPreventMapInteractionContext);
if (context === undefined) {
throw new Error(
"useSetPreventMapInteraction must be used within a MapInteractionProvider"
);
}
return context;
}
export function useStageWidth() {
const context = useContext(StageWidthContext);
if (context === undefined) {
throw new Error(
"useStageWidth must be used within a MapInteractionProvider"
);
}
return context;
}
export function useStageHeight() {
const context = useContext(StageHeightContext);
if (context === undefined) {
throw new Error(
"useStageHeight must be used within a MapInteractionProvider"
);
}
return context;
}
export function useMapWidth() {
const context = useContext(MapWidthContext);
if (context === undefined) {
throw new Error("useMapWidth must be used within a MapInteractionProvider");
}
return context;
}
export function useMapHeight() {
const context = useContext(MapHeightContext);
if (context === undefined) {
throw new Error(
"useMapHeight must be used within a MapInteractionProvider"
);
}
return context;
}
export function useStageScale() {
const context = useContext(StageScaleContext);
if (context === undefined) {
throw new Error(
"useStageScale must be used within a MapInteractionProvider"
);
}
return context;
}
export function useDebouncedStageScale() {
const context = useContext(DebouncedStageScaleContext);
if (context === undefined) {
throw new Error(
"useDebouncedStageScale must be used within a MapInteractionProvider"
);
}
return context;
}