allow canvases to change sizes in historical view

This commit is contained in:
HF 2021-04-18 16:47:14 +02:00
parent fb35eb5220
commit 3d4ded29ca
8 changed files with 191 additions and 77 deletions

View File

@ -84,7 +84,13 @@
[ 122, 148, 156 ], [ 122, 148, 156 ],
[ 174, 215, 185 ] [ 174, 215, 185 ]
], ],
"size" : 4096, "size": 16384,
"historicalSizes": [
[
"20210417",
4096
]
],
"hid": false, "hid": false,
"cli": 2, "cli": 2,
"bcd": 15000, "bcd": 15000,

View File

@ -69,6 +69,24 @@ export function getMaxTiledZoom(canvasSize: number): number {
return Math.log2(canvasSize / TILE_SIZE) / TILE_ZOOM_LEVEL * 2; return Math.log2(canvasSize / TILE_SIZE) / TILE_ZOOM_LEVEL * 2;
} }
export function getHistoricalCanvasSize(
historicalDate: string,
canvasSize: number,
historicalSizes: Array,
) {
if (historicalDate && historicalSizes) {
let i = historicalSizes.length;
while (i > 0) {
i -= 1;
const [date, size] = historicalSizes[i];
if (historicalDate <= date) {
return size;
}
}
}
return canvasSize;
}
export function getCanvasBoundaries(canvasSize: number): number { export function getCanvasBoundaries(canvasSize: number): number {
const canvasMinXY = -canvasSize / 2; const canvasMinXY = -canvasSize / 2;
const canvasMaxXY = canvasSize / 2 - 1; const canvasMaxXY = canvasSize / 2 - 1;

View File

@ -5,9 +5,10 @@ import type { Cell } from '../core/Cell';
import type { ColorIndex } from '../core/Palette'; import type { ColorIndex } from '../core/Palette';
import Palette from '../core/Palette'; import Palette from '../core/Palette';
import { import {
getMaxTiledZoom,
clamp, clamp,
getIdFromObject, getIdFromObject,
getHistoricalCanvasSize,
getMaxTiledZoom,
} from '../core/utils'; } from '../core/utils';
@ -25,7 +26,6 @@ export type CanvasState = {
selectedColor: ColorIndex, selectedColor: ColorIndex,
is3D: boolean, is3D: boolean,
canvasSize: number, canvasSize: number,
canvasMaxTiledZoom: number,
canvasStartDate: string, canvasStartDate: string,
palette: Palette, palette: Palette,
clrIgnore: number, clrIgnore: number,
@ -33,6 +33,7 @@ export type CanvasState = {
scale: number, scale: number,
viewscale: number, viewscale: number,
isHistoricalView: boolean, isHistoricalView: boolean,
historicalCanvasSize: number,
historicalDate: string, historicalDate: string,
historicalTime: string, historicalTime: string,
// object with all canvas informations from all canvases like colors and size // object with all canvas informations from all canvases like colors and size
@ -104,6 +105,7 @@ function getViewFromURL(canvases: Object) {
canvasId, canvasId,
canvasIdent, canvasIdent,
canvasSize, canvasSize,
historicalCanvasSize: canvasSize,
is3D, is3D,
canvasStartDate, canvasStartDate,
canvasMaxTiledZoom: getMaxTiledZoom(canvasSize), canvasMaxTiledZoom: getMaxTiledZoom(canvasSize),
@ -121,6 +123,7 @@ function getViewFromURL(canvases: Object) {
canvasId: DEFAULT_CANVAS_ID, canvasId: DEFAULT_CANVAS_ID,
canvasIdent: canvasd.ident, canvasIdent: canvasd.ident,
canvasSize: canvasd.size, canvasSize: canvasd.size,
historicalCanvasSize: canvasd.size,
is3D: !!canvasd.v, is3D: !!canvasd.v,
canvasStartDate: null, canvasStartDate: null,
canvasMaxTiledZoom: getMaxTiledZoom(canvasd.size), canvasMaxTiledZoom: getMaxTiledZoom(canvasd.size),
@ -130,6 +133,7 @@ function getViewFromURL(canvases: Object) {
view: getGivenCoords(), view: getGivenCoords(),
viewscale: DEFAULT_SCALE, viewscale: DEFAULT_SCALE,
scale: DEFAULT_SCALE, scale: DEFAULT_SCALE,
canvases,
}; };
} }
} }
@ -154,10 +158,13 @@ export default function canvasReducer(
viewscale, viewscale,
} = state; } = state;
const { const {
canvasSize,
isHistoricalView, isHistoricalView,
} = state; } = state;
const canvasSize = (isHistoricalView)
? state.historicalCanvasSize
: state.canvasSize;
let [hx, hy] = view; let [hx, hy] = view;
let { scale } = action; let { scale } = action;
const { zoompoint } = action; const { zoompoint } = action;
@ -192,8 +199,18 @@ export default function canvasReducer(
date, date,
time, time,
} = action; } = action;
const {
canvasSize,
canvases,
canvasId,
} = state;
const historicalCanvasSize = getHistoricalCanvasSize(
date, canvasSize, canvases[canvasId].historicalSizes,
);
return { return {
...state, ...state,
historicalCanvasSize,
historicalDate: date, historicalDate: date,
historicalTime: time, historicalTime: time,
}; };
@ -221,7 +238,9 @@ export default function canvasReducer(
case 'SET_VIEW_COORDINATES': { case 'SET_VIEW_COORDINATES': {
const { view } = action; const { view } = action;
const { canvasSize } = state; const canvasSize = (state.isHistoricalView)
? state.historicalCanvasSize
: state.canvasSize;
const canvasMinXY = -canvasSize / 2; const canvasMinXY = -canvasSize / 2;
const canvasMaxXY = canvasSize / 2 - 1; const canvasMaxXY = canvasSize / 2 - 1;
const newview = view.map((z) => clamp(z, canvasMinXY, canvasMaxXY)); const newview = view.map((z) => clamp(z, canvasMinXY, canvasMaxXY));
@ -249,7 +268,7 @@ export default function canvasReducer(
case 'SELECT_CANVAS': { case 'SELECT_CANVAS': {
let { canvasId } = action; let { canvasId } = action;
const { canvases, isHistoricalView } = state; const { canvases } = state;
let canvas = canvases[canvasId]; let canvas = canvases[canvasId];
if (!canvas) { if (!canvas) {
@ -264,7 +283,12 @@ export default function canvasReducer(
cli: clrIgnore, cli: clrIgnore,
colors, colors,
} = canvas; } = canvas;
const canvasMaxTiledZoom = getMaxTiledZoom(canvasSize); const isHistoricalView = !is3D && state.isHistoricalView;
const historicalCanvasSize = getHistoricalCanvasSize(
state.historicalDate,
canvasSize,
canvas.historicalSizes,
);
const palette = new Palette(colors, 0); const palette = new Palette(colors, 0);
const view = (canvasId === 0) ? getGivenCoords() : [0, 0, 0]; const view = (canvasId === 0) ? getGivenCoords() : [0, 0, 0];
if (!is3D) { if (!is3D) {
@ -278,13 +302,13 @@ export default function canvasReducer(
canvasSize, canvasSize,
is3D, is3D,
canvasStartDate, canvasStartDate,
canvasMaxTiledZoom,
palette, palette,
clrIgnore, clrIgnore,
view, view,
viewscale: DEFAULT_SCALE, viewscale: DEFAULT_SCALE,
scale: DEFAULT_SCALE, scale: DEFAULT_SCALE,
isHistoricalView: !is3D && isHistoricalView, isHistoricalView,
historicalCanvasSize,
}; };
} }
@ -304,7 +328,6 @@ export default function canvasReducer(
cli: clrIgnore, cli: clrIgnore,
colors, colors,
} = canvases[canvasId]; } = canvases[canvasId];
const canvasMaxTiledZoom = getMaxTiledZoom(canvasSize);
const palette = new Palette(colors, 0); const palette = new Palette(colors, 0);
if (!is3D) { if (!is3D) {
@ -319,7 +342,6 @@ export default function canvasReducer(
canvasSize, canvasSize,
is3D, is3D,
canvasStartDate, canvasStartDate,
canvasMaxTiledZoom,
palette, palette,
clrIgnore, clrIgnore,
canvases, canvases,

View File

@ -15,7 +15,14 @@ export default (store) => (next) => (action) => {
if (type === 'SET_HISTORICAL_TIME') { if (type === 'SET_HISTORICAL_TIME') {
const state = store.getState(); const state = store.getState();
const renderer = getRenderer(); const renderer = getRenderer();
renderer.updateOldHistoricalTime(state.canvas.historicalTime); const {
historicalDate: oldDate,
historicalTime: oldTime,
} = state.canvas;
renderer.updateOldHistoricalTime(
oldDate,
oldTime,
);
} }
// executed after reducers // executed after reducers
@ -37,7 +44,22 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'SET_HISTORICAL_TIME': case 'SET_HISTORICAL_TIME': {
const {
historicalDate,
historicalTime,
historicalCanvasSize,
} = state.canvas;
const renderer = getRenderer();
renderer.updateHistoricalTime(
historicalDate,
historicalTime,
historicalCanvasSize,
);
renderer.forceNextRender = true;
break;
}
case 'REQUEST_BIG_CHUNK': case 'REQUEST_BIG_CHUNK':
case 'PRE_LOADED_BIG_CHUNK': case 'PRE_LOADED_BIG_CHUNK':
case 'RECEIVE_BIG_CHUNK': case 'RECEIVE_BIG_CHUNK':
@ -55,14 +77,8 @@ export default (store) => (next) => (action) => {
case 'TOGGLE_HISTORICAL_VIEW': case 'TOGGLE_HISTORICAL_VIEW':
case 'SET_SCALE': { case 'SET_SCALE': {
const {
viewscale,
canvasMaxTiledZoom,
view,
canvasSize,
} = state.canvas;
const renderer = getRenderer(); const renderer = getRenderer();
renderer.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize); renderer.updateScale(state);
break; break;
} }
@ -79,9 +95,8 @@ export default (store) => (next) => (action) => {
} }
case 'SET_VIEW_COORDINATES': { case 'SET_VIEW_COORDINATES': {
const { view, canvasSize } = state.canvas;
const renderer = getRenderer(); const renderer = getRenderer();
renderer.updateView(view, canvasSize); renderer.updateView(state);
break; break;
} }

View File

@ -17,28 +17,37 @@ import {
// preLoadedBigChunk, // preLoadedBigChunk,
} from '../actions'; } from '../actions';
import { import {
getMaxTiledZoom,
getCellInsideChunk, getCellInsideChunk,
getChunkOfPixel, getChunkOfPixel,
getHistoricalCanvasSize,
} from '../core/utils'; } from '../core/utils';
class ChunkLoader { class ChunkLoader {
store = null; store = null;
canvasId: number; canvasId: number;
canvasMaxTiledZoom: number; canvasMaxTiledZoom: number;
historicalMaxTiledZooms: Array;
palette; palette;
canvasSize: number;
chunks: Map<string, ChunkRGB>; chunks: Map<string, ChunkRGB>;
constructor(store) { constructor(store, canvasId, palette, canvasSize, historicalSizes) {
this.store = store; this.store = store;
const state = store.getState();
const {
canvasId,
canvasMaxTiledZoom,
palette,
} = state.canvas;
this.canvasId = canvasId; this.canvasId = canvasId;
this.canvasMaxTiledZoom = canvasMaxTiledZoom;
this.palette = palette; this.palette = palette;
this.canvasSize = canvasSize;
this.canvasMaxTiledZoom = getMaxTiledZoom(canvasSize);
if (historicalSizes) {
this.historicalMaxTiledZooms = historicalSizes.map((ts) => {
const [date, size] = ts;
return [date, getMaxTiledZoom(size)];
});
} else {
this.historicalMaxTiledZooms = [];
}
this.chunks = new Map(); this.chunks = new Map();
} }
@ -64,8 +73,7 @@ class ChunkLoader {
x: number, x: number,
y: number, y: number,
) { ) {
const state: State = this.store.getState(); const { canvasSize } = this;
const { canvasSize } = state.canvas;
const [cx, cy] = getChunkOfPixel(canvasSize, x, y); const [cx, cy] = getChunkOfPixel(canvasSize, x, y);
const key = `${this.canvasMaxTiledZoom}:${cx}:${cy}`; const key = `${this.canvasMaxTiledZoom}:${cx}:${cy}`;
const chunk = this.chunks.get(key); const chunk = this.chunks.get(key);
@ -79,15 +87,17 @@ class ChunkLoader {
/* /*
* Get color of pixel in current historical view * Get color of pixel in current historical view
* (has to account for canvs size changes in the past
* @param x, y world coordiantes of pixel * @param x, y world coordiantes of pixel
* @return ColorIndex or null if chunks not loaded or historical view not set * @return ColorIndex or null if chunks not loaded or historical view not set
*/ */
getHistoricalIndexOfPixel( getHistoricalIndexOfPixel(
x: number, x: number,
y: number, y: number,
historicalDate: string,
historicalTime: string,
) { ) {
const state: State = this.store.getState(); const { canvasSize } = this;
const { canvasSize, historicalDate, historicalTime } = state.canvas;
if (!historicalDate) { if (!historicalDate) {
return null; return null;
} }
@ -208,10 +218,15 @@ class ChunkLoader {
} }
return (historicalTime) ? null : loadingTiles.getTile(canvasId); return (historicalTime) ? null : loadingTiles.getTile(canvasId);
} if (fetch) { } if (fetch) {
const historicalCanvasMaxTiledZoom = getHistoricalCanvasSize(
historicalDate,
this.canvasMaxTiledZoom,
this.historicalMaxTiledZooms,
);
// fetch tile // fetch tile
const chunkRGB = new ChunkRGB( const chunkRGB = new ChunkRGB(
this.palette, this.palette,
this.canvasMaxTiledZoom, historicalCanvasMaxTiledZoom,
cx, cx,
cy, cy,
); );
@ -221,6 +236,7 @@ class ChunkLoader {
cy, cy,
historicalDate, historicalDate,
historicalTime, historicalTime,
historicalCanvasMaxTiledZoom,
chunkRGB, chunkRGB,
); );
} }
@ -232,10 +248,12 @@ class ChunkLoader {
cy: number, cy: number,
historicalDate: string, historicalDate: string,
historicalTime: string, historicalTime: string,
historicalCanvasMaxTiledZoom,
chunkRGB, chunkRGB,
) { ) {
const { canvasId, canvasMaxTiledZoom } = this; const { canvasId } = this;
const center = [canvasMaxTiledZoom, cx, cy];
const center = [historicalCanvasMaxTiledZoom, cx, cy];
let url = `${window.ssv.backupurl}/${historicalDate}/`; let url = `${window.ssv.backupurl}/${historicalDate}/`;
if (historicalTime) { if (historicalTime) {
// incremential tiles // incremential tiles

View File

@ -23,15 +23,11 @@ class ChunkLoader {
palette; palette;
chunks: Map<string, Chunk>; chunks: Map<string, Chunk>;
constructor(store) { constructor(store, canvasId, palette, canvasSize) {
this.store = store; this.store = store;
const state = store.getState();
const {
canvasId,
palette,
} = state.canvas;
this.canvasId = canvasId; this.canvasId = canvasId;
this.palette = palette; this.palette = palette;
this.canvasSize = canvasSize;
this.chunks = new Map(); this.chunks = new Map();
} }
@ -43,10 +39,7 @@ class ChunkLoader {
} }
getVoxel(x: number, y: number, z: number) { getVoxel(x: number, y: number, z: number) {
const state = this.store.getState(); const { canvasSize } = this;
const {
canvasSize,
} = state.canvas;
const [xc, zc] = getChunkOfPixel(canvasSize, x, y, z); const [xc, zc] = getChunkOfPixel(canvasSize, x, y, z);
const offset = getOffsetOfPixel(canvasSize, x, y, z); const offset = getOffsetOfPixel(canvasSize, x, y, z);
const key = `${xc}:${zc}`; const key = `${xc}:${zc}`;

View File

@ -11,6 +11,7 @@ import { TILE_SIZE } from '../core/constants';
import { import {
getTileOfPixel, getTileOfPixel,
getPixelFromChunkOffset, getPixelFromChunkOffset,
getMaxTiledZoom,
} from '../core/utils'; } from '../core/utils';
import { import {
@ -58,6 +59,8 @@ class Renderer {
this.centerChunk = [null, null]; this.centerChunk = [null, null];
this.tiledScale = 0; this.tiledScale = 0;
this.tiledZoom = 4; this.tiledZoom = 4;
this.canvasMaxTiledZoom = 0;
this.historicalCanvasMaxTiledZoom = 0;
this.hover = false; this.hover = false;
//-- //--
this.forceNextRender = true; this.forceNextRender = true;
@ -110,70 +113,100 @@ class Renderer {
setStore(store) { setStore(store) {
this.store = store; this.store = store;
const state = store.getState(); const state = store.getState();
const {
canvasMaxTiledZoom,
viewscale,
view,
canvasSize,
} = state.canvas;
this.updateCanvasData(state); this.updateCanvasData(state);
this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize); this.updateScale(state);
this.controls = new PixelPainterControls(this, this.viewport, store); this.controls = new PixelPainterControls(this, this.viewport, store);
} }
updateCanvasData(state: State) { updateCanvasData(state: State) {
const { const {
canvasMaxTiledZoom,
viewscale,
view,
canvasSize,
canvasId, canvasId,
} = state.canvas; } = state.canvas;
if (canvasId !== this.canvasId) { if (canvasId !== this.canvasId) {
this.canvasId = canvasId; this.canvasId = canvasId;
if (canvasId !== null) { if (canvasId !== null) {
this.chunkLoader = new ChunkLoader(this.store); const {
palette,
canvasSize,
canvases,
} = state.canvas;
this.canvasMaxTiledZoom = getMaxTiledZoom(canvasSize);
this.chunkLoader = new ChunkLoader(
this.store,
canvasId,
palette,
canvasSize,
canvases[canvasId].historicalSizes,
);
} }
} }
this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize); this.updateScale(state);
} }
updateOldHistoricalTime(historicalTime: string) { updateOldHistoricalTime(oldDate, oldTime) {
if (historicalTime === '0000') { if (oldTime === '0000') {
this.oldHistoricalTime = null; this.oldHistoricalTime = null;
} else { } else {
this.oldHistoricalTime = historicalTime; this.oldHistoricalTime = oldTime;
} }
} }
updateHistoricalTime(historicalDate, historicalTime, historicalCanvasSize) {
this.historicalCanvasMaxTiledZoom = getMaxTiledZoom(
historicalCanvasSize,
);
}
getColorIndexOfPixel(cx, cy, historical: boolean = false) { getColorIndexOfPixel(cx, cy, historical: boolean = false) {
return (historical) if (historical) {
? this.chunkLoader.getHistoricalIndexOfPixel(cx, cy) const state = this.store.getState();
: this.chunkLoader.getColorIndexOfPixel(cx, cy); const {
historicalDate,
historicalTime,
} = state.canvas;
return this.chunkLoader.getHistoricalIndexOfPixel(cx, cy,
historicalDate, historicalTime);
}
return this.chunkLoader.getColorIndexOfPixel(cx, cy);
} }
updateScale( updateScale(
viewscale: number, state,
canvasMaxTiledZoom: number,
view,
canvasSize,
) { ) {
const {
viewscale,
view,
isHistoricalView,
} = state.canvas;
pixelNotify.updateScale(viewscale); pixelNotify.updateScale(viewscale);
let tiledScale = (viewscale > 0.5) let tiledScale = (viewscale > 0.5)
? 0 ? 0
: Math.round(Math.log2(viewscale) / 2); : Math.round(Math.log2(viewscale) / 2);
tiledScale = 4 ** tiledScale; tiledScale = 4 ** tiledScale;
const canvasSize = (isHistoricalView)
? state.canvas.historicalCanvasSize
: state.canvas.canvasSize;
const canvasMaxTiledZoom = (isHistoricalView)
? this.historicalCanvasMaxTiledZoom
: this.canvasMaxTiledZoom;
const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale) / 2; const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale) / 2;
const relScale = viewscale / tiledScale; const relScale = viewscale / tiledScale;
this.tiledScale = tiledScale; this.tiledScale = tiledScale;
this.tiledZoom = tiledZoom; this.tiledZoom = tiledZoom;
this.relScale = relScale; this.relScale = relScale;
this.updateView(view, canvasSize); this.updateView(state);
this.forceNextRender = true; this.forceNextRender = true;
} }
updateView(view, canvasSize) { updateView(state) {
const {
view,
} = state.canvas;
const canvasSize = (state.canvas.isHistoricalView)
? state.canvas.historicalCanvasSize
: state.canvas.canvasSize;
const [x, y] = view; const [x, y] = view;
let [cx, cy] = this.centerChunk; let [cx, cy] = this.centerChunk;
const [curcx, curcy] = getTileOfPixel( const [curcx, curcy] = getTileOfPixel(
@ -506,9 +539,9 @@ class Renderer {
} = this; } = this;
const { const {
viewscale, viewscale,
canvasSize,
historicalDate, historicalDate,
historicalTime, historicalTime,
historicalCanvasSize,
} = state.canvas; } = state.canvas;
@ -575,7 +608,7 @@ class Renderer {
const x = xOffset + dx * TILE_SIZE; const x = xOffset + dx * TILE_SIZE;
const y = yOffset + dy * TILE_SIZE; const y = yOffset + dy * TILE_SIZE;
const chunkMaxXY = canvasSize / TILE_SIZE; const chunkMaxXY = historicalCanvasSize / TILE_SIZE;
if (cx < 0 || cx >= chunkMaxXY || cy < 0 || cy >= chunkMaxXY) { if (cx < 0 || cx >= chunkMaxXY || cy < 0 || cy >= chunkMaxXY) {
// if out of bounds // if out of bounds
context.fillRect(x, y, TILE_SIZE, TILE_SIZE); context.fillRect(x, y, TILE_SIZE, TILE_SIZE);
@ -638,7 +671,7 @@ class Renderer {
const { const {
view, view,
viewscale, viewscale,
canvasSize, historicalCanvasSize,
} = state.canvas; } = state.canvas;
const [x, y] = view; const [x, y] = view;
@ -663,7 +696,7 @@ class Renderer {
viewportCtx.imageSmoothingEnabled = false; viewportCtx.imageSmoothingEnabled = false;
// If scale is so large that neighbouring chunks wouldn't fit in offscreen // If scale is so large that neighbouring chunks wouldn't fit in offscreen
// canvas, do scale = 1 in renderChunks and scale in render() // canvas, do scale = 1 in renderChunks and scale in render()
const canvasCenter = canvasSize / 2; const canvasCenter = historicalCanvasSize / 2;
if (viewscale > SCALE_THREASHOLD) { if (viewscale > SCALE_THREASHOLD) {
viewportCtx.save(); viewportCtx.save();
viewportCtx.scale(viewscale, viewscale); viewportCtx.scale(viewscale, viewscale);

View File

@ -249,7 +249,16 @@ class Renderer {
} }
} }
this.loadedChunks = new Map(); this.loadedChunks = new Map();
this.chunkLoader = new ChunkLoader(this.store); const {
palette,
canvasSize,
} = state.canvas;
this.chunkLoader = new ChunkLoader(
this.store,
canvasId,
palette,
canvasSize,
);
} }
} }
this.controls.setView(view); this.controls.setView(view);