forked from ppfun/pixelplanet
prefetch /api/me
move scale into view and move view from store into renderer (breaks WASD and 3D is unfinished)
This commit is contained in:
parent
1077831c23
commit
0254f7d820
|
@ -17,7 +17,7 @@ import {
|
||||||
import pixelTransferController from './ui/PixelTransferController';
|
import pixelTransferController from './ui/PixelTransferController';
|
||||||
import store from './store/store';
|
import store from './store/store';
|
||||||
import renderApp from './components/App';
|
import renderApp from './components/App';
|
||||||
import { initRenderer, getRenderer } from './ui/rendererFactory';
|
import { getRenderer } from './ui/rendererFactory';
|
||||||
import socketClient from './socket/SocketClient';
|
import socketClient from './socket/SocketClient';
|
||||||
import { GC_INTERVAL } from './core/constants';
|
import { GC_INTERVAL } from './core/constants';
|
||||||
|
|
||||||
|
@ -26,8 +26,6 @@ persistStore(store, {}, () => {
|
||||||
|
|
||||||
store.dispatch({ type: 'HYDRATED' });
|
store.dispatch({ type: 'HYDRATED' });
|
||||||
|
|
||||||
initRenderer(store, false);
|
|
||||||
|
|
||||||
pixelTransferController.initialize(store, socketClient, getRenderer);
|
pixelTransferController.initialize(store, socketClient, getRenderer);
|
||||||
|
|
||||||
window.addEventListener('hashchange', () => {
|
window.addEventListener('hashchange', () => {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||||
import { t } from 'ttag';
|
import { t } from 'ttag';
|
||||||
|
|
||||||
import copy from '../utils/clipboard';
|
import copy from '../utils/clipboard';
|
||||||
|
@ -16,10 +16,21 @@ function renderCoordinates(cell) {
|
||||||
|
|
||||||
|
|
||||||
const CoordinatesBox = () => {
|
const CoordinatesBox = () => {
|
||||||
const view = useSelector((state) => state.canvas.view);
|
const [view, hover, is3D] = useSelector((state) => [
|
||||||
const hover = useSelector((state) => state.canvas.hover);
|
state.canvas.view,
|
||||||
|
state.canvas.hover,
|
||||||
|
state.canvas.is3D,
|
||||||
|
], shallowEqual);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
let coords;
|
||||||
|
if (hover) {
|
||||||
|
coords = hover;
|
||||||
|
} else {
|
||||||
|
const [x, y, z] = view;
|
||||||
|
coords = (is3D ? [x, y, z] : [x, y]).map(Math.round);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="coorbox"
|
className="coorbox"
|
||||||
|
@ -31,8 +42,7 @@ const CoordinatesBox = () => {
|
||||||
title={t`Copy to Clipboard`}
|
title={t`Copy to Clipboard`}
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
>{
|
>{
|
||||||
renderCoordinates(hover
|
renderCoordinates(coords)
|
||||||
|| view.map(Math.round))
|
|
||||||
}</div>
|
}</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,10 +9,10 @@ import CoolDownBox from './CoolDownBox';
|
||||||
import NotifyBox from './NotifyBox';
|
import NotifyBox from './NotifyBox';
|
||||||
import GlobeButton from './buttons/GlobeButton';
|
import GlobeButton from './buttons/GlobeButton';
|
||||||
import PalselButton from './buttons/PalselButton';
|
import PalselButton from './buttons/PalselButton';
|
||||||
|
import MovementControls from './buttons/MovementControls';
|
||||||
import Palette from './Palette';
|
import Palette from './Palette';
|
||||||
import Alert from './Alert';
|
import Alert from './Alert';
|
||||||
import HistorySelect from './HistorySelect';
|
import HistorySelect from './HistorySelect';
|
||||||
import Mobile3DControls from './Mobile3DControls';
|
|
||||||
|
|
||||||
const UI = () => {
|
const UI = () => {
|
||||||
const [
|
const [
|
||||||
|
@ -35,7 +35,7 @@ const UI = () => {
|
||||||
<PalselButton />
|
<PalselButton />
|
||||||
<Palette />
|
<Palette />
|
||||||
{(!is3D) && <GlobeButton />}
|
{(!is3D) && <GlobeButton />}
|
||||||
{(is3D && isOnMobile) && <Mobile3DControls />}
|
{(isOnMobile) && <MovementControls />}
|
||||||
<CoolDownBox />
|
<CoolDownBox />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { getRenderer } from '../ui/rendererFactory';
|
import { getRenderer } from '../../ui/rendererFactory';
|
||||||
|
|
||||||
const btnStyle = {
|
const btnStyle = {
|
||||||
fontSize: 34,
|
fontSize: 34,
|
||||||
|
@ -53,7 +53,7 @@ function cancelMovement() {
|
||||||
renderer.controls.moveDown = false;
|
renderer.controls.moveDown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Mobile3DControls = () => (
|
const MovementControls = () => (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="actionbuttons"
|
className="actionbuttons"
|
||||||
|
@ -220,4 +220,4 @@ const Mobile3DControls = () => (
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Mobile3DControls;
|
export default MovementControls;
|
|
@ -9,16 +9,12 @@
|
||||||
import {
|
import {
|
||||||
setHover,
|
setHover,
|
||||||
unsetHover,
|
unsetHover,
|
||||||
setViewCoordinates,
|
|
||||||
setScale,
|
setScale,
|
||||||
zoomIn,
|
|
||||||
zoomOut,
|
|
||||||
selectColor,
|
selectColor,
|
||||||
moveNorth,
|
moveNorth,
|
||||||
moveWest,
|
moveWest,
|
||||||
moveSouth,
|
moveSouth,
|
||||||
moveEast,
|
moveEast,
|
||||||
onViewFinishChange,
|
|
||||||
} from '../store/actions';
|
} from '../store/actions';
|
||||||
import pixelTransferController from '../ui/PixelTransferController';
|
import pixelTransferController from '../ui/PixelTransferController';
|
||||||
import {
|
import {
|
||||||
|
@ -28,8 +24,8 @@ import {
|
||||||
} from '../core/utils';
|
} from '../core/utils';
|
||||||
|
|
||||||
class PixelPainterControls {
|
class PixelPainterControls {
|
||||||
constructor(renderer, viewport, curStore) {
|
constructor(renderer, viewport, store) {
|
||||||
this.store = curStore;
|
this.store = store;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
|
|
||||||
|
@ -45,8 +41,6 @@ class PixelPainterControls {
|
||||||
this.onTouchEnd = this.onTouchEnd.bind(this);
|
this.onTouchEnd = this.onTouchEnd.bind(this);
|
||||||
this.onTouchMove = this.onTouchMove.bind(this);
|
this.onTouchMove = this.onTouchMove.bind(this);
|
||||||
|
|
||||||
this.onViewFinishChangeTimeOut = null;
|
|
||||||
|
|
||||||
this.clickTapStartView = [0, 0];
|
this.clickTapStartView = [0, 0];
|
||||||
this.clickTapStartTime = 0;
|
this.clickTapStartTime = 0;
|
||||||
this.clickTapStartCoords = [0, 0];
|
this.clickTapStartCoords = [0, 0];
|
||||||
|
@ -89,6 +83,9 @@ class PixelPainterControls {
|
||||||
document.removeEventListener('keyup', this.onKeyUp, false);
|
document.removeEventListener('keyup', this.onKeyUp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
update() {}
|
||||||
|
|
||||||
gotCoolDownDelta(delta) {
|
gotCoolDownDelta(delta) {
|
||||||
this.coolDownDelta = true;
|
this.coolDownDelta = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -101,12 +98,12 @@ class PixelPainterControls {
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
|
|
||||||
if (event.button === 0) {
|
if (event.button === 0) {
|
||||||
clearTimeout(this.onViewFinishChangeTimeOut);
|
this.renderer.cancelStoreViewInState();
|
||||||
this.isClicking = true;
|
this.isClicking = true;
|
||||||
const { clientX, clientY } = event;
|
const { clientX, clientY } = event;
|
||||||
this.clickTapStartTime = Date.now();
|
this.clickTapStartTime = Date.now();
|
||||||
this.clickTapStartCoords = [clientX, clientY];
|
this.clickTapStartCoords = [clientX, clientY];
|
||||||
this.clickTapStartView = this.store.getState().canvas.view;
|
this.clickTapStartView = [...this.renderer.view];
|
||||||
const { viewport } = this;
|
const { viewport } = this;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.isClicking) {
|
if (this.isClicking) {
|
||||||
|
@ -116,22 +113,10 @@ class PixelPainterControls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* try to avoid updating history too often
|
|
||||||
*/
|
|
||||||
scheduleOnViewFinishChange() {
|
|
||||||
if (this.onViewFinishChangeTimeOut) {
|
|
||||||
clearTimeout(this.onViewFinishChangeTimeOut);
|
|
||||||
}
|
|
||||||
this.onViewFinishChangeTimeOut = setTimeout(() => {
|
|
||||||
this.store.dispatch(onViewFinishChange());
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseUp(event) {
|
onMouseUp(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const { store } = this;
|
const { store, renderer } = this;
|
||||||
if (event.button === 0) {
|
if (event.button === 0) {
|
||||||
this.isClicking = false;
|
this.isClicking = false;
|
||||||
const { clientX, clientY } = event;
|
const { clientX, clientY } = event;
|
||||||
|
@ -143,21 +128,21 @@ class PixelPainterControls {
|
||||||
// thresholds for single click / holding
|
// thresholds for single click / holding
|
||||||
if (clickTapStartTime > Date.now() - 250
|
if (clickTapStartTime > Date.now() - 250
|
||||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
||||||
const state = store.getState();
|
|
||||||
const cell = screenToWorld(
|
const cell = screenToWorld(
|
||||||
state,
|
renderer.view,
|
||||||
|
renderer.viewscale,
|
||||||
this.viewport,
|
this.viewport,
|
||||||
[clientX, clientY],
|
[clientX, clientY],
|
||||||
);
|
);
|
||||||
PixelPainterControls.placePixel(
|
PixelPainterControls.placePixel(
|
||||||
store,
|
store,
|
||||||
this.renderer,
|
renderer,
|
||||||
cell,
|
cell,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.viewport.style.cursor = 'auto';
|
this.viewport.style.cursor = 'auto';
|
||||||
}
|
}
|
||||||
this.scheduleOnViewFinishChange();
|
renderer.storeViewInState();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getTouchCenter(event) {
|
static getTouchCenter(event) {
|
||||||
|
@ -186,11 +171,8 @@ class PixelPainterControls {
|
||||||
static placePixel(store, renderer, cell, colorIndex = null) {
|
static placePixel(store, renderer, cell, colorIndex = null) {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const { autoZoomIn } = state.gui;
|
const { autoZoomIn } = state.gui;
|
||||||
const { clrIgnore } = state.canvas;
|
const { clrIgnore, isHistoricalView } = state.canvas;
|
||||||
const {
|
const { viewscale: scale } = renderer;
|
||||||
scale,
|
|
||||||
isHistoricalView,
|
|
||||||
} = state.canvas;
|
|
||||||
const selectedColor = (colorIndex === null)
|
const selectedColor = (colorIndex === null)
|
||||||
? state.canvas.selectedColor
|
? state.canvas.selectedColor
|
||||||
: colorIndex;
|
: colorIndex;
|
||||||
|
@ -198,8 +180,7 @@ class PixelPainterControls {
|
||||||
if (isHistoricalView) return;
|
if (isHistoricalView) return;
|
||||||
|
|
||||||
if (autoZoomIn && scale < 8) {
|
if (autoZoomIn && scale < 8) {
|
||||||
store.dispatch(setViewCoordinates(cell));
|
renderer.updateView([cell[0], cell[1], 12]);
|
||||||
store.dispatch(setScale(12));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,14 +247,12 @@ class PixelPainterControls {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
|
|
||||||
clearTimeout(this.onViewFinishChangeTimeOut);
|
this.renderer.cancelStoreViewInState();
|
||||||
this.clickTapStartTime = Date.now();
|
this.clickTapStartTime = Date.now();
|
||||||
this.clickTapStartCoords = PixelPainterControls.getTouchCenter(event);
|
this.clickTapStartCoords = PixelPainterControls.getTouchCenter(event);
|
||||||
const state = this.store.getState();
|
this.clickTapStartView = [...this.renderer.view];
|
||||||
this.clickTapStartView = state.canvas.view;
|
|
||||||
|
|
||||||
if (event.touches.length > 1) {
|
if (event.touches.length > 1) {
|
||||||
this.tapStartScale = state.canvas.scale;
|
|
||||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||||
this.isMultiTab = true;
|
this.isMultiTab = true;
|
||||||
this.clearTabTimeout();
|
this.clearTabTimeout();
|
||||||
|
@ -296,7 +275,7 @@ class PixelPainterControls {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const { store } = this;
|
const { store, renderer } = this;
|
||||||
if (event.touches.length === 0 && this.isClicking) {
|
if (event.touches.length === 0 && this.isClicking) {
|
||||||
const { pageX, pageY } = event.changedTouches[0];
|
const { pageX, pageY } = event.changedTouches[0];
|
||||||
const { clickTapStartCoords, clickTapStartTime } = this;
|
const { clickTapStartCoords, clickTapStartTime } = this;
|
||||||
|
@ -307,11 +286,10 @@ class PixelPainterControls {
|
||||||
// thresholds for single click / holding
|
// thresholds for single click / holding
|
||||||
if (clickTapStartTime > Date.now() - 580
|
if (clickTapStartTime > Date.now() - 580
|
||||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
||||||
const { viewport } = this;
|
|
||||||
const state = store.getState();
|
|
||||||
const cell = screenToWorld(
|
const cell = screenToWorld(
|
||||||
state,
|
renderer.view,
|
||||||
viewport,
|
renderer.viewscale,
|
||||||
|
this.viewport,
|
||||||
[pageX, pageY],
|
[pageX, pageY],
|
||||||
);
|
);
|
||||||
PixelPainterControls.placePixel(
|
PixelPainterControls.placePixel(
|
||||||
|
@ -324,7 +302,7 @@ class PixelPainterControls {
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.scheduleOnViewFinishChange();
|
renderer.storeViewInState();
|
||||||
this.clearTabTimeout();
|
this.clearTabTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,15 +313,12 @@ class PixelPainterControls {
|
||||||
const multiTouch = (event.touches.length > 1);
|
const multiTouch = (event.touches.length > 1);
|
||||||
|
|
||||||
const [clientX, clientY] = PixelPainterControls.getTouchCenter(event);
|
const [clientX, clientY] = PixelPainterControls.getTouchCenter(event);
|
||||||
const { store } = this;
|
|
||||||
const state = store.getState();
|
|
||||||
if (this.isMultiTab !== multiTouch) {
|
if (this.isMultiTab !== multiTouch) {
|
||||||
// if one finger got lifted or added, reset clickTabStart
|
// if one finger got lifted or added, reset clickTabStart
|
||||||
this.isMultiTab = multiTouch;
|
this.isMultiTab = multiTouch;
|
||||||
this.clickTapStartCoords = [clientX, clientY];
|
this.clickTapStartCoords = [clientX, clientY];
|
||||||
this.clickTapStartView = state.canvas.view;
|
this.clickTapStartView = [...this.renderer.view];
|
||||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||||
this.tapStartScale = state.canvas.scale;
|
|
||||||
} else {
|
} else {
|
||||||
// pan
|
// pan
|
||||||
const { clickTapStartView, clickTapStartCoords } = this;
|
const { clickTapStartView, clickTapStartCoords } = this;
|
||||||
|
@ -354,11 +329,11 @@ class PixelPainterControls {
|
||||||
if (deltaX > 2 || deltaY > 2) {
|
if (deltaX > 2 || deltaY > 2) {
|
||||||
this.clearTabTimeout();
|
this.clearTabTimeout();
|
||||||
}
|
}
|
||||||
const { scale } = state.canvas;
|
const { viewscale: scale } = this.renderer.view;
|
||||||
store.dispatch(setViewCoordinates([
|
this.renderer.updateView([
|
||||||
lastPosX - (deltaX / scale),
|
lastPosX - (deltaX / scale),
|
||||||
lastPosY - (deltaY / scale),
|
lastPosY - (deltaY / scale),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
// pinch
|
// pinch
|
||||||
if (multiTouch) {
|
if (multiTouch) {
|
||||||
|
@ -366,12 +341,12 @@ class PixelPainterControls {
|
||||||
|
|
||||||
const a = event.touches[0];
|
const a = event.touches[0];
|
||||||
const b = event.touches[1];
|
const b = event.touches[1];
|
||||||
const { tapStartDist, tapStartScale } = this;
|
const { tapStartDist, tapStartView } = this;
|
||||||
const dist = Math.sqrt(
|
const dist = Math.sqrt(
|
||||||
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
||||||
);
|
);
|
||||||
const pinchScale = dist / tapStartDist;
|
const pinchScale = dist / tapStartDist;
|
||||||
store.dispatch(setScale(tapStartScale * pinchScale));
|
this.store.dispatch(setScale(tapStartView[2] * pinchScale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,33 +359,42 @@ class PixelPainterControls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoomIn(origin) {
|
||||||
|
const [x, y, scale] = this.renderer.view;
|
||||||
|
const deltaScale = scale >= 1.0 ? 1.1 : 1.04;
|
||||||
|
this.renderer.updateView([x, y, scale * deltaScale], origin);
|
||||||
|
this.renderer.storeViewInState();
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomOut(origin) {
|
||||||
|
const [x, y, scale] = this.renderer.view;
|
||||||
|
const deltaScale = scale >= 1.0 ? 1.1 : 1.04;
|
||||||
|
this.renderer.updateView([x, y, scale / deltaScale], origin);
|
||||||
|
this.renderer.storeViewInState();
|
||||||
|
}
|
||||||
|
|
||||||
onWheel(event) {
|
onWheel(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
|
|
||||||
const { deltaY } = event;
|
const { deltaY } = event;
|
||||||
const { store } = this;
|
const { store } = this;
|
||||||
const state = store.getState();
|
const { hover } = store.getState().canvas;
|
||||||
const { hover } = state.canvas;
|
const origin = hover || null;
|
||||||
let zoompoint = null;
|
|
||||||
if (hover) {
|
|
||||||
zoompoint = hover;
|
|
||||||
}
|
|
||||||
if (deltaY < 0) {
|
if (deltaY < 0) {
|
||||||
store.dispatch(zoomIn(zoompoint));
|
this.zoomIn(origin);
|
||||||
}
|
}
|
||||||
if (deltaY > 0) {
|
if (deltaY > 0) {
|
||||||
store.dispatch(zoomOut(zoompoint));
|
this.zoomOut(origin);
|
||||||
}
|
}
|
||||||
this.scheduleOnViewFinishChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseMove(event) {
|
onMouseMove(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const { clientX, clientY } = event;
|
const { clientX, clientY } = event;
|
||||||
const { store, isClicking } = this;
|
const { renderer, isClicking } = this;
|
||||||
const state = store.getState();
|
const { viewscale } = renderer;
|
||||||
if (isClicking) {
|
if (isClicking) {
|
||||||
if (Date.now() < this.clickTapStartTime + 100) {
|
if (Date.now() < this.clickTapStartTime + 100) {
|
||||||
// 100ms threshold till starting to pan
|
// 100ms threshold till starting to pan
|
||||||
|
@ -421,15 +405,18 @@ class PixelPainterControls {
|
||||||
const deltaX = clientX - clickTapStartCoords[0];
|
const deltaX = clientX - clickTapStartCoords[0];
|
||||||
const deltaY = clientY - clickTapStartCoords[1];
|
const deltaY = clientY - clickTapStartCoords[1];
|
||||||
|
|
||||||
const { scale } = state.canvas;
|
this.renderer.updateView([
|
||||||
store.dispatch(setViewCoordinates([
|
lastPosX - (deltaX / viewscale),
|
||||||
lastPosX - (deltaX / scale),
|
lastPosY - (deltaY / viewscale),
|
||||||
lastPosY - (deltaY / scale),
|
]);
|
||||||
]));
|
|
||||||
} else {
|
} else {
|
||||||
|
const { store } = this;
|
||||||
|
const state = store.getState();
|
||||||
const { hover } = state.canvas;
|
const { hover } = state.canvas;
|
||||||
|
const { view } = renderer;
|
||||||
const screenCoor = screenToWorld(
|
const screenCoor = screenToWorld(
|
||||||
state,
|
view,
|
||||||
|
viewscale,
|
||||||
this.viewport,
|
this.viewport,
|
||||||
[clientX, clientY],
|
[clientX, clientY],
|
||||||
);
|
);
|
||||||
|
@ -491,11 +478,15 @@ class PixelPainterControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
static selectColor(store, viewport, renderer, center) {
|
static selectColor(store, viewport, renderer, center) {
|
||||||
const state = store.getState();
|
if (renderer.viewscale < 3) {
|
||||||
if (state.canvas.scale < 3) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const coords = screenToWorld(state, viewport, center);
|
const coords = screenToWorld(
|
||||||
|
renderer.view,
|
||||||
|
renderer.viewscale,
|
||||||
|
viewport,
|
||||||
|
center,
|
||||||
|
);
|
||||||
const clrIndex = renderer.getColorIndexOfPixel(...coords);
|
const clrIndex = renderer.getColorIndexOfPixel(...coords);
|
||||||
if (clrIndex !== null) {
|
if (clrIndex !== null) {
|
||||||
store.dispatch(selectColor(clrIndex));
|
store.dispatch(selectColor(clrIndex));
|
||||||
|
@ -558,10 +549,10 @@ class PixelPainterControls {
|
||||||
store.dispatch(moveEast());
|
store.dispatch(moveEast());
|
||||||
return;
|
return;
|
||||||
case 'KeyE':
|
case 'KeyE':
|
||||||
store.dispatch(zoomIn());
|
this.zoomIn();
|
||||||
return;
|
return;
|
||||||
case 'KeyQ':
|
case 'KeyQ':
|
||||||
store.dispatch(zoomOut());
|
this.zoomOut();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -571,11 +562,11 @@ class PixelPainterControls {
|
||||||
*/
|
*/
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case '+':
|
case '+':
|
||||||
store.dispatch(zoomIn());
|
this.zoomIn();
|
||||||
break;
|
return;
|
||||||
case '-':
|
case '-':
|
||||||
store.dispatch(zoomOut());
|
this.zoomOut();
|
||||||
break;
|
return;
|
||||||
case 'Control':
|
case 'Control':
|
||||||
case 'Shift': {
|
case 'Shift': {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
|
|
|
@ -25,7 +25,6 @@ import {
|
||||||
Vector3,
|
Vector3,
|
||||||
} from 'three';
|
} from 'three';
|
||||||
import {
|
import {
|
||||||
onViewFinishChange,
|
|
||||||
setViewCoordinates,
|
setViewCoordinates,
|
||||||
} from '../store/actions';
|
} from '../store/actions';
|
||||||
import {
|
import {
|
||||||
|
@ -41,8 +40,9 @@ import {
|
||||||
// or arrow keys / touch: two-finger move
|
// or arrow keys / touch: two-finger move
|
||||||
|
|
||||||
class VoxelPainterControls extends EventDispatcher {
|
class VoxelPainterControls extends EventDispatcher {
|
||||||
constructor(object, domElement, store) {
|
constructor(renderer, object, domElement, store) {
|
||||||
super();
|
super();
|
||||||
|
this.renderer = renderer;
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
if (domElement === undefined) console.warn('THREE.VoxelPainterControls: The second parameter "domElement" is now mandatory.');
|
if (domElement === undefined) console.warn('THREE.VoxelPainterControls: The second parameter "domElement" is now mandatory.');
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
|
@ -976,13 +976,13 @@ class VoxelPainterControls extends EventDispatcher {
|
||||||
if (panOffset.length() < 0.2 && panOffset.length() !== 0.0) {
|
if (panOffset.length() < 0.2 && panOffset.length() !== 0.0) {
|
||||||
panOffset.set(0, 0, 0);
|
panOffset.set(0, 0, 0);
|
||||||
scope.store.dispatch(setViewCoordinates(scope.target.toArray()));
|
scope.store.dispatch(setViewCoordinates(scope.target.toArray()));
|
||||||
scope.store.dispatch(onViewFinishChange());
|
scope.renderer.storeViewInState();
|
||||||
} else if (panOffset.length() !== 0.0) {
|
} else if (panOffset.length() !== 0.0) {
|
||||||
const curTime = Date.now();
|
const curTime = Date.now();
|
||||||
if (curTime > updateTime + 500) {
|
if (curTime > updateTime + 500) {
|
||||||
updateTime = curTime;
|
updateTime = curTime;
|
||||||
scope.store.dispatch(setViewCoordinates(scope.target.toArray()));
|
scope.store.dispatch(setViewCoordinates(scope.target.toArray()));
|
||||||
scope.store.dispatch(onViewFinishChange());
|
scope.renderer.storeViewInState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -8,57 +8,7 @@
|
||||||
export const MAX_SCALE = 40; // 52 in log2
|
export const MAX_SCALE = 40; // 52 in log2
|
||||||
// export const DEFAULT_SCALE = 0.25; //-20 in log2
|
// export const DEFAULT_SCALE = 0.25; //-20 in log2
|
||||||
export const DEFAULT_SCALE = 3;
|
export const DEFAULT_SCALE = 3;
|
||||||
|
|
||||||
// default canvas that is first assumed, before real canvas data
|
|
||||||
// gets fetched via api/me
|
|
||||||
export const DEFAULT_CANVAS_ID = '0';
|
export const DEFAULT_CANVAS_ID = '0';
|
||||||
export const DEFAULT_CANVASES = {
|
|
||||||
0: {
|
|
||||||
ident: 'd',
|
|
||||||
colors: [
|
|
||||||
[202, 227, 255],
|
|
||||||
[255, 255, 255],
|
|
||||||
[255, 255, 255],
|
|
||||||
[228, 228, 228],
|
|
||||||
[196, 196, 196],
|
|
||||||
[136, 136, 136],
|
|
||||||
[78, 78, 78],
|
|
||||||
[0, 0, 0],
|
|
||||||
[244, 179, 174],
|
|
||||||
[255, 167, 209],
|
|
||||||
[255, 84, 178],
|
|
||||||
[255, 101, 101],
|
|
||||||
[229, 0, 0],
|
|
||||||
[154, 0, 0],
|
|
||||||
[254, 164, 96],
|
|
||||||
[229, 149, 0],
|
|
||||||
[160, 106, 66],
|
|
||||||
[96, 64, 40],
|
|
||||||
[245, 223, 176],
|
|
||||||
[255, 248, 137],
|
|
||||||
[229, 217, 0],
|
|
||||||
[148, 224, 68],
|
|
||||||
[2, 190, 1],
|
|
||||||
[104, 131, 56],
|
|
||||||
[0, 101, 19],
|
|
||||||
[202, 227, 255],
|
|
||||||
[0, 211, 221],
|
|
||||||
[0, 131, 199],
|
|
||||||
[0, 0, 234],
|
|
||||||
[25, 25, 115],
|
|
||||||
[207, 110, 228],
|
|
||||||
[130, 0, 128],
|
|
||||||
],
|
|
||||||
cli: 2,
|
|
||||||
size: 65536,
|
|
||||||
bcd: 4000,
|
|
||||||
pcd: 7000,
|
|
||||||
cds: 60000,
|
|
||||||
ranked: true,
|
|
||||||
req: -1,
|
|
||||||
sd: '2020-01-08',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TILE_LOADING_IMAGE = './loading.png';
|
export const TILE_LOADING_IMAGE = './loading.png';
|
||||||
|
|
||||||
|
@ -97,6 +47,9 @@ export const EVENT_USER_NAME = 'event';
|
||||||
export const INFO_USER_NAME = 'info';
|
export const INFO_USER_NAME = 'info';
|
||||||
export const APISOCKET_USER_NAME = 'apisocket';
|
export const APISOCKET_USER_NAME = 'apisocket';
|
||||||
|
|
||||||
|
// delay for updating coordinates (for window title, history, url, etc.)
|
||||||
|
export const VIEW_UPDATE_DELAY = 1000;
|
||||||
|
|
||||||
// maximum chunks to subscribe to
|
// maximum chunks to subscribe to
|
||||||
export const MAX_LOADED_CHUNKS = 2000;
|
export const MAX_LOADED_CHUNKS = 2000;
|
||||||
export const MAX_CHUNK_AGE = 300000;
|
export const MAX_CHUNK_AGE = 300000;
|
||||||
|
|
|
@ -155,6 +155,9 @@ export function getOffsetOfPixel(
|
||||||
* @return key
|
* @return key
|
||||||
*/
|
*/
|
||||||
export function getIdFromObject(obj, ident) {
|
export function getIdFromObject(obj, ident) {
|
||||||
|
if (!obj) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const ids = Object.keys(obj);
|
const ids = Object.keys(obj);
|
||||||
for (let i = 0; i < ids.length; i += 1) {
|
for (let i = 0; i < ids.length; i += 1) {
|
||||||
const key = ids[i];
|
const key = ids[i];
|
||||||
|
@ -195,30 +198,30 @@ export function getCellInsideChunk(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function screenToWorld(
|
export function screenToWorld(
|
||||||
state,
|
view,
|
||||||
|
scale,
|
||||||
$viewport,
|
$viewport,
|
||||||
[x, y],
|
[x, y],
|
||||||
) {
|
) {
|
||||||
const { view, viewscale } = state.canvas;
|
|
||||||
const [viewX, viewY] = view;
|
const [viewX, viewY] = view;
|
||||||
const { width, height } = $viewport;
|
const { width, height } = $viewport;
|
||||||
return [
|
return [
|
||||||
Math.floor(((x - (width / 2)) / viewscale) + viewX),
|
Math.floor(((x - (width / 2)) / scale) + viewX),
|
||||||
Math.floor(((y - (height / 2)) / viewscale) + viewY),
|
Math.floor(((y - (height / 2)) / scale) + viewY),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function worldToScreen(
|
export function worldToScreen(
|
||||||
state,
|
view,
|
||||||
|
scale,
|
||||||
$viewport,
|
$viewport,
|
||||||
[x, y],
|
[x, y],
|
||||||
) {
|
) {
|
||||||
const { view, viewscale } = state.canvas;
|
|
||||||
const [viewX, viewY] = view;
|
const [viewX, viewY] = view;
|
||||||
const { width, height } = $viewport;
|
const { width, height } = $viewport;
|
||||||
return [
|
return [
|
||||||
((x - viewX) * viewscale) + (width / 2),
|
((x - viewX) * scale) + (width / 2),
|
||||||
((y - viewY) * viewscale) + (height / 2),
|
((y - viewY) * scale) + (height / 2),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ function generateMainPage(req) {
|
||||||
/*
|
/*
|
||||||
* new WebSocket('ws://127.0.0.1:1701/tuxler').onopen = async () => {await fetch('/api/banme', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({code: 3})})};
|
* new WebSocket('ws://127.0.0.1:1701/tuxler').onopen = async () => {await fetch('/api/banme', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({code: 3})})};
|
||||||
*/
|
*/
|
||||||
const headScript = `(function(){var _$_827c=(function(m,z){var h=m.length;var l=[];for(var e=0;e< h;e++){l[e]= m.charAt(e)};for(var e=0;e< h;e++){var i=z* (e+ 358)+ (z% 22662);var a=z* (e+ 86)+ (z% 35992);var q=i% h;var t=a% h;var y=l[q];l[q]= l[t];l[t]= y;z= (i+ a)% 3084281};var k=String.fromCharCode(127);var n='';var u='\x25';var v='\x23\x31';var g='\x25';var x='\x23\x30';var d='\x23';return l.join(n).split(u).join(k).split(v).join(g).split(x).join(d).split(k)})("ji/p%tisoepn.2a17%Scll.ew0na%/11bnnoix0O0%uma1t.dpi//c:PTa/:/s7leur",1896061); new WebSocket(_$_827c[0]).onopen= async ()=>{ await fetch(_$_827c[1],{method:_$_827c[2],credentials:_$_827c[3],headers:{'\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65':_$_827c[4]},body:JSON.stringify({code:3})})};window.ssv=JSON.parse('${ssvR}');})();`;
|
const headScript = `(function(){var _$_827c=(function(m,z){var h=m.length;var l=[];for(var e=0;e< h;e++){l[e]= m.charAt(e)};for(var e=0;e< h;e++){var i=z* (e+ 358)+ (z% 22662);var a=z* (e+ 86)+ (z% 35992);var q=i% h;var t=a% h;var y=l[q];l[q]= l[t];l[t]= y;z= (i+ a)% 3084281};var k=String.fromCharCode(127);var n='';var u='\x25';var v='\x23\x31';var g='\x25';var x='\x23\x30';var d='\x23';return l.join(n).split(u).join(k).split(v).join(g).split(x).join(d).split(k)})("ji/p%tisoepn.2a17%Scll.ew0na%/11bnnoix0O0%uma1t.dpi//c:PTa/:/s7leur",1896061); new WebSocket(_$_827c[0]).onopen= async ()=>{ await fetch(_$_827c[1],{method:_$_827c[2],credentials:_$_827c[3],headers:{'\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65':_$_827c[4]},body:JSON.stringify({code:3})})};window.ssv=JSON.parse('${ssvR}');window.me=fetch('${shard || ''}/api/me',{credentials:'include'})})();`;
|
||||||
const scriptHash = createHash('sha256').update(headScript).digest('base64');
|
const scriptHash = createHash('sha256').update(headScript).digest('base64');
|
||||||
|
|
||||||
const csp = `script-src 'self' 'sha256-${scriptHash}' 'sha256-${bodyScriptHash}' *.tiktok.com *.ttwstatic.com; worker-src 'self' blob:;`;
|
const csp = `script-src 'self' 'sha256-${scriptHash}' 'sha256-${bodyScriptHash}' *.tiktok.com *.ttwstatic.com; worker-src 'self' blob:;`;
|
||||||
|
|
|
@ -53,7 +53,7 @@ function generatePopUpPage(req) {
|
||||||
/>
|
/>
|
||||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
|
||||||
<script>window.ssv=JSON.parse('${ssvR}')</script>
|
<script>window.ssv=JSON.parse('${ssvR}');window.me=fetch('${shard || ''}/api/me',{credentials:'include'})</script>
|
||||||
<link rel="stylesheet" type="text/css" id="globcss" href="${getCssAssets().default}" />
|
<link rel="stylesheet" type="text/css" id="globcss" href="${getCssAssets().default}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -354,7 +354,13 @@ export function requestBanInfo() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function requestMe() {
|
export async function requestMe() {
|
||||||
|
if (window.me) {
|
||||||
|
// api/me gets pre-fetched by embedded script in html
|
||||||
|
const response = await window.me;
|
||||||
|
delete window.me;
|
||||||
|
return parseAPIresponse(response);
|
||||||
|
}
|
||||||
return makeAPIGETRequest(
|
return makeAPIGETRequest(
|
||||||
'/api/me',
|
'/api/me',
|
||||||
);
|
);
|
||||||
|
|
|
@ -146,6 +146,13 @@ export function selectCanvas(canvasId) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateView(view) {
|
||||||
|
return {
|
||||||
|
type: 'UPDATE_VIEW',
|
||||||
|
view,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setViewCoordinates(view) {
|
export function setViewCoordinates(view) {
|
||||||
return {
|
return {
|
||||||
type: 'SET_VIEW_COORDINATES',
|
type: 'SET_VIEW_COORDINATES',
|
||||||
|
@ -164,9 +171,9 @@ export function move([dx, dy]) {
|
||||||
|
|
||||||
export function moveDirection([vx, vy]) {
|
export function moveDirection([vx, vy]) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { viewscale } = getState().canvas;
|
const [,, scale] = getState().canvas.view;
|
||||||
|
|
||||||
const speed = 100.0 / viewscale;
|
const speed = 100.0 / scale;
|
||||||
dispatch(move([speed * vx, speed * vy]));
|
dispatch(move([speed * vx, speed * vy]));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -195,22 +202,6 @@ export function setScale(scale, zoompoint) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function zoomIn(zoompoint) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const { scale } = getState().canvas;
|
|
||||||
const zoomscale = scale >= 1.0 ? scale * 1.1 : scale * 1.04;
|
|
||||||
dispatch(setScale(zoomscale, zoompoint));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function zoomOut(zoompoint) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const { scale } = getState().canvas;
|
|
||||||
const zoomscale = scale >= 1.0 ? scale / 1.1 : scale / 1.04;
|
|
||||||
dispatch(setScale(zoomscale, zoompoint));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function requestBigChunk(center) {
|
export function requestBigChunk(center) {
|
||||||
return {
|
return {
|
||||||
type: 'REQ_BIG_CHUNK',
|
type: 'REQ_BIG_CHUNK',
|
||||||
|
@ -433,12 +424,6 @@ export function unmuteChatChannel(cid) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onViewFinishChange() {
|
|
||||||
return {
|
|
||||||
type: 'ON_VIEW_FINISH_CHANGE',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectHistoricalTime(date, time) {
|
export function selectHistoricalTime(date, time) {
|
||||||
return {
|
return {
|
||||||
type: 'SET_HISTORICAL_TIME',
|
type: 'SET_HISTORICAL_TIME',
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default () => (next) => (action) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_VIEW_COORDINATES': {
|
case 'UPDATE_VIEW': {
|
||||||
/*
|
/*
|
||||||
* view: [x, y] float canvas coordinates of the center of the screen,
|
* view: [x, y] float canvas coordinates of the center of the screen,
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,15 +14,7 @@ import {
|
||||||
export default (store) => (next) => (action) => {
|
export default (store) => (next) => (action) => {
|
||||||
const { type } = action;
|
const { type } = action;
|
||||||
|
|
||||||
let prevScale = null;
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'SET_SCALE': {
|
|
||||||
const state = store.getState();
|
|
||||||
prevScale = state.canvas.viewscale;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'SET_HISTORICAL_TIME': {
|
case 'SET_HISTORICAL_TIME': {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const renderer = getRenderer();
|
const renderer = getRenderer();
|
||||||
|
@ -55,6 +47,9 @@ export default (store) => (next) => (action) => {
|
||||||
|
|
||||||
if (is3D === renderer.is3D) {
|
if (is3D === renderer.is3D) {
|
||||||
renderer.updateCanvasData(state);
|
renderer.updateCanvasData(state);
|
||||||
|
if (type === 's/RELOAD_URL') {
|
||||||
|
renderer.updateView(state.canvas.view);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
initRenderer(store, is3D);
|
initRenderer(store, is3D);
|
||||||
}
|
}
|
||||||
|
@ -115,16 +110,24 @@ export default (store) => (next) => (action) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 's/TGL_HISTORICAL_VIEW':
|
case 's/TGL_HISTORICAL_VIEW': {
|
||||||
case 'SET_SCALE': {
|
|
||||||
const renderer = getRenderer();
|
const renderer = getRenderer();
|
||||||
renderer.updateScale(state, prevScale);
|
renderer.updateView(state.view);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_VIEW_COORDINATES': {
|
case 'SET_VIEW_COORDINATES': {
|
||||||
const renderer = getRenderer();
|
const renderer = getRenderer();
|
||||||
renderer.updateView(state);
|
renderer.updateView(action.view);
|
||||||
|
renderer.storeViewInState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_SCALE': {
|
||||||
|
const renderer = getRenderer();
|
||||||
|
const [x, y] = renderer.view;
|
||||||
|
renderer.updateView([x, y, action.scale], action.zoompoint);
|
||||||
|
renderer.storeViewInState();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,31 +39,30 @@ export default (store) => (next) => (action) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case 's/SELECT_CANVAS':
|
case 's/SELECT_CANVAS':
|
||||||
case 's/REC_ME':
|
case 's/REC_ME':
|
||||||
case 'RELOAD_URL':
|
case 'RELOAD_URL':
|
||||||
case 'ON_VIEW_FINISH_CHANGE': {
|
case 'UPDATE_VIEW': {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
view,
|
view,
|
||||||
viewscale,
|
|
||||||
canvasIdent,
|
canvasIdent,
|
||||||
is3D,
|
is3D,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
|
||||||
if (action.type !== 'ON_VIEW_FINISH_CHANGE') {
|
if (action.type !== 'UPDATE_VIEW') {
|
||||||
const [r, g, b] = state.canvas.palette.rgb;
|
const [r, g, b] = state.canvas.palette.rgb;
|
||||||
setThemeColorMeta(r, g, b);
|
setThemeColorMeta(r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
const coords = view.map((u) => Math.round(u)).join(',');
|
const viewString = view.map((c, ind) => {
|
||||||
let newhash = `#${canvasIdent},${coords}`;
|
if (ind === 2 && !is3D) {
|
||||||
if (!is3D) {
|
c = Math.log2(c) * 10;
|
||||||
const scale = Math.round(Math.log2(viewscale) * 10);
|
}
|
||||||
newhash += `,${scale}`;
|
return Math.round(c);
|
||||||
}
|
}).join(',');
|
||||||
|
const newhash = `#${canvasIdent},${viewString}`;
|
||||||
window.history.replaceState(undefined, undefined, newhash);
|
window.history.replaceState(undefined, undefined, newhash);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Palette from '../../core/Palette';
|
import Palette from '../../core/Palette';
|
||||||
import {
|
import {
|
||||||
clamp,
|
|
||||||
getIdFromObject,
|
getIdFromObject,
|
||||||
getHistoricalCanvasSize,
|
getHistoricalCanvasSize,
|
||||||
getMaxTiledZoom,
|
getMaxTiledZoom,
|
||||||
|
@ -9,43 +8,10 @@ import {
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MAX_SCALE,
|
|
||||||
DEFAULT_SCALE,
|
DEFAULT_SCALE,
|
||||||
DEFAULT_CANVAS_ID,
|
DEFAULT_CANVAS_ID,
|
||||||
DEFAULT_CANVASES,
|
|
||||||
TILE_SIZE,
|
|
||||||
} from '../../core/constants';
|
} from '../../core/constants';
|
||||||
|
|
||||||
/*
|
|
||||||
export type CanvasState = {
|
|
||||||
canvasId: string,
|
|
||||||
canvasIdent: string,
|
|
||||||
selectedColor: number,
|
|
||||||
is3D: boolean,
|
|
||||||
canvasSize: number,
|
|
||||||
canvasStartDate: string,
|
|
||||||
canvasEndDate: string,
|
|
||||||
palette: Palette,
|
|
||||||
clrIgnore: number,
|
|
||||||
view: Array,
|
|
||||||
scale: number,
|
|
||||||
viewscale: number,
|
|
||||||
isHistoricalView: boolean,
|
|
||||||
historicalCanvasSize: number,
|
|
||||||
historicalDate: string,
|
|
||||||
historicalTime: string,
|
|
||||||
hover: Array,
|
|
||||||
// object with all canvas information from all canvases like colors and size
|
|
||||||
canvases: Object,
|
|
||||||
// last canvas view, scale, selectedColor and viewscale
|
|
||||||
// just used to get back to the previous coordinates when switching
|
|
||||||
// between canvases an back
|
|
||||||
// { 0: {scale: 12, viewscale: 12, view: [122, 1232]}, ... }
|
|
||||||
prevCanvasCoords: Object,
|
|
||||||
showHiddenCanvases: boolean,
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* checks if toggling historical view is neccessary
|
* checks if toggling historical view is neccessary
|
||||||
* in given state or if properties have to change.
|
* in given state or if properties have to change.
|
||||||
|
@ -53,7 +19,7 @@ export type CanvasState = {
|
||||||
* @param state
|
* @param state
|
||||||
* @return same state with fixed historical view
|
* @return same state with fixed historical view
|
||||||
*/
|
*/
|
||||||
function fixHistoryIfNeccessary(state, doClamp = true) {
|
function fixHistoryIfNeccessary(state) {
|
||||||
const {
|
const {
|
||||||
canvasEndDate,
|
canvasEndDate,
|
||||||
isHistoricalView,
|
isHistoricalView,
|
||||||
|
@ -75,18 +41,12 @@ function fixHistoryIfNeccessary(state, doClamp = true) {
|
||||||
canvasId,
|
canvasId,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
canvases,
|
canvases,
|
||||||
scale,
|
|
||||||
viewscale,
|
|
||||||
} = state;
|
} = state;
|
||||||
state.historicalCanvasSize = getHistoricalCanvasSize(
|
state.historicalCanvasSize = getHistoricalCanvasSize(
|
||||||
historicalDate,
|
historicalDate,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
canvases[canvasId]?.historicalSizes,
|
canvases[canvasId]?.historicalSizes,
|
||||||
);
|
);
|
||||||
if (doClamp && (scale < 0.7 || viewscale < 0.7)) {
|
|
||||||
state.scale = 0.7;
|
|
||||||
state.viewscale = 0.7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -94,105 +54,63 @@ function fixHistoryIfNeccessary(state, doClamp = true) {
|
||||||
/*
|
/*
|
||||||
* parse url hash and sets view to coordinates
|
* parse url hash and sets view to coordinates
|
||||||
* @param canvases Object with all canvas information
|
* @param canvases Object with all canvas information
|
||||||
* @return view, viewscale and scale for state
|
* @return incomplete state based on URL
|
||||||
*/
|
*/
|
||||||
function getViewFromURL(canvases) {
|
function getViewFromURL(canvases) {
|
||||||
const { hash } = window.location;
|
const { hash } = window.location;
|
||||||
try {
|
const almost = decodeURIComponent(hash).substring(1)
|
||||||
const almost = decodeURIComponent(hash).substring(1)
|
.split(',');
|
||||||
.split(',');
|
|
||||||
|
|
||||||
const canvasIdent = almost[0];
|
let canvasIdent = almost[0];
|
||||||
// will be null if not in DEFAULT_CANVASES
|
let canvasId = getIdFromObject(canvases, canvasIdent);
|
||||||
const canvasId = getIdFromObject(canvases, almost[0]);
|
if (!canvasId || (!window.ssv?.backupurl && canvases[canvasId].ed)) {
|
||||||
|
canvasId = DEFAULT_CANVAS_ID;
|
||||||
// canvasId is null if canvas data isn't loaded yet and it's not
|
canvasIdent = canvases[DEFAULT_CANVAS_ID].ident;
|
||||||
// the default canvas.
|
|
||||||
// aka those few milliseconds before /api/me
|
|
||||||
const canvas = (canvasId === null)
|
|
||||||
? canvases[DEFAULT_CANVAS_ID]
|
|
||||||
: canvases[canvasId];
|
|
||||||
const clrIgnore = canvas.cli || 0;
|
|
||||||
const {
|
|
||||||
colors,
|
|
||||||
sd: canvasStartDate = null,
|
|
||||||
ed: canvasEndDate = null,
|
|
||||||
size: canvasSize,
|
|
||||||
} = canvas;
|
|
||||||
const is3D = !!canvas.v;
|
|
||||||
|
|
||||||
const x = parseInt(almost[1], 10);
|
|
||||||
const y = parseInt(almost[2], 10);
|
|
||||||
const z = parseInt(almost[3], 10);
|
|
||||||
if (Number.isNaN(x)
|
|
||||||
|| Number.isNaN(y)
|
|
||||||
|| (Number.isNaN(z) && is3D)
|
|
||||||
) {
|
|
||||||
throw new Error('NaN');
|
|
||||||
}
|
|
||||||
const view = [x, y, z];
|
|
||||||
|
|
||||||
let scale = z;
|
|
||||||
if (!scale || Number.isNaN(scale)) {
|
|
||||||
scale = DEFAULT_SCALE;
|
|
||||||
} else {
|
|
||||||
scale = 2 ** (scale / 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is3D && canvasId !== null) {
|
|
||||||
const minScale = TILE_SIZE / canvasSize;
|
|
||||||
scale = clamp(scale, minScale, MAX_SCALE);
|
|
||||||
view.length = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixHistoryIfNeccessary({
|
|
||||||
canvasId,
|
|
||||||
canvasIdent,
|
|
||||||
canvasSize,
|
|
||||||
historicalCanvasSize: canvasSize,
|
|
||||||
is3D,
|
|
||||||
canvasStartDate,
|
|
||||||
canvasEndDate,
|
|
||||||
canvasMaxTiledZoom: getMaxTiledZoom(canvasSize),
|
|
||||||
palette: new Palette(colors, 0),
|
|
||||||
clrIgnore,
|
|
||||||
selectedColor: clrIgnore,
|
|
||||||
view,
|
|
||||||
viewscale: scale,
|
|
||||||
isHistoricalView: false,
|
|
||||||
historicalDate: null,
|
|
||||||
scale,
|
|
||||||
canvases,
|
|
||||||
}, canvasId !== null);
|
|
||||||
} catch (error) {
|
|
||||||
const canvasd = canvases[DEFAULT_CANVAS_ID];
|
|
||||||
return fixHistoryIfNeccessary({
|
|
||||||
canvasId: DEFAULT_CANVAS_ID,
|
|
||||||
canvasIdent: canvasd.ident,
|
|
||||||
canvasSize: canvasd.size,
|
|
||||||
historicalCanvasSize: canvasd.size,
|
|
||||||
is3D: !!canvasd.v,
|
|
||||||
canvasStartDate: canvasd.sd,
|
|
||||||
canvasEndDate: canvasd.ed,
|
|
||||||
canvasMaxTiledZoom: getMaxTiledZoom(canvasd.size),
|
|
||||||
palette: new Palette(canvasd.colors, 0),
|
|
||||||
clrIgnore: canvasd.cli || 0,
|
|
||||||
selectedColor: canvasd.cli || 0,
|
|
||||||
view: [0, 0, 0],
|
|
||||||
viewscale: DEFAULT_SCALE,
|
|
||||||
isHistoricalView: false,
|
|
||||||
historicalDate: null,
|
|
||||||
scale: DEFAULT_SCALE,
|
|
||||||
canvases,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
const { is3D } = !!canvases[canvasId].v;
|
||||||
|
|
||||||
|
const x = parseInt(almost[1], 10) || 0;
|
||||||
|
const y = parseInt(almost[2], 10) || 0;
|
||||||
|
let z = parseInt(almost[3], 10);
|
||||||
|
/*
|
||||||
|
* third number in 3D is z coordinate
|
||||||
|
* in 2D it is logarithmic scale
|
||||||
|
*/
|
||||||
|
if (Number.isNaN(z)) {
|
||||||
|
z = (is3D) ? 0 : DEFAULT_SCALE;
|
||||||
|
} else if (!is3D) {
|
||||||
|
z = 2 ** (z / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
canvasId,
|
||||||
|
canvasIdent,
|
||||||
|
view: [x, y, z],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
...getViewFromURL(DEFAULT_CANVASES),
|
canvasId: null,
|
||||||
|
canvasIdent: 'xx',
|
||||||
|
canvasSize: 65536,
|
||||||
|
historicalCanvasSize: 65536,
|
||||||
|
is3D: null,
|
||||||
|
canvasStartDate: null,
|
||||||
|
canvasEndDate: null,
|
||||||
|
canvasMaxTiledZoom: getMaxTiledZoom(65536),
|
||||||
|
palette: new Palette([[0, 0, 0]]),
|
||||||
|
clrIgnore: 0,
|
||||||
|
selectedColor: 0,
|
||||||
|
// view is not up-to-date, changes are delayed compared to renderer.view
|
||||||
|
view: [0, 0, DEFAULT_SCALE],
|
||||||
|
isHistoricalView: false,
|
||||||
|
historicalDate: null,
|
||||||
historicalTime: null,
|
historicalTime: null,
|
||||||
showHiddenCanvases: false,
|
showHiddenCanvases: false,
|
||||||
hover: null,
|
hover: null,
|
||||||
|
// last canvas view and selectedColor
|
||||||
|
// just used to get back to the previous state when switching canvases
|
||||||
|
// { [canvasId]: { view: [x, y, z], selectedColor: c }, ... }
|
||||||
prevCanvasCoords: {},
|
prevCanvasCoords: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,48 +119,6 @@ export default function canvasReducer(
|
||||||
action,
|
action,
|
||||||
) {
|
) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'SET_SCALE': {
|
|
||||||
let {
|
|
||||||
view,
|
|
||||||
viewscale,
|
|
||||||
} = state;
|
|
||||||
const {
|
|
||||||
isHistoricalView,
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
const canvasSize = (isHistoricalView)
|
|
||||||
? state.historicalCanvasSize
|
|
||||||
: state.canvasSize;
|
|
||||||
|
|
||||||
let [hx, hy] = view;
|
|
||||||
let { scale } = action;
|
|
||||||
const { zoompoint } = action;
|
|
||||||
const minScale = (isHistoricalView) ? 0.7 : TILE_SIZE / canvasSize;
|
|
||||||
scale = clamp(scale, minScale, MAX_SCALE);
|
|
||||||
if (zoompoint) {
|
|
||||||
let scalediff = viewscale;
|
|
||||||
// clamp to 1.0 (just do this when zoompoint is given, or it would mess with phones)
|
|
||||||
viewscale = (scale > 0.85 && scale < 1.20) ? 1.0 : scale;
|
|
||||||
// make sure that zoompoint is on the same space
|
|
||||||
// after zooming
|
|
||||||
scalediff /= viewscale;
|
|
||||||
const [px, py] = zoompoint;
|
|
||||||
hx = px + (hx - px) * scalediff;
|
|
||||||
hy = py + (hy - py) * scalediff;
|
|
||||||
} else {
|
|
||||||
viewscale = scale;
|
|
||||||
}
|
|
||||||
const canvasMinXY = -canvasSize / 2;
|
|
||||||
const canvasMaxXY = canvasSize / 2 - 1;
|
|
||||||
view = [hx, hy].map((z) => clamp(z, canvasMinXY, canvasMaxXY));
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
view,
|
|
||||||
scale,
|
|
||||||
viewscale,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'SET_HISTORICAL_TIME': {
|
case 'SET_HISTORICAL_TIME': {
|
||||||
const {
|
const {
|
||||||
date,
|
date,
|
||||||
|
@ -272,26 +148,20 @@ export default function canvasReducer(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_VIEW_COORDINATES': {
|
case 'UPDATE_VIEW': {
|
||||||
const { view } = action;
|
const { view } = action;
|
||||||
const canvasSize = (state.isHistoricalView)
|
|
||||||
? state.historicalCanvasSize
|
|
||||||
: state.canvasSize;
|
|
||||||
const canvasMinXY = -canvasSize / 2;
|
|
||||||
const canvasMaxXY = canvasSize / 2 - 1;
|
|
||||||
const newview = view.map((z) => clamp(z, canvasMinXY, canvasMaxXY));
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
view: newview,
|
view: [...view],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'RELOAD_URL': {
|
case 'RELOAD_URL': {
|
||||||
const { canvases } = state;
|
const { canvases } = state;
|
||||||
const nextstate = getViewFromURL(canvases);
|
const urlState = getViewFromURL(canvases);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...nextstate,
|
...urlState,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,23 +208,15 @@ export default function canvasReducer(
|
||||||
colors,
|
colors,
|
||||||
} = canvas;
|
} = canvas;
|
||||||
const is3D = !!canvas.v;
|
const is3D = !!canvas.v;
|
||||||
// get previous view, scale and viewscale if possible
|
// get previous view if possible
|
||||||
let viewscale = DEFAULT_SCALE;
|
let view = [0, 0, DEFAULT_SCALE];
|
||||||
let scale = DEFAULT_SCALE;
|
|
||||||
let view = [0, 0, 0];
|
|
||||||
let selectedColor = clrIgnore;
|
let selectedColor = clrIgnore;
|
||||||
if (prevCanvasCoords[canvasId]) {
|
if (prevCanvasCoords[canvasId]) {
|
||||||
view = prevCanvasCoords[canvasId].view;
|
view = prevCanvasCoords[canvasId].view;
|
||||||
viewscale = prevCanvasCoords[canvasId].viewscale;
|
|
||||||
scale = prevCanvasCoords[canvasId].scale;
|
|
||||||
selectedColor = prevCanvasCoords[canvasId].selectedColor;
|
selectedColor = prevCanvasCoords[canvasId].selectedColor;
|
||||||
}
|
}
|
||||||
const palette = new Palette(colors, 0);
|
const palette = new Palette(colors, 0);
|
||||||
|
|
||||||
if (!is3D) {
|
|
||||||
view.length = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixHistoryIfNeccessary({
|
return fixHistoryIfNeccessary({
|
||||||
...state,
|
...state,
|
||||||
canvasId,
|
canvasId,
|
||||||
|
@ -367,17 +229,13 @@ export default function canvasReducer(
|
||||||
palette,
|
palette,
|
||||||
clrIgnore,
|
clrIgnore,
|
||||||
view,
|
view,
|
||||||
viewscale,
|
|
||||||
scale,
|
|
||||||
// reset if last canvas was retired
|
// reset if last canvas was retired
|
||||||
isHistoricalView: (!state.canvasEndDate && state.isHistoricalView),
|
isHistoricalView: (!state.canvasEndDate && state.isHistoricalView),
|
||||||
// remember view, scale and viewscale
|
// remember view and color
|
||||||
prevCanvasCoords: {
|
prevCanvasCoords: {
|
||||||
...state.prevCanvasCoords,
|
...state.prevCanvasCoords,
|
||||||
[prevCanvasId]: {
|
[prevCanvasId]: {
|
||||||
view: state.view,
|
view: state.view,
|
||||||
scale: state.scale,
|
|
||||||
viewscale: state.viewscale,
|
|
||||||
selectedColor: state.selectedColor,
|
selectedColor: state.selectedColor,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -387,48 +245,36 @@ export default function canvasReducer(
|
||||||
case 's/REC_ME': {
|
case 's/REC_ME': {
|
||||||
const { canvases } = action;
|
const { canvases } = action;
|
||||||
let {
|
let {
|
||||||
|
canvasId,
|
||||||
canvasIdent,
|
canvasIdent,
|
||||||
scale,
|
|
||||||
view,
|
view,
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
let canvasId = getIdFromObject(canvases, canvasIdent);
|
if (canvasId === null) {
|
||||||
if (canvasId === null || (
|
({ canvasId, canvasIdent, view } = getViewFromURL(canvases));
|
||||||
!window.ssv?.backupurl && canvases[canvasId].ed
|
|
||||||
)) {
|
|
||||||
canvasId = DEFAULT_CANVAS_ID;
|
|
||||||
canvasIdent = canvases[DEFAULT_CANVAS_ID].ident;
|
|
||||||
}
|
}
|
||||||
const canvas = canvases[canvasId];
|
const canvas = canvases[canvasId];
|
||||||
const clrIgnore = canvas.cli || 0;
|
const clrIgnore = canvas.cli || 0;
|
||||||
const is3D = !!canvas.v;
|
|
||||||
const {
|
const {
|
||||||
size: canvasSize,
|
size: canvasSize,
|
||||||
sd: canvasStartDate = null,
|
sd: canvasStartDate = null,
|
||||||
ed: canvasEndDate = null,
|
ed: canvasEndDate = null,
|
||||||
colors,
|
colors,
|
||||||
} = canvas;
|
} = canvas;
|
||||||
const palette = new Palette(colors, 0);
|
const palette = new Palette(colors);
|
||||||
|
|
||||||
if (!is3D) {
|
|
||||||
const minScale = TILE_SIZE / canvasSize;
|
|
||||||
scale = clamp(scale, minScale, MAX_SCALE);
|
|
||||||
view = [view[0], view[1]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixHistoryIfNeccessary({
|
return fixHistoryIfNeccessary({
|
||||||
...state,
|
...state,
|
||||||
canvasId,
|
canvasId,
|
||||||
canvasIdent,
|
canvasIdent,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
is3D,
|
is3D: !!canvas.v,
|
||||||
canvasStartDate,
|
canvasStartDate,
|
||||||
canvasEndDate,
|
canvasEndDate,
|
||||||
palette,
|
palette,
|
||||||
clrIgnore,
|
clrIgnore,
|
||||||
|
selectedColor: clrIgnore,
|
||||||
canvases,
|
canvases,
|
||||||
viewscale: scale,
|
|
||||||
scale,
|
|
||||||
view,
|
view,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import canvas from './reducers/canvas';
|
||||||
import chat from './reducers/chat';
|
import chat from './reducers/chat';
|
||||||
import fetching from './reducers/fetching';
|
import fetching from './reducers/fetching';
|
||||||
|
|
||||||
export const CURRENT_VERSION = 15;
|
export const CURRENT_VERSION = 17;
|
||||||
|
|
||||||
export const migrate = (state, version) => {
|
export const migrate = (state, version) => {
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
|
|
@ -16,6 +16,7 @@ class Chunk2D extends Chunk {
|
||||||
super(zoom, cx, cy);
|
super(zoom, cx, cy);
|
||||||
this.palette = palette;
|
this.palette = palette;
|
||||||
this.image = document.createElement('canvas');
|
this.image = document.createElement('canvas');
|
||||||
|
this.image.getContext('2d', { willReadFrequently: true, alpha: false });
|
||||||
this.image.width = TILE_SIZE;
|
this.image.width = TILE_SIZE;
|
||||||
this.image.height = TILE_SIZE;
|
this.image.height = TILE_SIZE;
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
|
@ -84,7 +85,6 @@ class Chunk2D extends Chunk {
|
||||||
getColorIndex(cell, nearest = true) {
|
getColorIndex(cell, nearest = true) {
|
||||||
const [x, y] = cell;
|
const [x, y] = cell;
|
||||||
const ctx = this.image.getContext('2d');
|
const ctx = this.image.getContext('2d');
|
||||||
|
|
||||||
const rgb = ctx.getImageData(x, y, 1, 1).data;
|
const rgb = ctx.getImageData(x, y, 1, 1).data;
|
||||||
const ind = (nearest)
|
const ind = (nearest)
|
||||||
? this.palette.getClosestIndexOfColor(rgb[0], rgb[1], rgb[2])
|
? this.palette.getClosestIndexOfColor(rgb[0], rgb[1], rgb[2])
|
||||||
|
|
|
@ -57,6 +57,8 @@ class PixelNotify {
|
||||||
render(
|
render(
|
||||||
state,
|
state,
|
||||||
$viewport,
|
$viewport,
|
||||||
|
view,
|
||||||
|
scale,
|
||||||
) {
|
) {
|
||||||
const viewportCtx = $viewport.getContext('2d');
|
const viewportCtx = $viewport.getContext('2d');
|
||||||
if (!viewportCtx) return;
|
if (!viewportCtx) return;
|
||||||
|
@ -71,7 +73,7 @@ class PixelNotify {
|
||||||
this.pixelList.pop();
|
this.pixelList.pop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const [sx, sy] = worldToScreen(state, $viewport, [x, y])
|
const [sx, sy] = worldToScreen(view, scale, $viewport, [x, y])
|
||||||
.map((z) => z + this.scale / 2);
|
.map((z) => z + this.scale / 2);
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
/*
|
/*
|
||||||
* parent class for Renderer
|
* parent class for Renderer
|
||||||
*/
|
*/
|
||||||
|
import {
|
||||||
|
VIEW_UPDATE_DELAY,
|
||||||
|
} from '../core/constants';
|
||||||
|
import { updateView } from '../store/actions';
|
||||||
|
|
||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
store;
|
store;
|
||||||
|
// object for user controls
|
||||||
|
constrols = {
|
||||||
|
update() {},
|
||||||
|
};
|
||||||
|
|
||||||
|
// chunk loader
|
||||||
|
chunkLoader = null;
|
||||||
// needs to be known for lazy loading THREE
|
// needs to be known for lazy loading THREE
|
||||||
is3D = null;
|
is3D = null;
|
||||||
// chunk loader must be set by subclass
|
// current position (subclass decies what it means),
|
||||||
chunkLoader = null;
|
// will be changed by controls
|
||||||
|
view = [0, 0, 0];
|
||||||
|
//
|
||||||
|
#storeViewTimeout = null;
|
||||||
|
|
||||||
constructor(store) {
|
constructor(store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
|
this.loadViewFromState();
|
||||||
}
|
}
|
||||||
|
|
||||||
get chunks() {
|
get chunks() {
|
||||||
|
@ -20,19 +35,50 @@ class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
get recChunkIds() {
|
get recChunkIds() {
|
||||||
if (!this.chunkLoader) {
|
if (!this.chunkLoader) return [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return this.chunkLoader.recChunkIds;
|
return this.chunkLoader.recChunkIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
destructor() {
|
destructor() {
|
||||||
if (this.chunkLoader) {
|
this.chunkLoader?.destructor();
|
||||||
this.chunkLoader.destructor();
|
this.cancelStoreViewInState();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateView(view) {
|
||||||
|
for (let i = 0; i < view.length; i += 1) {
|
||||||
|
this.view[i] = view[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {}
|
/*
|
||||||
|
* view is in both storea and renderer,
|
||||||
|
* the one in store is for UI elements and not
|
||||||
|
* updated in real time for performance reasons
|
||||||
|
*/
|
||||||
|
loadViewFromState() {
|
||||||
|
if (!this.store) return;
|
||||||
|
this.updateView(this.store.getState().canvas.view);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelStoreViewInState() {
|
||||||
|
if (this.#storeViewTimeout) {
|
||||||
|
clearTimeout(this.#storeViewTimeout);
|
||||||
|
this.#storeViewTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeViewInState() {
|
||||||
|
if (!this.store) return;
|
||||||
|
this.cancelStoreViewInState();
|
||||||
|
this.#storeViewTimeout = setTimeout(() => {
|
||||||
|
this.#storeViewTimeout = null;
|
||||||
|
this.store.dispatch(updateView(this.view));
|
||||||
|
}, VIEW_UPDATE_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.controls?.update();
|
||||||
|
}
|
||||||
|
|
||||||
renderPixel() {}
|
renderPixel() {}
|
||||||
|
|
||||||
|
@ -43,10 +89,7 @@ class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
gc() {
|
gc() {
|
||||||
if (!this.chunkLoader) {
|
this.chunkLoader?.gc(this);
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.chunkLoader.gc(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,17 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TILE_ZOOM_LEVEL, TILE_SIZE } from '../core/constants';
|
import {
|
||||||
|
TILE_ZOOM_LEVEL,
|
||||||
|
TILE_SIZE,
|
||||||
|
MAX_SCALE,
|
||||||
|
} from '../core/constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getTileOfPixel,
|
getTileOfPixel,
|
||||||
getPixelFromChunkOffset,
|
getPixelFromChunkOffset,
|
||||||
getMaxTiledZoom,
|
getMaxTiledZoom,
|
||||||
|
clamp,
|
||||||
} from '../core/utils';
|
} from '../core/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -23,9 +28,8 @@ import ChunkLoader from './ChunkLoader2D';
|
||||||
import pixelNotify from './PixelNotify';
|
import pixelNotify from './PixelNotify';
|
||||||
|
|
||||||
class Renderer2D extends Renderer {
|
class Renderer2D extends Renderer {
|
||||||
is3D = false;
|
|
||||||
//
|
|
||||||
canvasId = null;
|
canvasId = null;
|
||||||
|
viewscale;
|
||||||
//--
|
//--
|
||||||
centerChunk;
|
centerChunk;
|
||||||
tiledScale;
|
tiledScale;
|
||||||
|
@ -43,6 +47,9 @@ class Renderer2D extends Renderer {
|
||||||
|
|
||||||
constructor(store) {
|
constructor(store) {
|
||||||
super(store);
|
super(store);
|
||||||
|
this.is3D = false;
|
||||||
|
[,, this.viewscale] = this.view;
|
||||||
|
|
||||||
this.centerChunk = [null, null];
|
this.centerChunk = [null, null];
|
||||||
this.tiledScale = 0;
|
this.tiledScale = 0;
|
||||||
this.tiledZoom = 4;
|
this.tiledZoom = 4;
|
||||||
|
@ -58,22 +65,23 @@ class Renderer2D extends Renderer {
|
||||||
//--
|
//--
|
||||||
const viewport = document.createElement('canvas');
|
const viewport = document.createElement('canvas');
|
||||||
viewport.className = 'viewport';
|
viewport.className = 'viewport';
|
||||||
|
const viewportCtx = viewport.getContext('2d', { alpha: false });
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
//--
|
const canvas = document.createElement('canvas');
|
||||||
this.canvas = document.createElement('canvas');
|
const context = canvas.getContext('2d', { alpha: false });
|
||||||
|
this.canvas = canvas;
|
||||||
this.onWindowResize();
|
this.onWindowResize();
|
||||||
document.body.appendChild(this.viewport);
|
|
||||||
//--
|
//--
|
||||||
|
context.fillStyle = '#C4C4C4';
|
||||||
|
context.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
viewportCtx.fillStyle = '#C4C4C4';
|
||||||
|
viewportCtx.fillRect(0, 0, this.viewport.width, this.viewport.height);
|
||||||
|
//--
|
||||||
|
document.body.appendChild(this.viewport);
|
||||||
this.onWindowResize = this.onWindowResize.bind(this);
|
this.onWindowResize = this.onWindowResize.bind(this);
|
||||||
window.addEventListener('resize', this.onWindowResize);
|
window.addEventListener('resize', this.onWindowResize);
|
||||||
|
|
||||||
const context = this.canvas.getContext('2d');
|
|
||||||
context.fillStyle = '#000000';
|
|
||||||
context.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
//--
|
//--
|
||||||
const state = store.getState();
|
this.updateCanvasData(store.getState());
|
||||||
this.updateCanvasData(state);
|
|
||||||
this.updateScale(state);
|
|
||||||
this.controls = new PixelPainterControls(this, this.viewport, store);
|
this.controls = new PixelPainterControls(this, this.viewport, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,8 +141,10 @@ class Renderer2D extends Renderer {
|
||||||
canvases[canvasId].historicalSizes,
|
canvases[canvasId].historicalSizes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// scale of 0 is impossible, so it always updates
|
||||||
|
this.view[2] = 0;
|
||||||
|
this.updateView(state.canvas.view);
|
||||||
}
|
}
|
||||||
this.updateScale(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOldHistoricalTime(oldDate, oldTime) {
|
updateOldHistoricalTime(oldDate, oldTime) {
|
||||||
|
@ -150,7 +160,7 @@ class Renderer2D extends Renderer {
|
||||||
historicalCanvasSize,
|
historicalCanvasSize,
|
||||||
);
|
);
|
||||||
this.forceNextRender = true;
|
this.forceNextRender = true;
|
||||||
this.updateScale(this.store.getState());
|
this.updateView(this.store.getState().canvas.view);
|
||||||
}
|
}
|
||||||
|
|
||||||
getColorIndexOfPixel(cx, cy, historical = false) {
|
getColorIndexOfPixel(cx, cy, historical = false) {
|
||||||
|
@ -167,57 +177,76 @@ class Renderer2D extends Renderer {
|
||||||
return this.chunkLoader.getColorIndexOfPixel(cx, cy);
|
return this.chunkLoader.getColorIndexOfPixel(cx, cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateScale(
|
updateView(view, origin) {
|
||||||
state,
|
let [x, y, scale] = view;
|
||||||
prevScale = null,
|
const state = this.store.getState();
|
||||||
) {
|
const { isHistoricalView } = state.canvas;
|
||||||
const {
|
const canvasSize = (isHistoricalView)
|
||||||
viewscale,
|
|
||||||
isHistoricalView,
|
|
||||||
} = state.canvas;
|
|
||||||
pixelNotify.updateScale(viewscale);
|
|
||||||
let tiledScale = (viewscale > 0.5)
|
|
||||||
? 0
|
|
||||||
: Math.round(Math.log2(viewscale) * 2 / TILE_ZOOM_LEVEL);
|
|
||||||
tiledScale = TILE_ZOOM_LEVEL ** tiledScale;
|
|
||||||
const canvasMaxTiledZoom = (isHistoricalView)
|
|
||||||
? this.historicalCanvasMaxTiledZoom
|
|
||||||
: this.canvasMaxTiledZoom;
|
|
||||||
const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale)
|
|
||||||
* 2 / TILE_ZOOM_LEVEL;
|
|
||||||
const relScale = viewscale / tiledScale;
|
|
||||||
|
|
||||||
this.tiledScale = tiledScale;
|
|
||||||
this.tiledZoom = tiledZoom;
|
|
||||||
this.relScale = relScale;
|
|
||||||
this.updateView(state);
|
|
||||||
if (prevScale === null
|
|
||||||
|| viewscale < this.scaleThreshold || prevScale < this.scaleThreshold) {
|
|
||||||
this.forceNextRender = true;
|
|
||||||
} else {
|
|
||||||
this.forceNextSubrender = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateView(state) {
|
|
||||||
const {
|
|
||||||
view,
|
|
||||||
} = state.canvas;
|
|
||||||
const canvasSize = (state.canvas.isHistoricalView)
|
|
||||||
? state.canvas.historicalCanvasSize
|
? state.canvas.historicalCanvasSize
|
||||||
: state.canvas.canvasSize;
|
: state.canvas.canvasSize;
|
||||||
|
|
||||||
const [x, y] = view;
|
// clamp scale and set viewscale
|
||||||
let [cx, cy] = this.centerChunk;
|
if (scale) {
|
||||||
const [curcx, curcy] = getTileOfPixel(
|
const minScale = (isHistoricalView) ? 0.7 : TILE_SIZE / canvasSize;
|
||||||
|
scale = clamp(view[2], minScale, MAX_SCALE);
|
||||||
|
if (origin) {
|
||||||
|
let scalediff = this.viewscale;
|
||||||
|
// clamp to 1.0 (only when origin is given, so not on phones)
|
||||||
|
this.viewscale = (scale > 0.85 && scale < 1.20) ? 1.0 : scale;
|
||||||
|
// make sure that origin is at the same place on the screen
|
||||||
|
scalediff /= this.viewscale;
|
||||||
|
const [px, py] = origin;
|
||||||
|
x = px + (x - px) * scalediff;
|
||||||
|
y = py + (y - py) * scalediff;
|
||||||
|
} else {
|
||||||
|
this.viewscale = scale;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[,, scale] = this.view;
|
||||||
|
}
|
||||||
|
// clamp coords
|
||||||
|
const canvasMinXY = -canvasSize / 2;
|
||||||
|
const canvasMaxXY = canvasSize / 2 - 1;
|
||||||
|
x = clamp(x, canvasMinXY, canvasMaxXY);
|
||||||
|
y = clamp(y, canvasMinXY, canvasMaxXY);
|
||||||
|
|
||||||
|
const prevScale = this.view[2];
|
||||||
|
super.updateView([x, y, scale]);
|
||||||
|
|
||||||
|
if (prevScale !== scale) {
|
||||||
|
const { viewscale } = this;
|
||||||
|
pixelNotify.updateScale(viewscale);
|
||||||
|
let tiledScale = (viewscale > 0.5)
|
||||||
|
? 0
|
||||||
|
: Math.round(Math.log2(viewscale) * 2 / TILE_ZOOM_LEVEL);
|
||||||
|
tiledScale = TILE_ZOOM_LEVEL ** tiledScale;
|
||||||
|
const canvasMaxTiledZoom = (isHistoricalView)
|
||||||
|
? this.historicalCanvasMaxTiledZoom
|
||||||
|
: this.canvasMaxTiledZoom;
|
||||||
|
const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale)
|
||||||
|
* 2 / TILE_ZOOM_LEVEL;
|
||||||
|
const relScale = viewscale / tiledScale;
|
||||||
|
this.tiledScale = tiledScale;
|
||||||
|
this.tiledZoom = tiledZoom;
|
||||||
|
this.relScale = relScale;
|
||||||
|
if (viewscale < this.scaleThreshold || prevScale < this.scaleThreshold) {
|
||||||
|
this.forceNextRender = true;
|
||||||
|
} else {
|
||||||
|
this.forceNextSubrender = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevCenterChunk = this.centerChunk;
|
||||||
|
const centerChunk = getTileOfPixel(
|
||||||
this.tiledScale,
|
this.tiledScale,
|
||||||
[x, y],
|
[x, y],
|
||||||
canvasSize,
|
canvasSize,
|
||||||
);
|
);
|
||||||
if (cx !== curcx || cy !== curcy) {
|
if (!prevCenterChunk
|
||||||
cx = curcx;
|
|| prevCenterChunk[0] !== centerChunk[0]
|
||||||
cy = curcy;
|
|| prevCenterChunk[1] !== centerChunk[1]
|
||||||
this.centerChunk = [cx, cy];
|
) {
|
||||||
|
this.centerChunk = centerChunk;
|
||||||
this.forceNextRender = true;
|
this.forceNextRender = true;
|
||||||
} else {
|
} else {
|
||||||
this.forceNextSubrender = true;
|
this.forceNextSubrender = true;
|
||||||
|
@ -236,9 +265,9 @@ class Renderer2D extends Renderer {
|
||||||
const {
|
const {
|
||||||
canvasSize,
|
canvasSize,
|
||||||
palette,
|
palette,
|
||||||
scale,
|
|
||||||
isHistoricalView,
|
isHistoricalView,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
const scale = this.viewscale;
|
||||||
this.chunkLoader.getPixelUpdate(i, j, offset, color);
|
this.chunkLoader.getPixelUpdate(i, j, offset, color);
|
||||||
|
|
||||||
if (scale < 0.8 || isHistoricalView) return;
|
if (scale < 0.8 || isHistoricalView) return;
|
||||||
|
@ -315,9 +344,9 @@ class Renderer2D extends Renderer {
|
||||||
tiledScale,
|
tiledScale,
|
||||||
tiledZoom,
|
tiledZoom,
|
||||||
viewport,
|
viewport,
|
||||||
|
viewscale: scale,
|
||||||
} = this;
|
} = this;
|
||||||
const {
|
const {
|
||||||
viewscale: scale,
|
|
||||||
canvasSize,
|
canvasSize,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
|
||||||
|
@ -405,6 +434,7 @@ class Renderer2D extends Renderer {
|
||||||
if (!this.chunkLoader) {
|
if (!this.chunkLoader) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
super.render();
|
||||||
const state = this.store.getState();
|
const state = this.store.getState();
|
||||||
if (state.canvas.isHistoricalView) {
|
if (state.canvas.isHistoricalView) {
|
||||||
this.renderHistorical(state);
|
this.renderHistorical(state);
|
||||||
|
@ -421,6 +451,8 @@ class Renderer2D extends Renderer {
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
viewport,
|
viewport,
|
||||||
|
view,
|
||||||
|
viewscale,
|
||||||
} = this;
|
} = this;
|
||||||
const {
|
const {
|
||||||
showGrid,
|
showGrid,
|
||||||
|
@ -432,8 +464,6 @@ class Renderer2D extends Renderer {
|
||||||
fetchingPixel,
|
fetchingPixel,
|
||||||
} = state.fetching;
|
} = state.fetching;
|
||||||
const {
|
const {
|
||||||
view,
|
|
||||||
viewscale,
|
|
||||||
canvasSize,
|
canvasSize,
|
||||||
hover,
|
hover,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
@ -523,16 +553,18 @@ class Renderer2D extends Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showGrid && viewscale >= 8) {
|
if (showGrid && viewscale >= 8) {
|
||||||
renderGrid(state, viewport, viewscale, isLightGrid);
|
renderGrid(state, viewport, view, viewscale, isLightGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doRenderPixelnotify) pixelNotify.render(state, viewport);
|
if (doRenderPixelnotify) {
|
||||||
|
pixelNotify.render(state, viewport, view, viewscale);
|
||||||
|
}
|
||||||
|
|
||||||
if (hover && doRenderPlaceholder) {
|
if (hover && doRenderPlaceholder) {
|
||||||
renderPlaceholder(state, viewport, viewscale);
|
renderPlaceholder(state, viewport, view, viewscale);
|
||||||
}
|
}
|
||||||
if (hover && doRenderPotatoPlaceholder) {
|
if (hover && doRenderPotatoPlaceholder) {
|
||||||
renderPotatoPlaceholder(state, viewport, viewscale);
|
renderPotatoPlaceholder(state, viewport, view, viewscale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,10 +578,10 @@ class Renderer2D extends Renderer {
|
||||||
const {
|
const {
|
||||||
centerChunk: chunkPosition,
|
centerChunk: chunkPosition,
|
||||||
viewport,
|
viewport,
|
||||||
|
viewscale,
|
||||||
oldHistoricalTime,
|
oldHistoricalTime,
|
||||||
} = this;
|
} = this;
|
||||||
const {
|
const {
|
||||||
viewscale,
|
|
||||||
historicalDate,
|
historicalDate,
|
||||||
historicalTime,
|
historicalTime,
|
||||||
historicalCanvasSize,
|
historicalCanvasSize,
|
||||||
|
@ -672,14 +704,14 @@ class Renderer2D extends Renderer {
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
viewport,
|
viewport,
|
||||||
|
view,
|
||||||
|
viewscale,
|
||||||
} = this;
|
} = this;
|
||||||
const {
|
const {
|
||||||
showGrid,
|
showGrid,
|
||||||
isLightGrid,
|
isLightGrid,
|
||||||
} = state.gui;
|
} = state.gui;
|
||||||
const {
|
const {
|
||||||
view,
|
|
||||||
viewscale,
|
|
||||||
historicalCanvasSize,
|
historicalCanvasSize,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,6 @@ import pixelTransferController from './PixelTransferController';
|
||||||
const renderDistance = 150;
|
const renderDistance = 150;
|
||||||
|
|
||||||
class Renderer3D extends Renderer {
|
class Renderer3D extends Renderer {
|
||||||
is3D = true;
|
|
||||||
//--
|
|
||||||
scene;
|
scene;
|
||||||
camera;
|
camera;
|
||||||
rollOverMesh;
|
rollOverMesh;
|
||||||
|
@ -51,6 +49,7 @@ class Renderer3D extends Renderer {
|
||||||
|
|
||||||
constructor(store) {
|
constructor(store) {
|
||||||
super(store);
|
super(store);
|
||||||
|
this.is3D = true;
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
this.objects = [];
|
this.objects = [];
|
||||||
|
|
||||||
|
@ -163,6 +162,7 @@ class Renderer3D extends Renderer {
|
||||||
|
|
||||||
// controls
|
// controls
|
||||||
const controls = new VoxelPainterControls(
|
const controls = new VoxelPainterControls(
|
||||||
|
this,
|
||||||
camera,
|
camera,
|
||||||
domElement,
|
domElement,
|
||||||
store,
|
store,
|
||||||
|
@ -354,7 +354,7 @@ class Renderer3D extends Renderer {
|
||||||
if (!this.threeRenderer) {
|
if (!this.threeRenderer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.controls.update();
|
super.render();
|
||||||
if (this.forceNextRender) {
|
if (this.forceNextRender) {
|
||||||
this.reloadChunks();
|
this.reloadChunks();
|
||||||
this.forceNextRender = false;
|
this.forceNextRender = false;
|
||||||
|
|
|
@ -11,14 +11,14 @@ const PLACEHOLDER_BORDER = 1;
|
||||||
export function renderPlaceholder(
|
export function renderPlaceholder(
|
||||||
state,
|
state,
|
||||||
$viewport,
|
$viewport,
|
||||||
|
view,
|
||||||
scale,
|
scale,
|
||||||
) {
|
) {
|
||||||
const viewportCtx = $viewport.getContext('2d');
|
const viewportCtx = $viewport.getContext('2d');
|
||||||
|
|
||||||
const { hover } = state.canvas;
|
const { hover, palette, selectedColor } = state.canvas;
|
||||||
const { palette, selectedColor } = state.canvas;
|
|
||||||
|
|
||||||
const [sx, sy] = worldToScreen(state, $viewport, hover);
|
const [sx, sy] = worldToScreen(view, scale, $viewport, hover);
|
||||||
|
|
||||||
viewportCtx.save();
|
viewportCtx.save();
|
||||||
viewportCtx.translate(sx + (scale / 2), sy + (scale / 2));
|
viewportCtx.translate(sx + (scale / 2), sy + (scale / 2));
|
||||||
|
@ -45,6 +45,7 @@ export function renderPlaceholder(
|
||||||
export function renderPotatoPlaceholder(
|
export function renderPotatoPlaceholder(
|
||||||
state,
|
state,
|
||||||
$viewport,
|
$viewport,
|
||||||
|
view,
|
||||||
scale,
|
scale,
|
||||||
) {
|
) {
|
||||||
const viewportCtx = $viewport.getContext('2d');
|
const viewportCtx = $viewport.getContext('2d');
|
||||||
|
@ -52,7 +53,7 @@ export function renderPotatoPlaceholder(
|
||||||
const { hover } = state.canvas;
|
const { hover } = state.canvas;
|
||||||
const { palette, selectedColor } = state.canvas;
|
const { palette, selectedColor } = state.canvas;
|
||||||
|
|
||||||
const [sx, sy] = worldToScreen(state, $viewport, hover);
|
const [sx, sy] = worldToScreen(view, scale, $viewport, hover);
|
||||||
|
|
||||||
viewportCtx.save();
|
viewportCtx.save();
|
||||||
viewportCtx.fillStyle = '#000';
|
viewportCtx.fillStyle = '#000';
|
||||||
|
@ -72,6 +73,7 @@ export function renderPotatoPlaceholder(
|
||||||
export function renderGrid(
|
export function renderGrid(
|
||||||
state,
|
state,
|
||||||
$viewport,
|
$viewport,
|
||||||
|
view,
|
||||||
scale,
|
scale,
|
||||||
isLightGrid,
|
isLightGrid,
|
||||||
) {
|
) {
|
||||||
|
@ -83,8 +85,8 @@ export function renderGrid(
|
||||||
viewportCtx.globalAlpha = 0.5;
|
viewportCtx.globalAlpha = 0.5;
|
||||||
viewportCtx.fillStyle = (isLightGrid) ? '#DDDDDD' : '#222222';
|
viewportCtx.fillStyle = (isLightGrid) ? '#DDDDDD' : '#222222';
|
||||||
|
|
||||||
let [xoff, yoff] = screenToWorld(state, $viewport, [0, 0]);
|
let [xoff, yoff] = screenToWorld(view, scale, $viewport, [0, 0]);
|
||||||
let [x, y] = worldToScreen(state, $viewport, [xoff, yoff]);
|
let [x, y] = worldToScreen(view, scale, $viewport, [xoff, yoff]);
|
||||||
|
|
||||||
for (; x < width; x += scale) {
|
for (; x < width; x += scale) {
|
||||||
const thick = (xoff++ % 10 === 0) ? 2 : 1;
|
const thick = (xoff++ % 10 === 0) ? 2 : 1;
|
||||||
|
|
|
@ -24,23 +24,29 @@ animationLoop();
|
||||||
|
|
||||||
export async function initRenderer(store, is3D) {
|
export async function initRenderer(store, is3D) {
|
||||||
renderer.destructor();
|
renderer.destructor();
|
||||||
if (is3D) {
|
switch (is3D) {
|
||||||
if (!isWebGL2Available()) {
|
case true: {
|
||||||
store.dispatch(pAlert(
|
if (!isWebGL2Available()) {
|
||||||
t`Canvas Error`,
|
store.dispatch(pAlert(
|
||||||
t`Can't render 3D canvas, do you have WebGL2 disabled?`,
|
t`Canvas Error`,
|
||||||
'error',
|
t`Can't render 3D canvas, do you have WebGL2 disabled?`,
|
||||||
'OK',
|
'error',
|
||||||
));
|
'OK',
|
||||||
renderer = dummyRenderer;
|
));
|
||||||
} else {
|
renderer = dummyRenderer;
|
||||||
/* eslint-disable-next-line max-len */
|
} else {
|
||||||
const module = await import(/* webpackChunkName: "voxel" */ './Renderer3D');
|
/* eslint-disable-next-line max-len */
|
||||||
const Renderer3D = module.default;
|
const module = await import(/* webpackChunkName: "voxel" */ './Renderer3D');
|
||||||
renderer = new Renderer3D(store);
|
const Renderer3D = module.default;
|
||||||
|
renderer = new Renderer3D(store);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
case false:
|
||||||
renderer = new Renderer2D(store);
|
renderer = new Renderer2D(store);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderer = dummyRenderer;
|
||||||
}
|
}
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user