Added circle and rect outline options and optimised storage structure

This commit is contained in:
Mitchell McCaffrey 2021-04-25 17:10:32 +10:00
parent f7975c041a
commit 994b9b5ebb
4 changed files with 43 additions and 67 deletions

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react";
import { Image as KonvaImage, Group, Line } from "react-konva";
import { Image as KonvaImage, Group, Line, Rect, Circle } from "react-konva";
import { useSpring, animated } from "react-spring/konva";
import useImage from "use-image";
import Konva from "konva";
@ -246,6 +246,37 @@ function MapToken({
tokenName = tokenName + "-locked";
}
let outline = tokenState.outline;
if (Array.isArray(tokenState.outline)) {
outline = [...outline]; // Copy array so we can edit it imutably
for (let i = 0; i < outline.length; i += 2) {
// Scale outline to the token
outline[i] = (outline[i] / tokenState.width) * tokenWidth;
outline[i + 1] = (outline[i + 1] / tokenState.height) * tokenHeight;
}
}
function renderOutline() {
const sharedProps = {
fill: colors.black,
width: tokenWidth,
height: tokenHeight,
x: 0,
y: 0,
rotation: tokenState.rotation,
offsetX: tokenWidth / 2,
offsetY: tokenHeight / 2,
opacity: 0.8,
};
if (outline === "rect") {
return <Rect {...sharedProps} />;
} else if (outline === "circle") {
return <Circle {...sharedProps} offsetX={0} offsetY={0} />;
} else {
return <Line {...sharedProps} points={outline} closed tension={0.33} />;
}
}
return (
<animated.Group
{...props}
@ -268,24 +299,7 @@ function MapToken({
id={tokenState.id}
>
{!tokenImage ? (
<Group opacity={0.8}>
<Line
points={tokenState.outline
.map(({ x, y }) => [x * tokenWidth, y * tokenHeight])
.flat(1)}
fill={colors.black}
width={tokenWidth}
height={tokenHeight}
x={0}
y={0}
rotation={tokenState.rotation}
offsetX={tokenWidth / 2}
offsetY={tokenHeight / 2}
closed
// Round edges for more complex shapes
tension={tokenState.outline.length > 6 ? 0.333 : 0}
/>
</Group>
renderOutline()
) : (
<KonvaImage
ref={imageRef}

View File

@ -4,13 +4,11 @@ import "dexie-observable";
import shortid from "shortid";
import { v4 as uuid } from "uuid";
import Case from "case";
import imageOutline from "image-outline";
import blobToBuffer from "./helpers/blobToBuffer";
import { getGridDefaultInset } from "./helpers/grid";
import { convertOldActionsToShapes } from "./actions";
import { createThumbnail } from "./helpers/image";
import Vector2 from "./helpers/Vector2";
// Helper to create a thumbnail for a file in a db
async function createDataThumbnail(data) {
@ -571,23 +569,9 @@ const versions = {
delete token.category;
token.defaultLabel = "";
if (token.width === token.height) {
token.outline = [
{ x: 0.5, y: 0.0 },
{ x: 0.85, y: 0.15 },
{ x: 1.0, y: 0.5 },
{ x: 0.85, y: 0.85 },
{ x: 0.5, y: 1.0 },
{ x: 0.15, y: 0.85 },
{ x: 0.0, y: 0.5 },
{ x: 0.15, y: 0.15 },
];
token.outline = "circle";
} else {
token.outline = [
{ x: 0.0, y: 0.0 },
{ x: 1.0, y: 0.0 },
{ x: 1.0, y: 1.0 },
{ x: 0.0, y: 1.0 },
];
token.outline = "rect";
}
});
});
@ -612,12 +596,7 @@ const versions = {
tokenState.category = "character";
tokenState.type = "file";
tokenState.file = "";
tokenState.outline = [
{ x: 0.0, y: 0.0 },
{ x: 1.0, y: 0.0 },
{ x: 1.0, y: 1.0 },
{ x: 0.0, y: 1.0 },
];
tokenState.outline = "rect";
tokenState.width = 256;
tokenState.height = 256;
}
@ -625,16 +604,7 @@ const versions = {
tokenState.category = "character";
tokenState.type = "default";
tokenState.key = Case.camel(tokenState.tokenId.slice(10));
tokenState.outline = [
{ x: 0.5, y: 0.0 },
{ x: 0.85, y: 0.15 },
{ x: 1.0, y: 0.5 },
{ x: 0.85, y: 0.85 },
{ x: 0.5, y: 1.0 },
{ x: 0.15, y: 0.85 },
{ x: 0.0, y: 0.5 },
{ x: 0.15, y: 0.15 },
];
tokenState.outline = "circle";
tokenState.width = 256;
tokenState.height = 256;
}

View File

@ -179,13 +179,14 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
};
assets.push(fileAsset);
let outline = imageOutline(image).map(({ x, y }) => ({
x: x / image.width,
y: y / image.height,
}));
let outline = imageOutline(image);
if (outline.length > 100) {
outline = Vector2.resample(outline, 100);
}
// Flatten and round outline to save on storage size
outline = outline
.map(({ x, y }) => [Math.round(x), Math.round(y)])
.flat();
const token = {
name,

View File

@ -90,16 +90,7 @@ export const tokens = Object.keys(tokenSources).map((key) => ({
hideInSidebar: false,
width: 256,
height: 256,
outline: [
{ x: 0.5, y: 0.0 },
{ x: 0.85, y: 0.15 },
{ x: 1.0, y: 0.5 },
{ x: 0.85, y: 0.85 },
{ x: 0.5, y: 1.0 },
{ x: 0.15, y: 0.85 },
{ x: 0.0, y: 0.5 },
{ x: 0.15, y: 0.15 },
],
outline: "circle",
}));
export const unknownSource = unknown;