This commit is contained in:
Mitchell McCaffrey 2021-07-08 12:00:47 +10:00
parent 62686136ab
commit 123ebd880a
6 changed files with 151 additions and 116 deletions

View File

@ -31,72 +31,70 @@ import Session from "../../network/Session";
import { Grid } from "../../helpers/grid"; import { Grid } from "../../helpers/grid";
import { ImageFile } from "../../helpers/image"; import { ImageFile } from "../../helpers/image";
export type Resolutions = Record<string, ImageFile> export type Resolutions = Record<string, ImageFile>;
export type Map = { export type Map = {
id: string, id: string;
name: string, name: string;
owner: string, owner: string;
file?: Uint8Array, file?: Uint8Array;
quality?: string, quality?: string;
resolutions?: Resolutions, resolutions?: Resolutions;
grid: Grid, grid: Grid;
group: string, group: string;
width: number, width: number;
height: number, height: number;
type: string, type: string;
lastUsed: number, lastUsed: number;
lastModified: number, lastModified: number;
created: number, created: number;
showGrid: boolean, showGrid: boolean;
snapToGrid: boolean, snapToGrid: boolean;
thumbnail?: ImageFile, thumbnail?: ImageFile;
} };
export type Note = { export type Note = {
id: string, id: string;
color: string, color: string;
lastModified: number, lastModified: number;
lastModifiedBy: string, lastModifiedBy: string;
locked: boolean, locked: boolean;
size: number, size: number;
text: string, text: string;
textOnly: boolean, textOnly: boolean;
visible: boolean, visible: boolean;
x: number, x: number;
y: number, y: number;
} };
export type TokenState = { export type TokenState = {
id: string, id: string;
tokenId: string, tokenId: string;
owner: string, owner: string;
size: number, size: number;
label: string, category: string;
status: string[], label: string;
x: number, statuses: string[];
y: number, x: number;
lastModifiedBy: string, y: number;
lastModified: number, lastModifiedBy: string;
rotation: number, lastModified: number;
locked: boolean, rotation: number;
visible: boolean locked: boolean;
} visible: boolean;
type: "default" | "file";
outline: any;
width: number;
height: number;
};
interface PathId extends Path {
id: string
}
interface ShapeId extends Shape {
id: string
}
export type MapState = { export type MapState = {
tokens: Record<string, TokenState>, tokens: Record<string, TokenState>;
drawShapes: PathId | ShapeId, drawShapes: Record<string, Path | Shape>;
fogShapes: Fog[], fogShapes: Record<string, Fog>;
editFlags: ["drawing", "tokens", "notes", "fog"], editFlags: ["drawing", "tokens", "notes", "fog"];
notes: Note[], notes: Record<string, Note>;
mapId: string, mapId: string;
} };
function Map({ function Map({
map, map,
@ -121,34 +119,35 @@ function Map({
disabledTokens, disabledTokens,
session, session,
}: { }: {
map: any map: any;
mapState: MapState mapState: MapState;
mapActions: any, mapActions: any;
onMapTokenStateChange: any, onMapTokenStateChange: any;
onMapTokenStateRemove: any, onMapTokenStateRemove: any;
onMapChange: any, onMapChange: any;
onMapReset: any, onMapReset: any;
onMapDraw: any, onMapDraw: any;
onMapDrawUndo: any, onMapDrawUndo: any;
onMapDrawRedo: any, onMapDrawRedo: any;
onFogDraw: any, onFogDraw: any;
onFogDrawUndo: any, onFogDrawUndo: any;
onFogDrawRedo: any, onFogDrawRedo: any;
onMapNoteChange: any, onMapNoteChange: any;
onMapNoteRemove: any, onMapNoteRemove: any;
allowMapDrawing: boolean, allowMapDrawing: boolean;
allowFogDrawing: boolean, allowFogDrawing: boolean;
allowMapChange: boolean, allowMapChange: boolean;
allowNoteEditing: boolean, allowNoteEditing: boolean;
disabledTokens: any, disabledTokens: any;
session: Session session: Session;
}) { }) {
const { addToast } = useToasts(); const { addToast } = useToasts();
const { tokensById } = useTokenData(); const { tokensById } = useTokenData();
const [selectedToolId, setSelectedToolId] = useState("move"); const [selectedToolId, setSelectedToolId] = useState("move");
const { settings, setSettings }: { settings: any, setSettings: any} = useSettings(); const { settings, setSettings }: { settings: any; setSettings: any } =
useSettings();
function handleToolSettingChange(tool: any, change: any) { function handleToolSettingChange(tool: any, change: any) {
setSettings((prevSettings: any) => ({ setSettings((prevSettings: any) => ({
@ -224,7 +223,10 @@ function Map({
disabledControls.push("note"); disabledControls.push("note");
} }
const disabledSettings: { fog: any[], drawing: any[]} = { fog: [], drawing: [] }; const disabledSettings: { fog: any[]; drawing: any[] } = {
fog: [],
drawing: [],
};
if (drawShapes.length === 0) { if (drawShapes.length === 0) {
disabledSettings.drawing.push("erase"); disabledSettings.drawing.push("erase");
} }
@ -263,9 +265,18 @@ function Map({
/> />
); );
const [isTokenMenuOpen, setIsTokenMenuOpen]: [ isTokenMenuOpen: boolean, setIsTokenMenuOpen: React.Dispatch<React.SetStateAction<boolean>>] = useState<boolean>(false); const [isTokenMenuOpen, setIsTokenMenuOpen]: [
const [tokenMenuOptions, setTokenMenuOptions]: [ tokenMenuOptions: any, setTokenMenuOptions: any ] = useState({}); isTokenMenuOpen: boolean,
const [tokenDraggingOptions, setTokenDraggingOptions]: [ tokenDraggingOptions: any, setTokenDragginOptions: any ] = useState(); setIsTokenMenuOpen: React.Dispatch<React.SetStateAction<boolean>>
] = useState<boolean>(false);
const [tokenMenuOptions, setTokenMenuOptions]: [
tokenMenuOptions: any,
setTokenMenuOptions: any
] = useState({});
const [tokenDraggingOptions, setTokenDraggingOptions]: [
tokenDraggingOptions: any,
setTokenDragginOptions: any
] = useState();
function handleTokenMenuOpen(tokenStateId: string, tokenImage: any) { function handleTokenMenuOpen(tokenStateId: string, tokenImage: any) {
setTokenMenuOptions({ tokenStateId, tokenImage }); setTokenMenuOptions({ tokenStateId, tokenImage });
setIsTokenMenuOpen(true); setIsTokenMenuOpen(true);
@ -338,10 +349,7 @@ function Map({
const mapGrid = map && map.showGrid && <MapGrid map={map} />; const mapGrid = map && map.showGrid && <MapGrid map={map} />;
const mapMeasure = ( const mapMeasure = (
<MapMeasure <MapMeasure map={map} active={selectedToolId === "measure"} />
map={map}
active={selectedToolId === "measure"}
/>
); );
const mapPointer = ( const mapPointer = (
@ -353,7 +361,7 @@ function Map({
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false); const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false);
const [noteMenuOptions, setNoteMenuOptions] = useState<any>({}); const [noteMenuOptions, setNoteMenuOptions] = useState<any>({});
const [noteDraggingOptions, setNoteDraggingOptions]= useState<any>(); const [noteDraggingOptions, setNoteDraggingOptions] = useState<any>();
function handleNoteMenuOpen(noteId: string, noteNode: any) { function handleNoteMenuOpen(noteId: string, noteNode: any) {
setNoteMenuOptions({ noteId, noteNode }); setNoteMenuOptions({ noteId, noteNode });
setIsNoteMenuOpen(true); setIsNoteMenuOpen(true);

View File

@ -11,12 +11,8 @@ import { getGroupItems } from "../../helpers/group";
import { useGroup } from "../../contexts/GroupContext"; import { useGroup } from "../../contexts/GroupContext";
function MapTiles({ mapsById, onMapEdit, onMapSelect, subgroup }) { function MapTiles({ mapsById, onMapEdit, onMapSelect, subgroup }) {
const { const { selectedGroupIds, selectMode, onGroupOpen, onGroupSelect } =
selectedGroupIds, useGroup();
selectMode,
onGroupOpen,
onGroupSelect,
} = useGroup();
function renderTile(group) { function renderTile(group) {
if (group.type === "item") { if (group.type === "item") {
@ -66,4 +62,8 @@ function MapTiles({ mapsById, onMapEdit, onMapSelect, subgroup }) {
); );
} }
MapTiles.defaultProps = {
subgroup: false,
};
export default MapTiles; export default MapTiles;

View File

@ -12,12 +12,8 @@ import { getGroupItems } from "../../helpers/group";
import { useGroup } from "../../contexts/GroupContext"; import { useGroup } from "../../contexts/GroupContext";
function TokenTiles({ tokensById, onTokenEdit, subgroup }) { function TokenTiles({ tokensById, onTokenEdit, subgroup }) {
const { const { selectedGroupIds, selectMode, onGroupOpen, onGroupSelect } =
selectedGroupIds, useGroup();
selectMode,
onGroupOpen,
onGroupSelect,
} = useGroup();
function renderTile(group) { function renderTile(group) {
if (group.type === "item") { if (group.type === "item") {
@ -70,4 +66,8 @@ function TokenTiles({ tokensById, onTokenEdit, subgroup }) {
); );
} }
TokenTiles.defaultProps = {
subgroup: false,
};
export default TokenTiles; export default TokenTiles;

View File

@ -1,8 +1,6 @@
import React, { useContext } from "react"; import React, { useContext } from "react";
const MapStageContext = React.createContext({ const MapStageContext = React.createContext({ current: null });
mapStageRef: { current: null },
});
export const MapStageProvider: any = MapStageContext.Provider; export const MapStageProvider: any = MapStageContext.Provider;
export function useMapStage() { export function useMapStage() {

View File

@ -44,10 +44,11 @@ class Vector2 {
} }
/** /**
* Returns the length of vector `p` Note: magnitude to not conflict with native length property
* @param {Vector2} p * @param {Vector2} p
* @returns {number} Length of `p` * @returns {number} Length of `p`
*/ */
static setLength(p: Vector2): number { static magnitude(p: Vector2): number {
return Math.sqrt(this.lengthSquared(p)); return Math.sqrt(this.lengthSquared(p));
} }
@ -56,7 +57,7 @@ class Vector2 {
* @returns {Vector2} `p` normalized, if length of `p` is 0 `{x: 0, y: 0}` is returned * @returns {Vector2} `p` normalized, if length of `p` is 0 `{x: 0, y: 0}` is returned
*/ */
static normalize(p: Vector2): Vector2 { static normalize(p: Vector2): Vector2 {
const l = this.setLength(p); const l = this.magnitude(p);
if (l === 0) { if (l === 0) {
return { x: 0, y: 0 }; return { x: 0, y: 0 };
} }
@ -271,7 +272,7 @@ class Vector2 {
const pa = this.subtract(p, a); const pa = this.subtract(p, a);
const ba = this.subtract(b, a); const ba = this.subtract(b, a);
const h = Math.min(Math.max(this.dot(pa, ba) / this.dot(ba, ba), 0), 1); const h = Math.min(Math.max(this.dot(pa, ba) / this.dot(ba, ba), 0), 1);
const distance = this.setLength(this.subtract(pa, this.multiply(ba, h))); const distance = this.magnitude(this.subtract(pa, this.multiply(ba, h)));
const point = this.add(a, this.multiply(ba, h)); const point = this.add(a, this.multiply(ba, h));
return { distance, point }; return { distance, point };
} }
@ -443,7 +444,7 @@ class Vector2 {
* @returns {number} * @returns {number}
*/ */
static distance(a: Vector2, b: Vector2): number { static distance(a: Vector2, b: Vector2): number {
return this.setLength(this.subtract(a, b)); return this.magnitude(this.subtract(a, b));
} }
/** /**

View File

@ -6,7 +6,7 @@ import Color from "color";
import Vector2 from "./Vector2"; import Vector2 from "./Vector2";
// Holes should be wound in the opposite direction as the containing points array // Holes should be wound in the opposite direction as the containing points array
export function HoleyLine({ holes, ...props }: { holes: any, props: []}) { export function HoleyLine({ holes, ...props }: { holes: any; props: [] }) {
// Converted from https://github.com/rfestag/konva/blob/master/src/shapes/Line.ts // Converted from https://github.com/rfestag/konva/blob/master/src/shapes/Line.ts
function drawLine(points: number[], context: any, shape: any) { function drawLine(points: number[], context: any, shape: any) {
const length = points.length; const length = points.length;
@ -109,7 +109,19 @@ export function HoleyLine({ holes, ...props }: { holes: any, props: []}) {
return <Line sceneFunc={sceneFunc} {...props} />; return <Line sceneFunc={sceneFunc} {...props} />;
} }
export function Tick({ x, y, scale, onClick, cross }: { x: any, y: any, scale: any, onClick: any, cross: any}) { export function Tick({
x,
y,
scale,
onClick,
cross,
}: {
x: any;
y: any;
scale: any;
onClick: any;
cross: any;
}) {
const [fill, setFill] = useState("white"); const [fill, setFill] = useState("white");
function handleEnter() { function handleEnter() {
setFill("hsl(260, 100%, 80%)"); setFill("hsl(260, 100%, 80%)");
@ -145,10 +157,22 @@ export function Tick({ x, y, scale, onClick, cross }: { x: any, y: any, scale: a
} }
interface TrailPoint extends Vector2 { interface TrailPoint extends Vector2 {
lifetime: number lifetime: number;
} }
export function Trail({ position, size, duration, segments, color }: { position: Vector2, size: any, duration: number, segments: any, color: string }) { export function Trail({
position,
size,
duration,
segments,
color,
}: {
position: Vector2;
size: any;
duration: number;
segments: any;
color: string;
}) {
const trailRef: React.MutableRefObject<Konva.Line | undefined> = useRef(); const trailRef: React.MutableRefObject<Konva.Line | undefined> = useRef();
const pointsRef: React.MutableRefObject<TrailPoint[]> = useRef([]); const pointsRef: React.MutableRefObject<TrailPoint[]> = useRef([]);
const prevPositionRef = useRef(position); const prevPositionRef = useRef(position);
@ -259,7 +283,7 @@ export function Trail({ position, size, duration, segments, color }: { position:
// Create a radial gradient from the center of the trail to the tail // Create a radial gradient from the center of the trail to the tail
const gradientCenter = resampledPoints[resampledPoints.length - 1]; const gradientCenter = resampledPoints[resampledPoints.length - 1];
const gradientEnd = resampledPoints[0]; const gradientEnd = resampledPoints[0];
const gradientRadius = Vector2.setLength( const gradientRadius = Vector2.magnitude(
Vector2.subtract(gradientCenter, gradientEnd) Vector2.subtract(gradientCenter, gradientEnd)
); );
let gradient = context.createRadialGradient( let gradient = context.createRadialGradient(
@ -302,7 +326,9 @@ Trail.defaultProps = {
* @param {Konva.Node} node * @param {Konva.Node} node
* @returns {Vector2} * @returns {Vector2}
*/ */
export function getRelativePointerPosition(node: Konva.Node): { x: number, y: number } | undefined { export function getRelativePointerPosition(
node: Konva.Node
): { x: number; y: number } | undefined {
let transform = node.getAbsoluteTransform().copy(); let transform = node.getAbsoluteTransform().copy();
transform.invert(); transform.invert();
// TODO: handle possible null value // TODO: handle possible null value
@ -314,7 +340,9 @@ export function getRelativePointerPosition(node: Konva.Node): { x: number, y: nu
return transform.point(position); return transform.point(position);
} }
export function getRelativePointerPositionNormalized(node: Konva.Node): { x: number, y: number } | undefined { export function getRelativePointerPositionNormalized(
node: Konva.Node
): { x: number; y: number } | undefined {
const relativePosition = getRelativePointerPosition(node); const relativePosition = getRelativePointerPosition(node);
if (!relativePosition) { if (!relativePosition) {
// TODO: handle possible null value // TODO: handle possible null value