first overlay render
This commit is contained in:
parent
ddd692bcbd
commit
7a15fa556b
|
@ -1,129 +0,0 @@
|
||||||
/*
|
|
||||||
* Settings of minimap / overlay
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
|
||||||
import { useSelector, shallowEqual } from 'react-redux';
|
|
||||||
import { t } from 'ttag';
|
|
||||||
|
|
||||||
import { coordsFromUrl } from '../core/utils';
|
|
||||||
import templateLoader from '../ui/templateLoader';
|
|
||||||
|
|
||||||
const AddTemplate = ({ close, triggerClose: refClose }) => {
|
|
||||||
const [render, setRender] = useState(false);
|
|
||||||
const [coords, setCoords] = useState(null);
|
|
||||||
const [dimensions, setDimensions] = useState(null);
|
|
||||||
const [title, setTitle] = useState('');
|
|
||||||
const [file, setFile] = useState(null);
|
|
||||||
const imgRef = useRef();
|
|
||||||
const [
|
|
||||||
canvasId,
|
|
||||||
canvases,
|
|
||||||
] = useSelector((state) => [
|
|
||||||
state.canvas.canvasId,
|
|
||||||
state.canvas.canvases,
|
|
||||||
], shallowEqual);
|
|
||||||
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.setTimeout(() => setRender(true), 10);
|
|
||||||
refClose.current = () => setRender(false);
|
|
||||||
}, [refClose]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!file || !imgRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const fr = new FileReader();
|
|
||||||
fr.onload = () => { imgRef.current.src = fr.result; };
|
|
||||||
fr.readAsDataURL(file);
|
|
||||||
}, [file]);
|
|
||||||
|
|
||||||
const canSubmit = (imgRef.current && file && coords && title && dimensions);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="inarea"
|
|
||||||
style={{
|
|
||||||
opacity: render ? 1 : 0,
|
|
||||||
transition: 'opacity 200ms',
|
|
||||||
}}
|
|
||||||
onTransitionEnd={() => !render && close()}
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
onSubmit={async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (canSubmit) {
|
|
||||||
await templateLoader.addFile(
|
|
||||||
file, title, selectedCanvas, ...coords, imgRef.current,
|
|
||||||
);
|
|
||||||
setRender(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="./logo.svg"
|
|
||||||
alt="preview"
|
|
||||||
key="logo"
|
|
||||||
style={{
|
|
||||||
maxWidth: 96,
|
|
||||||
maxHeight: 96,
|
|
||||||
}}
|
|
||||||
onLoad={(evt) => setDimensions([
|
|
||||||
evt.target.naturalWidth,
|
|
||||||
evt.target.naturalHeight,
|
|
||||||
])}
|
|
||||||
onError={() => setDimensions(null)}
|
|
||||||
ref={imgRef}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
onChange={(evt) => {
|
|
||||||
setDimensions(null);
|
|
||||||
setFile(evt.target.files?.[0]);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
value={title}
|
|
||||||
type="text"
|
|
||||||
onChange={(evt) => setTitle(evt.target.value)}
|
|
||||||
placeholder={t`Template Name`}
|
|
||||||
/>
|
|
||||||
<select
|
|
||||||
value={selectedCanvas}
|
|
||||||
onChange={(e) => {
|
|
||||||
const sel = e.target;
|
|
||||||
selectCanvas(sel.options[sel.selectedIndex].value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.keys(canvases).filter((c) => !canvases[c].v).map((canvas) => (
|
|
||||||
<option key={canvas} value={canvas}>
|
|
||||||
{canvases[canvas].title}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
style={{
|
|
||||||
display: 'inline-block',
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '15em',
|
|
||||||
}}
|
|
||||||
placeholder="X_Y or URL"
|
|
||||||
onChange={(evt) => {
|
|
||||||
let co = evt.target.value.trim();
|
|
||||||
co = coordsFromUrl(co) || co;
|
|
||||||
evt.target.value = co;
|
|
||||||
const newCoords = co.split('_').map((z) => parseInt(z, 10));
|
|
||||||
setCoords((!newCoords.some(Number.isNaN) && newCoords.length === 2)
|
|
||||||
? newCoords : null,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button type="submit" disabled={!canSubmit}>{t`Save`}</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default React.memo(AddTemplate);
|
|
|
@ -127,11 +127,14 @@ const TemplateItemEdit = ({
|
||||||
selectCanvas(sel.options[sel.selectedIndex].value);
|
selectCanvas(sel.options[sel.selectedIndex].value);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Object.keys(canvases).filter((c) => !canvases[c].v).map((canvas) => (
|
{Object.keys(canvases)
|
||||||
|
.filter((c) => !canvases[c].v && !canvases[c].ed)
|
||||||
|
.map((canvas) => (
|
||||||
<option key={canvas} value={canvas}>
|
<option key={canvas} value={canvas}>
|
||||||
{canvases[canvas].title}
|
{canvases[canvas].title}
|
||||||
</option>
|
</option>
|
||||||
))}
|
),
|
||||||
|
)}
|
||||||
</select></span>
|
</select></span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { useSelector } from 'react-redux';
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
import { t } from 'ttag';
|
import { t } from 'ttag';
|
||||||
|
|
||||||
import AddTemplate from './AddTemplate';
|
|
||||||
import TemplateItem from './TemplateItem';
|
import TemplateItem from './TemplateItem';
|
||||||
import TemplateItemEdit from './TemplateItemEdit';
|
import TemplateItemEdit from './TemplateItemEdit';
|
||||||
import templateLoader from '../ui/templateLoader';
|
import templateLoader from '../ui/templateLoader';
|
||||||
|
|
|
@ -706,7 +706,7 @@ export function bufferToBase64(array) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function base64ToBuffer(base64) {
|
export async function base64ToBuffer(base64) {
|
||||||
const dataUrl = "data:application/octet-binary;base64," + base64;
|
const dataUrl = `data:application/octet-binary;base64,${base64}`;
|
||||||
const res = await fetch(dataUrl);
|
const res = await fetch(dataUrl);
|
||||||
return res.arrayBuffer();
|
return res.arrayBuffer();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
renderGrid,
|
renderGrid,
|
||||||
renderPlaceholder,
|
renderPlaceholder,
|
||||||
renderPotatoPlaceholder,
|
renderPotatoPlaceholder,
|
||||||
|
renderOverlay,
|
||||||
} from './render2Delements';
|
} from './render2Delements';
|
||||||
import PixelPainterControls from '../controls/PixelPainterControls';
|
import PixelPainterControls from '../controls/PixelPainterControls';
|
||||||
|
|
||||||
|
@ -272,6 +273,7 @@ class Renderer2D extends Renderer {
|
||||||
|
|
||||||
const [x, y] = getPixelFromChunkOffset(i, j, offset, canvasSize);
|
const [x, y] = getPixelFromChunkOffset(i, j, offset, canvasSize);
|
||||||
|
|
||||||
|
// TODO centerChunk is scaled!
|
||||||
const [canX, canY] = this.centerChunk
|
const [canX, canY] = this.centerChunk
|
||||||
.map((z) => (z + 0.5) * TILE_SIZE - canvasSize / 2);
|
.map((z) => (z + 0.5) * TILE_SIZE - canvasSize / 2);
|
||||||
const { width: canvasWidth, height: canvasHeight } = this.canvas;
|
const { width: canvasWidth, height: canvasHeight } = this.canvas;
|
||||||
|
@ -340,9 +342,7 @@ class Renderer2D extends Renderer {
|
||||||
viewport,
|
viewport,
|
||||||
viewscale: scale,
|
viewscale: scale,
|
||||||
} = this;
|
} = this;
|
||||||
const {
|
const { canvasSize } = state.canvas;
|
||||||
canvasSize,
|
|
||||||
} = state.canvas;
|
|
||||||
|
|
||||||
let { relScale } = this;
|
let { relScale } = this;
|
||||||
|
|
||||||
|
@ -422,6 +422,11 @@ class Renderer2D extends Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
|
// TODO conditions
|
||||||
|
renderOverlay(
|
||||||
|
this.canvas, chunkPosition, canvasSize, scale,
|
||||||
|
this.tiledScale, this.scaleThreshold,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import templateLoader from './templateLoader';
|
||||||
import { screenToWorld, worldToScreen } from '../core/utils';
|
import { screenToWorld, worldToScreen } from '../core/utils';
|
||||||
|
import { TILE_SIZE } from '../core/constants';
|
||||||
|
|
||||||
const PLACEHOLDER_SIZE = 1.2;
|
const PLACEHOLDER_SIZE = 1.2;
|
||||||
const PLACEHOLDER_BORDER = 1;
|
const PLACEHOLDER_BORDER = 1;
|
||||||
|
@ -100,3 +102,46 @@ export function renderGrid(
|
||||||
|
|
||||||
viewportCtx.globalAlpha = 1;
|
viewportCtx.globalAlpha = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Overlay draws onto offscreen canvas, so its doing weirder math
|
||||||
|
*/
|
||||||
|
export function renderOverlay(
|
||||||
|
$canvas,
|
||||||
|
centerChunk,
|
||||||
|
canvasSize,
|
||||||
|
scale,
|
||||||
|
tiledScale,
|
||||||
|
scaleThreshold,
|
||||||
|
) {
|
||||||
|
// world coordinates of center of center chunk
|
||||||
|
const [x, y] = centerChunk
|
||||||
|
.map((z) => z * TILE_SIZE / tiledScale
|
||||||
|
+ TILE_SIZE / 2 / tiledScale - canvasSize / 2);
|
||||||
|
const { width, height } = $canvas;
|
||||||
|
const horizontalRadius = width / 2 / scale;
|
||||||
|
const verticalRadius = height / 2 / scale;
|
||||||
|
const templates = templateLoader.getTemplatesInView(
|
||||||
|
x, y, horizontalRadius, verticalRadius,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!templates.length) return;
|
||||||
|
const context = $canvas.getContext('2d');
|
||||||
|
if (!context) return;
|
||||||
|
|
||||||
|
// if scale > scaleThreshold, then scaling happens in renderer
|
||||||
|
// instead of offscreen canvas
|
||||||
|
const offscreenScale = (scale > scaleThreshold) ? 1.0 : scale;
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.scale(offscreenScale, offscreenScale);
|
||||||
|
for (const template of templates) {
|
||||||
|
const image = templateLoader.getTemplateSync(template.imageId);
|
||||||
|
if (!image) continue;
|
||||||
|
context.drawImage(image,
|
||||||
|
template.x - x + width / 2 / offscreenScale,
|
||||||
|
template.y - y + height / 2 / offscreenScale,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
|
@ -50,11 +50,35 @@ class TemplateLoader {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
getTemplateSync(id) {
|
||||||
getTemplatesInView() {
|
if (!this.ready) {
|
||||||
this.#store.templates
|
return null;
|
||||||
|
}
|
||||||
|
const template = this.#templates.get(id);
|
||||||
|
if (template) {
|
||||||
|
return template.image;
|
||||||
|
}
|
||||||
|
// TODO some store action when available
|
||||||
|
this.loadExistingTemplate(id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemplatesInView(x, y, horizontalRadius, verticalRadius) {
|
||||||
|
const topX = x - horizontalRadius;
|
||||||
|
const topY = y - verticalRadius;
|
||||||
|
const bottomX = x + horizontalRadius;
|
||||||
|
const bottomY = y + verticalRadius;
|
||||||
|
|
||||||
|
const templates = [];
|
||||||
|
this.#store.getState().templates.list.forEach((template) => {
|
||||||
|
if (x < bottomX && y < bottomY
|
||||||
|
&& x + template.width > topX && y + template.height > topY
|
||||||
|
) {
|
||||||
|
templates.push(template);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return templates;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sync database to store,
|
* sync database to store,
|
||||||
|
@ -152,7 +176,7 @@ class TemplateLoader {
|
||||||
|
|
||||||
deleteTemplate(title) {
|
deleteTemplate(title) {
|
||||||
const { list } = this.#store.getState().templates;
|
const { list } = this.#store.getState().templates;
|
||||||
const tData = list.find((z) => z.title === title)
|
const tData = list.find((z) => z.title === title);
|
||||||
if (!tData) {
|
if (!tData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -197,7 +221,9 @@ class TemplateLoader {
|
||||||
const imageIdList = await this.#fileStorage.saveFile(fileList);
|
const imageIdList = await this.#fileStorage.saveFile(fileList);
|
||||||
const idsToDelete = [];
|
const idsToDelete = [];
|
||||||
for (let i = 0; i < tDataList.length; i += 1) {
|
for (let i = 0; i < tDataList.length; i += 1) {
|
||||||
const { x, y, width, height, canvasId, title } = tDataList[i];
|
const {
|
||||||
|
x, y, width, height, canvasId, title,
|
||||||
|
} = tDataList[i];
|
||||||
const imageId = imageIdList[i];
|
const imageId = imageIdList[i];
|
||||||
const existing = list.find((z) => z.title === title);
|
const existing = list.find((z) => z.title === title);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user