From 3d4ded29caeffe2b0a97a78f41f03c721a94e793 Mon Sep 17 00:00:00 2001 From: HF Date: Sun, 18 Apr 2021 16:47:14 +0200 Subject: [PATCH] allow canvases to change sizes in historical view --- src/canvases.json | 8 +++- src/core/utils.js | 18 ++++++++ src/reducers/canvas.js | 42 +++++++++++++----- src/store/rendererHook.js | 37 +++++++++++----- src/ui/ChunkLoader2D.js | 48 ++++++++++++++------- src/ui/ChunkLoader3D.js | 13 ++---- src/ui/Renderer2D.js | 91 ++++++++++++++++++++++++++------------- src/ui/Renderer3D.js | 11 ++++- 8 files changed, 191 insertions(+), 77 deletions(-) diff --git a/src/canvases.json b/src/canvases.json index 1ae93cb..ddd2581 100644 --- a/src/canvases.json +++ b/src/canvases.json @@ -84,7 +84,13 @@ [ 122, 148, 156 ], [ 174, 215, 185 ] ], - "size" : 4096, + "size": 16384, + "historicalSizes": [ + [ + "20210417", + 4096 + ] + ], "hid": false, "cli": 2, "bcd": 15000, diff --git a/src/core/utils.js b/src/core/utils.js index a7faf99..c82b1f7 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -69,6 +69,24 @@ export function getMaxTiledZoom(canvasSize: number): number { 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 { const canvasMinXY = -canvasSize / 2; const canvasMaxXY = canvasSize / 2 - 1; diff --git a/src/reducers/canvas.js b/src/reducers/canvas.js index 457ec46..611853e 100644 --- a/src/reducers/canvas.js +++ b/src/reducers/canvas.js @@ -5,9 +5,10 @@ import type { Cell } from '../core/Cell'; import type { ColorIndex } from '../core/Palette'; import Palette from '../core/Palette'; import { - getMaxTiledZoom, clamp, getIdFromObject, + getHistoricalCanvasSize, + getMaxTiledZoom, } from '../core/utils'; @@ -25,7 +26,6 @@ export type CanvasState = { selectedColor: ColorIndex, is3D: boolean, canvasSize: number, - canvasMaxTiledZoom: number, canvasStartDate: string, palette: Palette, clrIgnore: number, @@ -33,6 +33,7 @@ export type CanvasState = { scale: number, viewscale: number, isHistoricalView: boolean, + historicalCanvasSize: number, historicalDate: string, historicalTime: string, // object with all canvas informations from all canvases like colors and size @@ -104,6 +105,7 @@ function getViewFromURL(canvases: Object) { canvasId, canvasIdent, canvasSize, + historicalCanvasSize: canvasSize, is3D, canvasStartDate, canvasMaxTiledZoom: getMaxTiledZoom(canvasSize), @@ -121,6 +123,7 @@ function getViewFromURL(canvases: Object) { canvasId: DEFAULT_CANVAS_ID, canvasIdent: canvasd.ident, canvasSize: canvasd.size, + historicalCanvasSize: canvasd.size, is3D: !!canvasd.v, canvasStartDate: null, canvasMaxTiledZoom: getMaxTiledZoom(canvasd.size), @@ -130,6 +133,7 @@ function getViewFromURL(canvases: Object) { view: getGivenCoords(), viewscale: DEFAULT_SCALE, scale: DEFAULT_SCALE, + canvases, }; } } @@ -154,10 +158,13 @@ export default function canvasReducer( viewscale, } = state; const { - canvasSize, isHistoricalView, } = state; + const canvasSize = (isHistoricalView) + ? state.historicalCanvasSize + : state.canvasSize; + let [hx, hy] = view; let { scale } = action; const { zoompoint } = action; @@ -192,8 +199,18 @@ export default function canvasReducer( date, time, } = action; + const { + canvasSize, + canvases, + canvasId, + } = state; + const historicalCanvasSize = getHistoricalCanvasSize( + date, canvasSize, canvases[canvasId].historicalSizes, + ); + return { ...state, + historicalCanvasSize, historicalDate: date, historicalTime: time, }; @@ -221,7 +238,9 @@ export default function canvasReducer( case 'SET_VIEW_COORDINATES': { const { view } = action; - const { canvasSize } = state; + 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)); @@ -249,7 +268,7 @@ export default function canvasReducer( case 'SELECT_CANVAS': { let { canvasId } = action; - const { canvases, isHistoricalView } = state; + const { canvases } = state; let canvas = canvases[canvasId]; if (!canvas) { @@ -264,7 +283,12 @@ export default function canvasReducer( cli: clrIgnore, colors, } = 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 view = (canvasId === 0) ? getGivenCoords() : [0, 0, 0]; if (!is3D) { @@ -278,13 +302,13 @@ export default function canvasReducer( canvasSize, is3D, canvasStartDate, - canvasMaxTiledZoom, palette, clrIgnore, view, viewscale: DEFAULT_SCALE, scale: DEFAULT_SCALE, - isHistoricalView: !is3D && isHistoricalView, + isHistoricalView, + historicalCanvasSize, }; } @@ -304,7 +328,6 @@ export default function canvasReducer( cli: clrIgnore, colors, } = canvases[canvasId]; - const canvasMaxTiledZoom = getMaxTiledZoom(canvasSize); const palette = new Palette(colors, 0); if (!is3D) { @@ -319,7 +342,6 @@ export default function canvasReducer( canvasSize, is3D, canvasStartDate, - canvasMaxTiledZoom, palette, clrIgnore, canvases, diff --git a/src/store/rendererHook.js b/src/store/rendererHook.js index 5b6d9ac..9651797 100644 --- a/src/store/rendererHook.js +++ b/src/store/rendererHook.js @@ -15,7 +15,14 @@ export default (store) => (next) => (action) => { if (type === 'SET_HISTORICAL_TIME') { const state = store.getState(); const renderer = getRenderer(); - renderer.updateOldHistoricalTime(state.canvas.historicalTime); + const { + historicalDate: oldDate, + historicalTime: oldTime, + } = state.canvas; + renderer.updateOldHistoricalTime( + oldDate, + oldTime, + ); } // executed after reducers @@ -37,7 +44,22 @@ export default (store) => (next) => (action) => { 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 'PRE_LOADED_BIG_CHUNK': case 'RECEIVE_BIG_CHUNK': @@ -55,14 +77,8 @@ export default (store) => (next) => (action) => { case 'TOGGLE_HISTORICAL_VIEW': case 'SET_SCALE': { - const { - viewscale, - canvasMaxTiledZoom, - view, - canvasSize, - } = state.canvas; const renderer = getRenderer(); - renderer.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize); + renderer.updateScale(state); break; } @@ -79,9 +95,8 @@ export default (store) => (next) => (action) => { } case 'SET_VIEW_COORDINATES': { - const { view, canvasSize } = state.canvas; const renderer = getRenderer(); - renderer.updateView(view, canvasSize); + renderer.updateView(state); break; } diff --git a/src/ui/ChunkLoader2D.js b/src/ui/ChunkLoader2D.js index 05251a9..4d3c334 100644 --- a/src/ui/ChunkLoader2D.js +++ b/src/ui/ChunkLoader2D.js @@ -17,28 +17,37 @@ import { // preLoadedBigChunk, } from '../actions'; import { + getMaxTiledZoom, getCellInsideChunk, getChunkOfPixel, + getHistoricalCanvasSize, } from '../core/utils'; class ChunkLoader { store = null; canvasId: number; canvasMaxTiledZoom: number; + historicalMaxTiledZooms: Array; palette; + canvasSize: number; chunks: Map; - constructor(store) { + constructor(store, canvasId, palette, canvasSize, historicalSizes) { this.store = store; - const state = store.getState(); - const { - canvasId, - canvasMaxTiledZoom, - palette, - } = state.canvas; this.canvasId = canvasId; - this.canvasMaxTiledZoom = canvasMaxTiledZoom; 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(); } @@ -64,8 +73,7 @@ class ChunkLoader { x: number, y: number, ) { - const state: State = this.store.getState(); - const { canvasSize } = state.canvas; + const { canvasSize } = this; const [cx, cy] = getChunkOfPixel(canvasSize, x, y); const key = `${this.canvasMaxTiledZoom}:${cx}:${cy}`; const chunk = this.chunks.get(key); @@ -79,15 +87,17 @@ class ChunkLoader { /* * 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 * @return ColorIndex or null if chunks not loaded or historical view not set */ getHistoricalIndexOfPixel( x: number, y: number, + historicalDate: string, + historicalTime: string, ) { - const state: State = this.store.getState(); - const { canvasSize, historicalDate, historicalTime } = state.canvas; + const { canvasSize } = this; if (!historicalDate) { return null; } @@ -208,10 +218,15 @@ class ChunkLoader { } return (historicalTime) ? null : loadingTiles.getTile(canvasId); } if (fetch) { + const historicalCanvasMaxTiledZoom = getHistoricalCanvasSize( + historicalDate, + this.canvasMaxTiledZoom, + this.historicalMaxTiledZooms, + ); // fetch tile const chunkRGB = new ChunkRGB( this.palette, - this.canvasMaxTiledZoom, + historicalCanvasMaxTiledZoom, cx, cy, ); @@ -221,6 +236,7 @@ class ChunkLoader { cy, historicalDate, historicalTime, + historicalCanvasMaxTiledZoom, chunkRGB, ); } @@ -232,10 +248,12 @@ class ChunkLoader { cy: number, historicalDate: string, historicalTime: string, + historicalCanvasMaxTiledZoom, chunkRGB, ) { - const { canvasId, canvasMaxTiledZoom } = this; - const center = [canvasMaxTiledZoom, cx, cy]; + const { canvasId } = this; + + const center = [historicalCanvasMaxTiledZoom, cx, cy]; let url = `${window.ssv.backupurl}/${historicalDate}/`; if (historicalTime) { // incremential tiles diff --git a/src/ui/ChunkLoader3D.js b/src/ui/ChunkLoader3D.js index b7bd6dd..17901c9 100644 --- a/src/ui/ChunkLoader3D.js +++ b/src/ui/ChunkLoader3D.js @@ -23,15 +23,11 @@ class ChunkLoader { palette; chunks: Map; - constructor(store) { + constructor(store, canvasId, palette, canvasSize) { this.store = store; - const state = store.getState(); - const { - canvasId, - palette, - } = state.canvas; this.canvasId = canvasId; this.palette = palette; + this.canvasSize = canvasSize; this.chunks = new Map(); } @@ -43,10 +39,7 @@ class ChunkLoader { } getVoxel(x: number, y: number, z: number) { - const state = this.store.getState(); - const { - canvasSize, - } = state.canvas; + const { canvasSize } = this; const [xc, zc] = getChunkOfPixel(canvasSize, x, y, z); const offset = getOffsetOfPixel(canvasSize, x, y, z); const key = `${xc}:${zc}`; diff --git a/src/ui/Renderer2D.js b/src/ui/Renderer2D.js index 96d277a..9876f2e 100644 --- a/src/ui/Renderer2D.js +++ b/src/ui/Renderer2D.js @@ -11,6 +11,7 @@ import { TILE_SIZE } from '../core/constants'; import { getTileOfPixel, getPixelFromChunkOffset, + getMaxTiledZoom, } from '../core/utils'; import { @@ -58,6 +59,8 @@ class Renderer { this.centerChunk = [null, null]; this.tiledScale = 0; this.tiledZoom = 4; + this.canvasMaxTiledZoom = 0; + this.historicalCanvasMaxTiledZoom = 0; this.hover = false; //-- this.forceNextRender = true; @@ -110,70 +113,100 @@ class Renderer { setStore(store) { this.store = store; const state = store.getState(); - const { - canvasMaxTiledZoom, - viewscale, - view, - canvasSize, - } = state.canvas; this.updateCanvasData(state); - this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize); + this.updateScale(state); this.controls = new PixelPainterControls(this, this.viewport, store); } updateCanvasData(state: State) { const { - canvasMaxTiledZoom, - viewscale, - view, - canvasSize, canvasId, } = state.canvas; if (canvasId !== this.canvasId) { this.canvasId = canvasId; 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) { - if (historicalTime === '0000') { + updateOldHistoricalTime(oldDate, oldTime) { + if (oldTime === '0000') { this.oldHistoricalTime = null; } else { - this.oldHistoricalTime = historicalTime; + this.oldHistoricalTime = oldTime; } } + updateHistoricalTime(historicalDate, historicalTime, historicalCanvasSize) { + this.historicalCanvasMaxTiledZoom = getMaxTiledZoom( + historicalCanvasSize, + ); + } + getColorIndexOfPixel(cx, cy, historical: boolean = false) { - return (historical) - ? this.chunkLoader.getHistoricalIndexOfPixel(cx, cy) - : this.chunkLoader.getColorIndexOfPixel(cx, cy); + if (historical) { + const state = this.store.getState(); + const { + historicalDate, + historicalTime, + } = state.canvas; + return this.chunkLoader.getHistoricalIndexOfPixel(cx, cy, + historicalDate, historicalTime); + } + return this.chunkLoader.getColorIndexOfPixel(cx, cy); } updateScale( - viewscale: number, - canvasMaxTiledZoom: number, - view, - canvasSize, + state, ) { + const { + viewscale, + view, + isHistoricalView, + } = state.canvas; pixelNotify.updateScale(viewscale); let tiledScale = (viewscale > 0.5) ? 0 : Math.round(Math.log2(viewscale) / 2); 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 relScale = viewscale / tiledScale; this.tiledScale = tiledScale; this.tiledZoom = tiledZoom; this.relScale = relScale; - this.updateView(view, canvasSize); + this.updateView(state); 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; let [cx, cy] = this.centerChunk; const [curcx, curcy] = getTileOfPixel( @@ -506,9 +539,9 @@ class Renderer { } = this; const { viewscale, - canvasSize, historicalDate, historicalTime, + historicalCanvasSize, } = state.canvas; @@ -575,7 +608,7 @@ class Renderer { const x = xOffset + dx * 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 out of bounds context.fillRect(x, y, TILE_SIZE, TILE_SIZE); @@ -638,7 +671,7 @@ class Renderer { const { view, viewscale, - canvasSize, + historicalCanvasSize, } = state.canvas; const [x, y] = view; @@ -663,7 +696,7 @@ class Renderer { viewportCtx.imageSmoothingEnabled = false; // If scale is so large that neighbouring chunks wouldn't fit in offscreen // canvas, do scale = 1 in renderChunks and scale in render() - const canvasCenter = canvasSize / 2; + const canvasCenter = historicalCanvasSize / 2; if (viewscale > SCALE_THREASHOLD) { viewportCtx.save(); viewportCtx.scale(viewscale, viewscale); diff --git a/src/ui/Renderer3D.js b/src/ui/Renderer3D.js index 50494b2..4d6b70d 100644 --- a/src/ui/Renderer3D.js +++ b/src/ui/Renderer3D.js @@ -249,7 +249,16 @@ class Renderer { } } 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);