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 store from './store/store';
|
||||
import renderApp from './components/App';
|
||||
import { initRenderer, getRenderer } from './ui/rendererFactory';
|
||||
import { getRenderer } from './ui/rendererFactory';
|
||||
import socketClient from './socket/SocketClient';
|
||||
import { GC_INTERVAL } from './core/constants';
|
||||
|
||||
|
@ -26,8 +26,6 @@ persistStore(store, {}, () => {
|
|||
|
||||
store.dispatch({ type: 'HYDRATED' });
|
||||
|
||||
initRenderer(store, false);
|
||||
|
||||
pixelTransferController.initialize(store, socketClient, getRenderer);
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import copy from '../utils/clipboard';
|
||||
|
@ -16,10 +16,21 @@ function renderCoordinates(cell) {
|
|||
|
||||
|
||||
const CoordinatesBox = () => {
|
||||
const view = useSelector((state) => state.canvas.view);
|
||||
const hover = useSelector((state) => state.canvas.hover);
|
||||
const [view, hover, is3D] = useSelector((state) => [
|
||||
state.canvas.view,
|
||||
state.canvas.hover,
|
||||
state.canvas.is3D,
|
||||
], shallowEqual);
|
||||
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 (
|
||||
<div
|
||||
className="coorbox"
|
||||
|
@ -31,8 +42,7 @@ const CoordinatesBox = () => {
|
|||
title={t`Copy to Clipboard`}
|
||||
tabIndex="0"
|
||||
>{
|
||||
renderCoordinates(hover
|
||||
|| view.map(Math.round))
|
||||
renderCoordinates(coords)
|
||||
}</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,10 +9,10 @@ import CoolDownBox from './CoolDownBox';
|
|||
import NotifyBox from './NotifyBox';
|
||||
import GlobeButton from './buttons/GlobeButton';
|
||||
import PalselButton from './buttons/PalselButton';
|
||||
import MovementControls from './buttons/MovementControls';
|
||||
import Palette from './Palette';
|
||||
import Alert from './Alert';
|
||||
import HistorySelect from './HistorySelect';
|
||||
import Mobile3DControls from './Mobile3DControls';
|
||||
|
||||
const UI = () => {
|
||||
const [
|
||||
|
@ -35,7 +35,7 @@ const UI = () => {
|
|||
<PalselButton />
|
||||
<Palette />
|
||||
{(!is3D) && <GlobeButton />}
|
||||
{(is3D && isOnMobile) && <Mobile3DControls />}
|
||||
{(isOnMobile) && <MovementControls />}
|
||||
<CoolDownBox />
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { getRenderer } from '../ui/rendererFactory';
|
||||
import { getRenderer } from '../../ui/rendererFactory';
|
||||
|
||||
const btnStyle = {
|
||||
fontSize: 34,
|
||||
|
@ -53,7 +53,7 @@ function cancelMovement() {
|
|||
renderer.controls.moveDown = false;
|
||||
}
|
||||
|
||||
const Mobile3DControls = () => (
|
||||
const MovementControls = () => (
|
||||
<div>
|
||||
<div
|
||||
className="actionbuttons"
|
||||
|
@ -220,4 +220,4 @@ const Mobile3DControls = () => (
|
|||
</div>
|
||||
);
|
||||
|
||||
export default Mobile3DControls;
|
||||
export default MovementControls;
|
|
@ -9,16 +9,12 @@
|
|||
import {
|
||||
setHover,
|
||||
unsetHover,
|
||||
setViewCoordinates,
|
||||
setScale,
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
selectColor,
|
||||
moveNorth,
|
||||
moveWest,
|
||||
moveSouth,
|
||||
moveEast,
|
||||
onViewFinishChange,
|
||||
} from '../store/actions';
|
||||
import pixelTransferController from '../ui/PixelTransferController';
|
||||
import {
|
||||
|
@ -28,8 +24,8 @@ import {
|
|||
} from '../core/utils';
|
||||
|
||||
class PixelPainterControls {
|
||||
constructor(renderer, viewport, curStore) {
|
||||
this.store = curStore;
|
||||
constructor(renderer, viewport, store) {
|
||||
this.store = store;
|
||||
this.renderer = renderer;
|
||||
this.viewport = viewport;
|
||||
|
||||
|
@ -45,8 +41,6 @@ class PixelPainterControls {
|
|||
this.onTouchEnd = this.onTouchEnd.bind(this);
|
||||
this.onTouchMove = this.onTouchMove.bind(this);
|
||||
|
||||
this.onViewFinishChangeTimeOut = null;
|
||||
|
||||
this.clickTapStartView = [0, 0];
|
||||
this.clickTapStartTime = 0;
|
||||
this.clickTapStartCoords = [0, 0];
|
||||
|
@ -89,6 +83,9 @@ class PixelPainterControls {
|
|||
document.removeEventListener('keyup', this.onKeyUp, false);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
update() {}
|
||||
|
||||
gotCoolDownDelta(delta) {
|
||||
this.coolDownDelta = true;
|
||||
setTimeout(() => {
|
||||
|
@ -101,12 +98,12 @@ class PixelPainterControls {
|
|||
document.activeElement.blur();
|
||||
|
||||
if (event.button === 0) {
|
||||
clearTimeout(this.onViewFinishChangeTimeOut);
|
||||
this.renderer.cancelStoreViewInState();
|
||||
this.isClicking = true;
|
||||
const { clientX, clientY } = event;
|
||||
this.clickTapStartTime = Date.now();
|
||||
this.clickTapStartCoords = [clientX, clientY];
|
||||
this.clickTapStartView = this.store.getState().canvas.view;
|
||||
this.clickTapStartView = [...this.renderer.view];
|
||||
const { viewport } = this;
|
||||
setTimeout(() => {
|
||||
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) {
|
||||
event.preventDefault();
|
||||
|
||||
const { store } = this;
|
||||
const { store, renderer } = this;
|
||||
if (event.button === 0) {
|
||||
this.isClicking = false;
|
||||
const { clientX, clientY } = event;
|
||||
|
@ -143,21 +128,21 @@ class PixelPainterControls {
|
|||
// thresholds for single click / holding
|
||||
if (clickTapStartTime > Date.now() - 250
|
||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
||||
const state = store.getState();
|
||||
const cell = screenToWorld(
|
||||
state,
|
||||
renderer.view,
|
||||
renderer.viewscale,
|
||||
this.viewport,
|
||||
[clientX, clientY],
|
||||
);
|
||||
PixelPainterControls.placePixel(
|
||||
store,
|
||||
this.renderer,
|
||||
renderer,
|
||||
cell,
|
||||
);
|
||||
}
|
||||
this.viewport.style.cursor = 'auto';
|
||||
}
|
||||
this.scheduleOnViewFinishChange();
|
||||
renderer.storeViewInState();
|
||||
}
|
||||
|
||||
static getTouchCenter(event) {
|
||||
|
@ -186,11 +171,8 @@ class PixelPainterControls {
|
|||
static placePixel(store, renderer, cell, colorIndex = null) {
|
||||
const state = store.getState();
|
||||
const { autoZoomIn } = state.gui;
|
||||
const { clrIgnore } = state.canvas;
|
||||
const {
|
||||
scale,
|
||||
isHistoricalView,
|
||||
} = state.canvas;
|
||||
const { clrIgnore, isHistoricalView } = state.canvas;
|
||||
const { viewscale: scale } = renderer;
|
||||
const selectedColor = (colorIndex === null)
|
||||
? state.canvas.selectedColor
|
||||
: colorIndex;
|
||||
|
@ -198,8 +180,7 @@ class PixelPainterControls {
|
|||
if (isHistoricalView) return;
|
||||
|
||||
if (autoZoomIn && scale < 8) {
|
||||
store.dispatch(setViewCoordinates(cell));
|
||||
store.dispatch(setScale(12));
|
||||
renderer.updateView([cell[0], cell[1], 12]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -266,14 +247,12 @@ class PixelPainterControls {
|
|||
event.stopPropagation();
|
||||
document.activeElement.blur();
|
||||
|
||||
clearTimeout(this.onViewFinishChangeTimeOut);
|
||||
this.renderer.cancelStoreViewInState();
|
||||
this.clickTapStartTime = Date.now();
|
||||
this.clickTapStartCoords = PixelPainterControls.getTouchCenter(event);
|
||||
const state = this.store.getState();
|
||||
this.clickTapStartView = state.canvas.view;
|
||||
this.clickTapStartView = [...this.renderer.view];
|
||||
|
||||
if (event.touches.length > 1) {
|
||||
this.tapStartScale = state.canvas.scale;
|
||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||
this.isMultiTab = true;
|
||||
this.clearTabTimeout();
|
||||
|
@ -296,7 +275,7 @@ class PixelPainterControls {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const { store } = this;
|
||||
const { store, renderer } = this;
|
||||
if (event.touches.length === 0 && this.isClicking) {
|
||||
const { pageX, pageY } = event.changedTouches[0];
|
||||
const { clickTapStartCoords, clickTapStartTime } = this;
|
||||
|
@ -307,11 +286,10 @@ class PixelPainterControls {
|
|||
// thresholds for single click / holding
|
||||
if (clickTapStartTime > Date.now() - 580
|
||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
||||
const { viewport } = this;
|
||||
const state = store.getState();
|
||||
const cell = screenToWorld(
|
||||
state,
|
||||
viewport,
|
||||
renderer.view,
|
||||
renderer.viewscale,
|
||||
this.viewport,
|
||||
[pageX, pageY],
|
||||
);
|
||||
PixelPainterControls.placePixel(
|
||||
|
@ -324,7 +302,7 @@ class PixelPainterControls {
|
|||
}, 500);
|
||||
}
|
||||
}
|
||||
this.scheduleOnViewFinishChange();
|
||||
renderer.storeViewInState();
|
||||
this.clearTabTimeout();
|
||||
}
|
||||
|
||||
|
@ -335,15 +313,12 @@ class PixelPainterControls {
|
|||
const multiTouch = (event.touches.length > 1);
|
||||
|
||||
const [clientX, clientY] = PixelPainterControls.getTouchCenter(event);
|
||||
const { store } = this;
|
||||
const state = store.getState();
|
||||
if (this.isMultiTab !== multiTouch) {
|
||||
// if one finger got lifted or added, reset clickTabStart
|
||||
this.isMultiTab = multiTouch;
|
||||
this.clickTapStartCoords = [clientX, clientY];
|
||||
this.clickTapStartView = state.canvas.view;
|
||||
this.clickTapStartView = [...this.renderer.view];
|
||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||
this.tapStartScale = state.canvas.scale;
|
||||
} else {
|
||||
// pan
|
||||
const { clickTapStartView, clickTapStartCoords } = this;
|
||||
|
@ -354,11 +329,11 @@ class PixelPainterControls {
|
|||
if (deltaX > 2 || deltaY > 2) {
|
||||
this.clearTabTimeout();
|
||||
}
|
||||
const { scale } = state.canvas;
|
||||
store.dispatch(setViewCoordinates([
|
||||
const { viewscale: scale } = this.renderer.view;
|
||||
this.renderer.updateView([
|
||||
lastPosX - (deltaX / scale),
|
||||
lastPosY - (deltaY / scale),
|
||||
]));
|
||||
]);
|
||||
|
||||
// pinch
|
||||
if (multiTouch) {
|
||||
|
@ -366,12 +341,12 @@ class PixelPainterControls {
|
|||
|
||||
const a = event.touches[0];
|
||||
const b = event.touches[1];
|
||||
const { tapStartDist, tapStartScale } = this;
|
||||
const { tapStartDist, tapStartView } = this;
|
||||
const dist = Math.sqrt(
|
||||
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
||||
);
|
||||
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) {
|
||||
event.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
||||
const { deltaY } = event;
|
||||
const { store } = this;
|
||||
const state = store.getState();
|
||||
const { hover } = state.canvas;
|
||||
let zoompoint = null;
|
||||
if (hover) {
|
||||
zoompoint = hover;
|
||||
}
|
||||
const { hover } = store.getState().canvas;
|
||||
const origin = hover || null;
|
||||
if (deltaY < 0) {
|
||||
store.dispatch(zoomIn(zoompoint));
|
||||
this.zoomIn(origin);
|
||||
}
|
||||
if (deltaY > 0) {
|
||||
store.dispatch(zoomOut(zoompoint));
|
||||
this.zoomOut(origin);
|
||||
}
|
||||
this.scheduleOnViewFinishChange();
|
||||
}
|
||||
|
||||
onMouseMove(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const { clientX, clientY } = event;
|
||||
const { store, isClicking } = this;
|
||||
const state = store.getState();
|
||||
const { renderer, isClicking } = this;
|
||||
const { viewscale } = renderer;
|
||||
if (isClicking) {
|
||||
if (Date.now() < this.clickTapStartTime + 100) {
|
||||
// 100ms threshold till starting to pan
|
||||
|
@ -421,15 +405,18 @@ class PixelPainterControls {
|
|||
const deltaX = clientX - clickTapStartCoords[0];
|
||||
const deltaY = clientY - clickTapStartCoords[1];
|
||||
|
||||
const { scale } = state.canvas;
|
||||
store.dispatch(setViewCoordinates([
|
||||
lastPosX - (deltaX / scale),
|
||||
lastPosY - (deltaY / scale),
|
||||
]));
|
||||
this.renderer.updateView([
|
||||
lastPosX - (deltaX / viewscale),
|
||||
lastPosY - (deltaY / viewscale),
|
||||
]);
|
||||
} else {
|
||||
const { store } = this;
|
||||
const state = store.getState();
|
||||
const { hover } = state.canvas;
|
||||
const { view } = renderer;
|
||||
const screenCoor = screenToWorld(
|
||||
state,
|
||||
view,
|
||||
viewscale,
|
||||
this.viewport,
|
||||
[clientX, clientY],
|
||||
);
|
||||
|
@ -491,11 +478,15 @@ class PixelPainterControls {
|
|||
}
|
||||
|
||||
static selectColor(store, viewport, renderer, center) {
|
||||
const state = store.getState();
|
||||
if (state.canvas.scale < 3) {
|
||||
if (renderer.viewscale < 3) {
|
||||
return;
|
||||
}
|
||||
const coords = screenToWorld(state, viewport, center);
|
||||
const coords = screenToWorld(
|
||||
renderer.view,
|
||||
renderer.viewscale,
|
||||
viewport,
|
||||
center,
|
||||
);
|
||||
const clrIndex = renderer.getColorIndexOfPixel(...coords);
|
||||
if (clrIndex !== null) {
|
||||
store.dispatch(selectColor(clrIndex));
|
||||
|
@ -558,10 +549,10 @@ class PixelPainterControls {
|
|||
store.dispatch(moveEast());
|
||||
return;
|
||||
case 'KeyE':
|
||||
store.dispatch(zoomIn());
|
||||
this.zoomIn();
|
||||
return;
|
||||
case 'KeyQ':
|
||||
store.dispatch(zoomOut());
|
||||
this.zoomOut();
|
||||
return;
|
||||
default:
|
||||
}
|
||||
|
@ -571,11 +562,11 @@ class PixelPainterControls {
|
|||
*/
|
||||
switch (event.key) {
|
||||
case '+':
|
||||
store.dispatch(zoomIn());
|
||||
break;
|
||||
this.zoomIn();
|
||||
return;
|
||||
case '-':
|
||||
store.dispatch(zoomOut());
|
||||
break;
|
||||
this.zoomOut();
|
||||
return;
|
||||
case 'Control':
|
||||
case 'Shift': {
|
||||
const state = store.getState();
|
||||
|
|
|
@ -25,7 +25,6 @@ import {
|
|||
Vector3,
|
||||
} from 'three';
|
||||
import {
|
||||
onViewFinishChange,
|
||||
setViewCoordinates,
|
||||
} from '../store/actions';
|
||||
import {
|
||||
|
@ -41,8 +40,9 @@ import {
|
|||
// or arrow keys / touch: two-finger move
|
||||
|
||||
class VoxelPainterControls extends EventDispatcher {
|
||||
constructor(object, domElement, store) {
|
||||
constructor(renderer, object, domElement, store) {
|
||||
super();
|
||||
this.renderer = renderer;
|
||||
// eslint-disable-next-line max-len
|
||||
if (domElement === undefined) console.warn('THREE.VoxelPainterControls: The second parameter "domElement" is now mandatory.');
|
||||
// eslint-disable-next-line max-len
|
||||
|
@ -976,13 +976,13 @@ class VoxelPainterControls extends EventDispatcher {
|
|||
if (panOffset.length() < 0.2 && panOffset.length() !== 0.0) {
|
||||
panOffset.set(0, 0, 0);
|
||||
scope.store.dispatch(setViewCoordinates(scope.target.toArray()));
|
||||
scope.store.dispatch(onViewFinishChange());
|
||||
scope.renderer.storeViewInState();
|
||||
} else if (panOffset.length() !== 0.0) {
|
||||
const curTime = Date.now();
|
||||
if (curTime > updateTime + 500) {
|
||||
updateTime = curTime;
|
||||
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 DEFAULT_SCALE = 0.25; //-20 in log2
|
||||
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_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';
|
||||
|
||||
|
@ -97,6 +47,9 @@ export const EVENT_USER_NAME = 'event';
|
|||
export const INFO_USER_NAME = 'info';
|
||||
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
|
||||
export const MAX_LOADED_CHUNKS = 2000;
|
||||
export const MAX_CHUNK_AGE = 300000;
|
||||
|
|
|
@ -155,6 +155,9 @@ export function getOffsetOfPixel(
|
|||
* @return key
|
||||
*/
|
||||
export function getIdFromObject(obj, ident) {
|
||||
if (!obj) {
|
||||
return null;
|
||||
}
|
||||
const ids = Object.keys(obj);
|
||||
for (let i = 0; i < ids.length; i += 1) {
|
||||
const key = ids[i];
|
||||
|
@ -195,30 +198,30 @@ export function getCellInsideChunk(
|
|||
}
|
||||
|
||||
export function screenToWorld(
|
||||
state,
|
||||
view,
|
||||
scale,
|
||||
$viewport,
|
||||
[x, y],
|
||||
) {
|
||||
const { view, viewscale } = state.canvas;
|
||||
const [viewX, viewY] = view;
|
||||
const { width, height } = $viewport;
|
||||
return [
|
||||
Math.floor(((x - (width / 2)) / viewscale) + viewX),
|
||||
Math.floor(((y - (height / 2)) / viewscale) + viewY),
|
||||
Math.floor(((x - (width / 2)) / scale) + viewX),
|
||||
Math.floor(((y - (height / 2)) / scale) + viewY),
|
||||
];
|
||||
}
|
||||
|
||||
export function worldToScreen(
|
||||
state,
|
||||
view,
|
||||
scale,
|
||||
$viewport,
|
||||
[x, y],
|
||||
) {
|
||||
const { view, viewscale } = state.canvas;
|
||||
const [viewX, viewY] = view;
|
||||
const { width, height } = $viewport;
|
||||
return [
|
||||
((x - viewX) * viewscale) + (width / 2),
|
||||
((y - viewY) * viewscale) + (height / 2),
|
||||
((x - viewX) * scale) + (width / 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})})};
|
||||
*/
|
||||
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 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="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}" />
|
||||
</head>
|
||||
<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(
|
||||
'/api/me',
|
||||
);
|
||||
|
|
|
@ -146,6 +146,13 @@ export function selectCanvas(canvasId) {
|
|||
};
|
||||
}
|
||||
|
||||
export function updateView(view) {
|
||||
return {
|
||||
type: 'UPDATE_VIEW',
|
||||
view,
|
||||
};
|
||||
}
|
||||
|
||||
export function setViewCoordinates(view) {
|
||||
return {
|
||||
type: 'SET_VIEW_COORDINATES',
|
||||
|
@ -164,9 +171,9 @@ export function move([dx, dy]) {
|
|||
|
||||
export function moveDirection([vx, vy]) {
|
||||
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]));
|
||||
};
|
||||
}
|
||||
|
@ -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) {
|
||||
return {
|
||||
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) {
|
||||
return {
|
||||
type: 'SET_HISTORICAL_TIME',
|
||||
|
|
|
@ -17,7 +17,7 @@ export default () => (next) => (action) => {
|
|||
break;
|
||||
}
|
||||
|
||||
case 'SET_VIEW_COORDINATES': {
|
||||
case 'UPDATE_VIEW': {
|
||||
/*
|
||||
* view: [x, y] float canvas coordinates of the center of the screen,
|
||||
*/
|
||||
|
|
|
@ -14,15 +14,7 @@ import {
|
|||
export default (store) => (next) => (action) => {
|
||||
const { type } = action;
|
||||
|
||||
let prevScale = null;
|
||||
|
||||
switch (type) {
|
||||
case 'SET_SCALE': {
|
||||
const state = store.getState();
|
||||
prevScale = state.canvas.viewscale;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'SET_HISTORICAL_TIME': {
|
||||
const state = store.getState();
|
||||
const renderer = getRenderer();
|
||||
|
@ -55,6 +47,9 @@ export default (store) => (next) => (action) => {
|
|||
|
||||
if (is3D === renderer.is3D) {
|
||||
renderer.updateCanvasData(state);
|
||||
if (type === 's/RELOAD_URL') {
|
||||
renderer.updateView(state.canvas.view);
|
||||
}
|
||||
} else {
|
||||
initRenderer(store, is3D);
|
||||
}
|
||||
|
@ -115,16 +110,24 @@ export default (store) => (next) => (action) => {
|
|||
break;
|
||||
}
|
||||
|
||||
case 's/TGL_HISTORICAL_VIEW':
|
||||
case 'SET_SCALE': {
|
||||
case 's/TGL_HISTORICAL_VIEW': {
|
||||
const renderer = getRenderer();
|
||||
renderer.updateScale(state, prevScale);
|
||||
renderer.updateView(state.view);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'SET_VIEW_COORDINATES': {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,31 +39,30 @@ export default (store) => (next) => (action) => {
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
case 's/SELECT_CANVAS':
|
||||
case 's/REC_ME':
|
||||
case 'RELOAD_URL':
|
||||
case 'ON_VIEW_FINISH_CHANGE': {
|
||||
case 'UPDATE_VIEW': {
|
||||
const state = store.getState();
|
||||
|
||||
const {
|
||||
view,
|
||||
viewscale,
|
||||
canvasIdent,
|
||||
is3D,
|
||||
} = state.canvas;
|
||||
|
||||
if (action.type !== 'ON_VIEW_FINISH_CHANGE') {
|
||||
if (action.type !== 'UPDATE_VIEW') {
|
||||
const [r, g, b] = state.canvas.palette.rgb;
|
||||
setThemeColorMeta(r, g, b);
|
||||
}
|
||||
|
||||
const coords = view.map((u) => Math.round(u)).join(',');
|
||||
let newhash = `#${canvasIdent},${coords}`;
|
||||
if (!is3D) {
|
||||
const scale = Math.round(Math.log2(viewscale) * 10);
|
||||
newhash += `,${scale}`;
|
||||
}
|
||||
const viewString = view.map((c, ind) => {
|
||||
if (ind === 2 && !is3D) {
|
||||
c = Math.log2(c) * 10;
|
||||
}
|
||||
return Math.round(c);
|
||||
}).join(',');
|
||||
const newhash = `#${canvasIdent},${viewString}`;
|
||||
window.history.replaceState(undefined, undefined, newhash);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Palette from '../../core/Palette';
|
||||
import {
|
||||
clamp,
|
||||
getIdFromObject,
|
||||
getHistoricalCanvasSize,
|
||||
getMaxTiledZoom,
|
||||
|
@ -9,43 +8,10 @@ import {
|
|||
|
||||
|
||||
import {
|
||||
MAX_SCALE,
|
||||
DEFAULT_SCALE,
|
||||
DEFAULT_CANVAS_ID,
|
||||
DEFAULT_CANVASES,
|
||||
TILE_SIZE,
|
||||
} 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
|
||||
* in given state or if properties have to change.
|
||||
|
@ -53,7 +19,7 @@ export type CanvasState = {
|
|||
* @param state
|
||||
* @return same state with fixed historical view
|
||||
*/
|
||||
function fixHistoryIfNeccessary(state, doClamp = true) {
|
||||
function fixHistoryIfNeccessary(state) {
|
||||
const {
|
||||
canvasEndDate,
|
||||
isHistoricalView,
|
||||
|
@ -75,18 +41,12 @@ function fixHistoryIfNeccessary(state, doClamp = true) {
|
|||
canvasId,
|
||||
canvasSize,
|
||||
canvases,
|
||||
scale,
|
||||
viewscale,
|
||||
} = state;
|
||||
state.historicalCanvasSize = getHistoricalCanvasSize(
|
||||
historicalDate,
|
||||
canvasSize,
|
||||
canvases[canvasId]?.historicalSizes,
|
||||
);
|
||||
if (doClamp && (scale < 0.7 || viewscale < 0.7)) {
|
||||
state.scale = 0.7;
|
||||
state.viewscale = 0.7;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
@ -94,105 +54,63 @@ function fixHistoryIfNeccessary(state, doClamp = true) {
|
|||
/*
|
||||
* parse url hash and sets view to coordinates
|
||||
* @param canvases Object with all canvas information
|
||||
* @return view, viewscale and scale for state
|
||||
* @return incomplete state based on URL
|
||||
*/
|
||||
function getViewFromURL(canvases) {
|
||||
const { hash } = window.location;
|
||||
try {
|
||||
const almost = decodeURIComponent(hash).substring(1)
|
||||
.split(',');
|
||||
const almost = decodeURIComponent(hash).substring(1)
|
||||
.split(',');
|
||||
|
||||
const canvasIdent = almost[0];
|
||||
// will be null if not in DEFAULT_CANVASES
|
||||
const canvasId = getIdFromObject(canvases, almost[0]);
|
||||
|
||||
// canvasId is null if canvas data isn't loaded yet and it's not
|
||||
// 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,
|
||||
});
|
||||
let canvasIdent = almost[0];
|
||||
let canvasId = getIdFromObject(canvases, canvasIdent);
|
||||
if (!canvasId || (!window.ssv?.backupurl && canvases[canvasId].ed)) {
|
||||
canvasId = DEFAULT_CANVAS_ID;
|
||||
canvasIdent = canvases[DEFAULT_CANVAS_ID].ident;
|
||||
}
|
||||
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 = {
|
||||
...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,
|
||||
showHiddenCanvases: false,
|
||||
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: {},
|
||||
};
|
||||
|
||||
|
@ -201,48 +119,6 @@ export default function canvasReducer(
|
|||
action,
|
||||
) {
|
||||
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': {
|
||||
const {
|
||||
date,
|
||||
|
@ -272,26 +148,20 @@ export default function canvasReducer(
|
|||
};
|
||||
}
|
||||
|
||||
case 'SET_VIEW_COORDINATES': {
|
||||
case 'UPDATE_VIEW': {
|
||||
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 {
|
||||
...state,
|
||||
view: newview,
|
||||
view: [...view],
|
||||
};
|
||||
}
|
||||
|
||||
case 'RELOAD_URL': {
|
||||
const { canvases } = state;
|
||||
const nextstate = getViewFromURL(canvases);
|
||||
const urlState = getViewFromURL(canvases);
|
||||
return {
|
||||
...state,
|
||||
...nextstate,
|
||||
...urlState,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -338,23 +208,15 @@ export default function canvasReducer(
|
|||
colors,
|
||||
} = canvas;
|
||||
const is3D = !!canvas.v;
|
||||
// get previous view, scale and viewscale if possible
|
||||
let viewscale = DEFAULT_SCALE;
|
||||
let scale = DEFAULT_SCALE;
|
||||
let view = [0, 0, 0];
|
||||
// get previous view if possible
|
||||
let view = [0, 0, DEFAULT_SCALE];
|
||||
let selectedColor = clrIgnore;
|
||||
if (prevCanvasCoords[canvasId]) {
|
||||
view = prevCanvasCoords[canvasId].view;
|
||||
viewscale = prevCanvasCoords[canvasId].viewscale;
|
||||
scale = prevCanvasCoords[canvasId].scale;
|
||||
selectedColor = prevCanvasCoords[canvasId].selectedColor;
|
||||
}
|
||||
const palette = new Palette(colors, 0);
|
||||
|
||||
if (!is3D) {
|
||||
view.length = 2;
|
||||
}
|
||||
|
||||
return fixHistoryIfNeccessary({
|
||||
...state,
|
||||
canvasId,
|
||||
|
@ -367,17 +229,13 @@ export default function canvasReducer(
|
|||
palette,
|
||||
clrIgnore,
|
||||
view,
|
||||
viewscale,
|
||||
scale,
|
||||
// reset if last canvas was retired
|
||||
isHistoricalView: (!state.canvasEndDate && state.isHistoricalView),
|
||||
// remember view, scale and viewscale
|
||||
// remember view and color
|
||||
prevCanvasCoords: {
|
||||
...state.prevCanvasCoords,
|
||||
[prevCanvasId]: {
|
||||
view: state.view,
|
||||
scale: state.scale,
|
||||
viewscale: state.viewscale,
|
||||
selectedColor: state.selectedColor,
|
||||
},
|
||||
},
|
||||
|
@ -387,48 +245,36 @@ export default function canvasReducer(
|
|||
case 's/REC_ME': {
|
||||
const { canvases } = action;
|
||||
let {
|
||||
canvasId,
|
||||
canvasIdent,
|
||||
scale,
|
||||
view,
|
||||
} = state;
|
||||
|
||||
let canvasId = getIdFromObject(canvases, canvasIdent);
|
||||
if (canvasId === null || (
|
||||
!window.ssv?.backupurl && canvases[canvasId].ed
|
||||
)) {
|
||||
canvasId = DEFAULT_CANVAS_ID;
|
||||
canvasIdent = canvases[DEFAULT_CANVAS_ID].ident;
|
||||
if (canvasId === null) {
|
||||
({ canvasId, canvasIdent, view } = getViewFromURL(canvases));
|
||||
}
|
||||
const canvas = canvases[canvasId];
|
||||
const clrIgnore = canvas.cli || 0;
|
||||
const is3D = !!canvas.v;
|
||||
const {
|
||||
size: canvasSize,
|
||||
sd: canvasStartDate = null,
|
||||
ed: canvasEndDate = null,
|
||||
colors,
|
||||
} = canvas;
|
||||
const palette = new Palette(colors, 0);
|
||||
|
||||
if (!is3D) {
|
||||
const minScale = TILE_SIZE / canvasSize;
|
||||
scale = clamp(scale, minScale, MAX_SCALE);
|
||||
view = [view[0], view[1]];
|
||||
}
|
||||
const palette = new Palette(colors);
|
||||
|
||||
return fixHistoryIfNeccessary({
|
||||
...state,
|
||||
canvasId,
|
||||
canvasIdent,
|
||||
canvasSize,
|
||||
is3D,
|
||||
is3D: !!canvas.v,
|
||||
canvasStartDate,
|
||||
canvasEndDate,
|
||||
palette,
|
||||
clrIgnore,
|
||||
selectedColor: clrIgnore,
|
||||
canvases,
|
||||
viewscale: scale,
|
||||
scale,
|
||||
view,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import canvas from './reducers/canvas';
|
|||
import chat from './reducers/chat';
|
||||
import fetching from './reducers/fetching';
|
||||
|
||||
export const CURRENT_VERSION = 15;
|
||||
export const CURRENT_VERSION = 17;
|
||||
|
||||
export const migrate = (state, version) => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
|
|
|
@ -16,6 +16,7 @@ class Chunk2D extends Chunk {
|
|||
super(zoom, cx, cy);
|
||||
this.palette = palette;
|
||||
this.image = document.createElement('canvas');
|
||||
this.image.getContext('2d', { willReadFrequently: true, alpha: false });
|
||||
this.image.width = TILE_SIZE;
|
||||
this.image.height = TILE_SIZE;
|
||||
this.ready = false;
|
||||
|
@ -84,7 +85,6 @@ class Chunk2D extends Chunk {
|
|||
getColorIndex(cell, nearest = true) {
|
||||
const [x, y] = cell;
|
||||
const ctx = this.image.getContext('2d');
|
||||
|
||||
const rgb = ctx.getImageData(x, y, 1, 1).data;
|
||||
const ind = (nearest)
|
||||
? this.palette.getClosestIndexOfColor(rgb[0], rgb[1], rgb[2])
|
||||
|
|
|
@ -57,6 +57,8 @@ class PixelNotify {
|
|||
render(
|
||||
state,
|
||||
$viewport,
|
||||
view,
|
||||
scale,
|
||||
) {
|
||||
const viewportCtx = $viewport.getContext('2d');
|
||||
if (!viewportCtx) return;
|
||||
|
@ -71,7 +73,7 @@ class PixelNotify {
|
|||
this.pixelList.pop();
|
||||
continue;
|
||||
}
|
||||
const [sx, sy] = worldToScreen(state, $viewport, [x, y])
|
||||
const [sx, sy] = worldToScreen(view, scale, $viewport, [x, y])
|
||||
.map((z) => z + this.scale / 2);
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
|
|
|
@ -1,18 +1,33 @@
|
|||
/*
|
||||
* parent class for Renderer
|
||||
*/
|
||||
import {
|
||||
VIEW_UPDATE_DELAY,
|
||||
} from '../core/constants';
|
||||
import { updateView } from '../store/actions';
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
class Renderer {
|
||||
store;
|
||||
// object for user controls
|
||||
constrols = {
|
||||
update() {},
|
||||
};
|
||||
|
||||
// chunk loader
|
||||
chunkLoader = null;
|
||||
// needs to be known for lazy loading THREE
|
||||
is3D = null;
|
||||
// chunk loader must be set by subclass
|
||||
chunkLoader = null;
|
||||
// current position (subclass decies what it means),
|
||||
// will be changed by controls
|
||||
view = [0, 0, 0];
|
||||
//
|
||||
#storeViewTimeout = null;
|
||||
|
||||
constructor(store) {
|
||||
this.store = store;
|
||||
this.loadViewFromState();
|
||||
}
|
||||
|
||||
get chunks() {
|
||||
|
@ -20,19 +35,50 @@ class Renderer {
|
|||
}
|
||||
|
||||
get recChunkIds() {
|
||||
if (!this.chunkLoader) {
|
||||
return [];
|
||||
}
|
||||
if (!this.chunkLoader) return [];
|
||||
return this.chunkLoader.recChunkIds;
|
||||
}
|
||||
|
||||
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() {}
|
||||
|
||||
|
@ -43,10 +89,7 @@ class Renderer {
|
|||
}
|
||||
|
||||
gc() {
|
||||
if (!this.chunkLoader) {
|
||||
return;
|
||||
}
|
||||
this.chunkLoader.gc(this);
|
||||
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 {
|
||||
getTileOfPixel,
|
||||
getPixelFromChunkOffset,
|
||||
getMaxTiledZoom,
|
||||
clamp,
|
||||
} from '../core/utils';
|
||||
|
||||
import {
|
||||
|
@ -23,9 +28,8 @@ import ChunkLoader from './ChunkLoader2D';
|
|||
import pixelNotify from './PixelNotify';
|
||||
|
||||
class Renderer2D extends Renderer {
|
||||
is3D = false;
|
||||
//
|
||||
canvasId = null;
|
||||
viewscale;
|
||||
//--
|
||||
centerChunk;
|
||||
tiledScale;
|
||||
|
@ -43,6 +47,9 @@ class Renderer2D extends Renderer {
|
|||
|
||||
constructor(store) {
|
||||
super(store);
|
||||
this.is3D = false;
|
||||
[,, this.viewscale] = this.view;
|
||||
|
||||
this.centerChunk = [null, null];
|
||||
this.tiledScale = 0;
|
||||
this.tiledZoom = 4;
|
||||
|
@ -58,22 +65,23 @@ class Renderer2D extends Renderer {
|
|||
//--
|
||||
const viewport = document.createElement('canvas');
|
||||
viewport.className = 'viewport';
|
||||
const viewportCtx = viewport.getContext('2d', { alpha: false });
|
||||
this.viewport = viewport;
|
||||
//--
|
||||
this.canvas = document.createElement('canvas');
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d', { alpha: false });
|
||||
this.canvas = canvas;
|
||||
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);
|
||||
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(state);
|
||||
this.updateScale(state);
|
||||
this.updateCanvasData(store.getState());
|
||||
this.controls = new PixelPainterControls(this, this.viewport, store);
|
||||
}
|
||||
|
||||
|
@ -133,8 +141,10 @@ class Renderer2D extends Renderer {
|
|||
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) {
|
||||
|
@ -150,7 +160,7 @@ class Renderer2D extends Renderer {
|
|||
historicalCanvasSize,
|
||||
);
|
||||
this.forceNextRender = true;
|
||||
this.updateScale(this.store.getState());
|
||||
this.updateView(this.store.getState().canvas.view);
|
||||
}
|
||||
|
||||
getColorIndexOfPixel(cx, cy, historical = false) {
|
||||
|
@ -167,57 +177,76 @@ class Renderer2D extends Renderer {
|
|||
return this.chunkLoader.getColorIndexOfPixel(cx, cy);
|
||||
}
|
||||
|
||||
updateScale(
|
||||
state,
|
||||
prevScale = null,
|
||||
) {
|
||||
const {
|
||||
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)
|
||||
updateView(view, origin) {
|
||||
let [x, y, scale] = view;
|
||||
const state = this.store.getState();
|
||||
const { isHistoricalView } = state.canvas;
|
||||
const canvasSize = (isHistoricalView)
|
||||
? state.canvas.historicalCanvasSize
|
||||
: state.canvas.canvasSize;
|
||||
|
||||
const [x, y] = view;
|
||||
let [cx, cy] = this.centerChunk;
|
||||
const [curcx, curcy] = getTileOfPixel(
|
||||
// clamp scale and set viewscale
|
||||
if (scale) {
|
||||
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,
|
||||
[x, y],
|
||||
canvasSize,
|
||||
);
|
||||
if (cx !== curcx || cy !== curcy) {
|
||||
cx = curcx;
|
||||
cy = curcy;
|
||||
this.centerChunk = [cx, cy];
|
||||
if (!prevCenterChunk
|
||||
|| prevCenterChunk[0] !== centerChunk[0]
|
||||
|| prevCenterChunk[1] !== centerChunk[1]
|
||||
) {
|
||||
this.centerChunk = centerChunk;
|
||||
this.forceNextRender = true;
|
||||
} else {
|
||||
this.forceNextSubrender = true;
|
||||
|
@ -236,9 +265,9 @@ class Renderer2D extends Renderer {
|
|||
const {
|
||||
canvasSize,
|
||||
palette,
|
||||
scale,
|
||||
isHistoricalView,
|
||||
} = state.canvas;
|
||||
const scale = this.viewscale;
|
||||
this.chunkLoader.getPixelUpdate(i, j, offset, color);
|
||||
|
||||
if (scale < 0.8 || isHistoricalView) return;
|
||||
|
@ -315,9 +344,9 @@ class Renderer2D extends Renderer {
|
|||
tiledScale,
|
||||
tiledZoom,
|
||||
viewport,
|
||||
viewscale: scale,
|
||||
} = this;
|
||||
const {
|
||||
viewscale: scale,
|
||||
canvasSize,
|
||||
} = state.canvas;
|
||||
|
||||
|
@ -405,6 +434,7 @@ class Renderer2D extends Renderer {
|
|||
if (!this.chunkLoader) {
|
||||
return;
|
||||
}
|
||||
super.render();
|
||||
const state = this.store.getState();
|
||||
if (state.canvas.isHistoricalView) {
|
||||
this.renderHistorical(state);
|
||||
|
@ -421,6 +451,8 @@ class Renderer2D extends Renderer {
|
|||
) {
|
||||
const {
|
||||
viewport,
|
||||
view,
|
||||
viewscale,
|
||||
} = this;
|
||||
const {
|
||||
showGrid,
|
||||
|
@ -432,8 +464,6 @@ class Renderer2D extends Renderer {
|
|||
fetchingPixel,
|
||||
} = state.fetching;
|
||||
const {
|
||||
view,
|
||||
viewscale,
|
||||
canvasSize,
|
||||
hover,
|
||||
} = state.canvas;
|
||||
|
@ -523,16 +553,18 @@ class Renderer2D extends Renderer {
|
|||
}
|
||||
|
||||
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) {
|
||||
renderPlaceholder(state, viewport, viewscale);
|
||||
renderPlaceholder(state, viewport, view, viewscale);
|
||||
}
|
||||
if (hover && doRenderPotatoPlaceholder) {
|
||||
renderPotatoPlaceholder(state, viewport, viewscale);
|
||||
renderPotatoPlaceholder(state, viewport, view, viewscale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,10 +578,10 @@ class Renderer2D extends Renderer {
|
|||
const {
|
||||
centerChunk: chunkPosition,
|
||||
viewport,
|
||||
viewscale,
|
||||
oldHistoricalTime,
|
||||
} = this;
|
||||
const {
|
||||
viewscale,
|
||||
historicalDate,
|
||||
historicalTime,
|
||||
historicalCanvasSize,
|
||||
|
@ -672,14 +704,14 @@ class Renderer2D extends Renderer {
|
|||
) {
|
||||
const {
|
||||
viewport,
|
||||
view,
|
||||
viewscale,
|
||||
} = this;
|
||||
const {
|
||||
showGrid,
|
||||
isLightGrid,
|
||||
} = state.gui;
|
||||
const {
|
||||
view,
|
||||
viewscale,
|
||||
historicalCanvasSize,
|
||||
} = state.canvas;
|
||||
|
||||
|
|
|
@ -26,8 +26,6 @@ import pixelTransferController from './PixelTransferController';
|
|||
const renderDistance = 150;
|
||||
|
||||
class Renderer3D extends Renderer {
|
||||
is3D = true;
|
||||
//--
|
||||
scene;
|
||||
camera;
|
||||
rollOverMesh;
|
||||
|
@ -51,6 +49,7 @@ class Renderer3D extends Renderer {
|
|||
|
||||
constructor(store) {
|
||||
super(store);
|
||||
this.is3D = true;
|
||||
const state = store.getState();
|
||||
this.objects = [];
|
||||
|
||||
|
@ -163,6 +162,7 @@ class Renderer3D extends Renderer {
|
|||
|
||||
// controls
|
||||
const controls = new VoxelPainterControls(
|
||||
this,
|
||||
camera,
|
||||
domElement,
|
||||
store,
|
||||
|
@ -354,7 +354,7 @@ class Renderer3D extends Renderer {
|
|||
if (!this.threeRenderer) {
|
||||
return;
|
||||
}
|
||||
this.controls.update();
|
||||
super.render();
|
||||
if (this.forceNextRender) {
|
||||
this.reloadChunks();
|
||||
this.forceNextRender = false;
|
||||
|
|
|
@ -11,14 +11,14 @@ const PLACEHOLDER_BORDER = 1;
|
|||
export function renderPlaceholder(
|
||||
state,
|
||||
$viewport,
|
||||
view,
|
||||
scale,
|
||||
) {
|
||||
const viewportCtx = $viewport.getContext('2d');
|
||||
|
||||
const { hover } = state.canvas;
|
||||
const { palette, selectedColor } = state.canvas;
|
||||
const { hover, palette, selectedColor } = state.canvas;
|
||||
|
||||
const [sx, sy] = worldToScreen(state, $viewport, hover);
|
||||
const [sx, sy] = worldToScreen(view, scale, $viewport, hover);
|
||||
|
||||
viewportCtx.save();
|
||||
viewportCtx.translate(sx + (scale / 2), sy + (scale / 2));
|
||||
|
@ -45,6 +45,7 @@ export function renderPlaceholder(
|
|||
export function renderPotatoPlaceholder(
|
||||
state,
|
||||
$viewport,
|
||||
view,
|
||||
scale,
|
||||
) {
|
||||
const viewportCtx = $viewport.getContext('2d');
|
||||
|
@ -52,7 +53,7 @@ export function renderPotatoPlaceholder(
|
|||
const { hover } = 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.fillStyle = '#000';
|
||||
|
@ -72,6 +73,7 @@ export function renderPotatoPlaceholder(
|
|||
export function renderGrid(
|
||||
state,
|
||||
$viewport,
|
||||
view,
|
||||
scale,
|
||||
isLightGrid,
|
||||
) {
|
||||
|
@ -83,8 +85,8 @@ export function renderGrid(
|
|||
viewportCtx.globalAlpha = 0.5;
|
||||
viewportCtx.fillStyle = (isLightGrid) ? '#DDDDDD' : '#222222';
|
||||
|
||||
let [xoff, yoff] = screenToWorld(state, $viewport, [0, 0]);
|
||||
let [x, y] = worldToScreen(state, $viewport, [xoff, yoff]);
|
||||
let [xoff, yoff] = screenToWorld(view, scale, $viewport, [0, 0]);
|
||||
let [x, y] = worldToScreen(view, scale, $viewport, [xoff, yoff]);
|
||||
|
||||
for (; x < width; x += scale) {
|
||||
const thick = (xoff++ % 10 === 0) ? 2 : 1;
|
||||
|
|
|
@ -24,23 +24,29 @@ animationLoop();
|
|||
|
||||
export async function initRenderer(store, is3D) {
|
||||
renderer.destructor();
|
||||
if (is3D) {
|
||||
if (!isWebGL2Available()) {
|
||||
store.dispatch(pAlert(
|
||||
t`Canvas Error`,
|
||||
t`Can't render 3D canvas, do you have WebGL2 disabled?`,
|
||||
'error',
|
||||
'OK',
|
||||
));
|
||||
renderer = dummyRenderer;
|
||||
} else {
|
||||
/* eslint-disable-next-line max-len */
|
||||
const module = await import(/* webpackChunkName: "voxel" */ './Renderer3D');
|
||||
const Renderer3D = module.default;
|
||||
renderer = new Renderer3D(store);
|
||||
switch (is3D) {
|
||||
case true: {
|
||||
if (!isWebGL2Available()) {
|
||||
store.dispatch(pAlert(
|
||||
t`Canvas Error`,
|
||||
t`Can't render 3D canvas, do you have WebGL2 disabled?`,
|
||||
'error',
|
||||
'OK',
|
||||
));
|
||||
renderer = dummyRenderer;
|
||||
} else {
|
||||
/* eslint-disable-next-line max-len */
|
||||
const module = await import(/* webpackChunkName: "voxel" */ './Renderer3D');
|
||||
const Renderer3D = module.default;
|
||||
renderer = new Renderer3D(store);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
renderer = new Renderer2D(store);
|
||||
case false:
|
||||
renderer = new Renderer2D(store);
|
||||
break;
|
||||
default:
|
||||
renderer = dummyRenderer;
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue