Added circle and rect outline options and optimised storage structure
This commit is contained in:
parent
f7975c041a
commit
994b9b5ebb
@ -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}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user