14 Commits

8 changed files with 189 additions and 144 deletions
+36 -52
View File
@@ -7260,7 +7260,7 @@
let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected)); let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected));
let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected)); let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected));
if (this.element.offsetWidth + resizeW / this.scale > this.width * 0.3 && this.element.offsetHeight + resizeH / this.scale > this.height * 0.3) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale }); if ((this.element.offsetWidth + resizeW) / this.scale > this.width * 0.5 / this.scale && (this.element.offsetHeight + resizeH) / this.scale > this.height * 0.3 / this.scale) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale });
this.oldX = e.clientX; this.oldX = e.clientX;
this.oldY = e.clientY; this.oldY = e.clientY;
@@ -7805,7 +7805,7 @@
/* globals PIXI, console*/ /* globals PIXI, console*/
const registeredTiles = new Map(); const registeredTiles = new Map();
const pendingTiles = new Map(); // const pendingTiles = new Map()
/** Implements a baseTexture cache. The last textures are kept for reuse */ /** Implements a baseTexture cache. The last textures are kept for reuse */
let keepTextures = 0; let keepTextures = 0;
const keptTextures = []; const keptTextures = [];
@@ -7840,14 +7840,35 @@
* @param {*} url * @param {*} url
* @memberof Tile * @memberof Tile
*/ */
static schedule(url) { /* static schedule(url) {
let count = 0; let count = 0
if (pendingTiles.has(url)) { if (pendingTiles.has(url)) {
count = pendingTiles.get(url); count = pendingTiles.get(url)
} }
pendingTiles.set(url, count + 1); pendingTiles.set(url, count + 1)
// console.log("Tile.scheduled", url, pendingTiles.size) // console.log("Tile.scheduled", url, pendingTiles.size)
} */
/**
* Removes the given url from pending urls.
*
* @static
* @param {*} url
* @memberof Tile
*/
/* static unschedule(url) {
if (pendingTiles.has(url)) {
let count = pendingTiles.get(url)
if (count > 1) {
pendingTiles.set(url, count - 1)
} }
else {
pendingTiles.clear(url)
}
}
// console.log("Tile.unscheduled", url, pendingTiles.size)
} */
/** /**
* Returns true iff the url is pending * Returns true iff the url is pending
@@ -7857,9 +7878,9 @@
* @returns * @returns
* @memberof Tile * @memberof Tile
*/ */
static isPending(url) { /*static isPending(url) {
return pendingTiles.has(url) && pendingTiles.get(url) > 0 return pendingTiles.has(url) && pendingTiles.get(url) > 0
} } */
static isObsolete(url) { static isObsolete(url) {
if (registeredTiles.has(url) && registeredTiles.get(url) > 0) { if (registeredTiles.has(url) && registeredTiles.get(url) > 0) {
@@ -7868,26 +7889,6 @@
return true return true
} }
/**
* Removes the given url from pending urls.
*
* @static
* @param {*} url
* @memberof Tile
*/
static unschedule(url) {
if (pendingTiles.has(url)) {
let count = pendingTiles.get(url);
if (count > 1) {
pendingTiles.set(url, count - 1);
}
else {
pendingTiles.clear(url);
}
}
// console.log("Tile.unscheduled", url, pendingTiles.size)
}
/** /**
* Loads a tile from image using the PIXI.Texture.fromImage method. * Loads a tile from image using the PIXI.Texture.fromImage method.
* *
@@ -7910,7 +7911,7 @@
* @memberof Tile * @memberof Tile
*/ */
register(url, debug = false) { register(url, debug = false) {
Tile.unschedule(url); //Tile.unschedule(url)
if (registeredTiles.has(url)) { if (registeredTiles.has(url)) {
let tiles = registeredTiles.get(url); let tiles = registeredTiles.get(url);
tiles.add(this); tiles.add(this);
@@ -7964,7 +7965,7 @@
} }
else { else {
// No longer registered and not pending // No longer registered and not pending
if (count <= 0 && !Tile.isPending(this.url)) { if (count <= 0) { // && !Tile.isPending(this.url)
let opts = { children: true, texture: true, baseTexture: true }; let opts = { children: true, texture: true, baseTexture: true };
super.destroy(opts); super.destroy(opts);
if (debug) console.log("Tile.destroy", registeredTiles.size, opts); if (debug) console.log("Tile.destroy", registeredTiles.size, opts);
@@ -8053,7 +8054,7 @@
if (this.loaded.has(url)) return false if (this.loaded.has(url)) return false
if (this.loading.has(url)) return false if (this.loading.has(url)) return false
Tile.schedule(url); //Tile.schedule(url)
this.map.set(url, [col, row]); this.map.set(url, [col, row]);
this.loading.add(url); this.loading.add(url);
this.loadQueue.push(url); this.loadQueue.push(url);
@@ -8063,7 +8064,7 @@
unschedule(url) { unschedule(url) {
if (this.loaded.has(url)) this.loaded.delete(url); if (this.loaded.has(url)) this.loaded.delete(url);
if (this.loading.has(url)) this.loading.delete(url); if (this.loading.has(url)) this.loading.delete(url);
Tile.unschedule(url); //Tile.unschedule(url)
this.loadQueue = this.loadQueue.filter(item => item != url); this.loadQueue = this.loadQueue.filter(item => item != url);
} }
@@ -8123,7 +8124,7 @@
if (this.loaded.has(url)) return false if (this.loaded.has(url)) return false
if (this.loading.has(url)) return false if (this.loading.has(url)) return false
Tile.schedule(url); //Tile.schedule(url)
let reusableTexture = Tile.textureAvailable(url); let reusableTexture = Tile.textureAvailable(url);
if (reusableTexture) { if (reusableTexture) {
if (this.debug) console.log('Texture reusable', reusableTexture); if (this.debug) console.log('Texture reusable', reusableTexture);
@@ -8646,9 +8647,6 @@
return n % 2 == 0 return n % 2 == 0
} }
function printTileCacheInfos() {
Tile.printInfos();
}
/** /**
* A utility class that holds information typically provided by DZI files, i.e. * A utility class that holds information typically provided by DZI files, i.e.
* height and width of the overall image, overlap, and image type. * height and width of the overall image, overlap, and image type.
@@ -9275,9 +9273,9 @@
} }
worldBounds() { worldBounds() {
let viewBounds = this.app.scene.bounds; // UO: Never use getBounds() let viewBounds = this.app.scene.bounds || this.app.scene.getBounds();
// Using getBounds extends visible scope after loading tiles and leads // Using getBounds extends visible scope after loading tiles and leads
// to excessive loading // to excessive loading. So we prefer bounds over getBounds()
if (this.world != null) { if (this.world != null) {
let bounds = this.world.bounds; let bounds = this.world.bounds;
let x = Math.max(-bounds.width, bounds.x); let x = Math.max(-bounds.width, bounds.x);
@@ -9722,7 +9720,6 @@
this.active = false; this.active = false;
this.destroyAllTiles(); this.destroyAllTiles();
this.tileContainer.destroy({ children: true }); this.tileContainer.destroy({ children: true });
printTileCacheInfos();
} }
throwFinished() { throwFinished() {
@@ -9732,20 +9729,7 @@
if (typeof currentTiles == 'undefined') { if (typeof currentTiles == 'undefined') {
return return
} }
this.ensureTiles(this.currentLevel); this.ensureTiles(this.currentLevel);
// let all = new Set()
// for (let tile of currentTiles.children) {
// all.add(tile.url)
// }
// let { centerCol, centerRow, needed } = this.neededTiles(currentTiles, this.currentLevel)
// for (let [url, col, row] of needed) {
// all.delete(url)
// }
// for (let url of all) {
// currentTiles.destroyTileByUrl(url)
// }
// currentTiles.loader.loader.reset()
} }
} }
+39 -1
View File
@@ -1,11 +1,17 @@
let ipc = null let ipc = null
let logMessages = new Set()
try { try {
ipc = require('electron').ipcRenderer ipc = require('electron').ipcRenderer
} catch (e) {} } catch (e) {
console.log("Cannot use electron logging.")
}
/** Basic class for app specific logging requirements. /** Basic class for app specific logging requirements.
* Can be used to implement persistent logging in electron apps. * Can be used to implement persistent logging in electron apps.
* Uses a logMessage cache to prevent error overflows. This is
* needed since errors may occur very frequently
* (e.g. display update loops at 60fps, programmatic loops, ...).
*/ */
export default class Logging { export default class Logging {
@@ -20,4 +26,36 @@ export default class Logging {
console.log(message) console.log(message)
} }
} }
/**
* Static warn function.
* Emits each warning only once per session.
* @param {*} message
*/
static warn(message) {
if (!logMessages.has(message)) {
logMessages.add(message)
if (ipc) {
ipc.send('warn', message)
} else {
console.warn(message)
}
}
}
/**
* Static error function.
* Emits each error message only once per session.
* @param {*} message
*/
static error(message) {
if (!logMessages.has(message)) {
logMessages.add(message)
if (ipc) {
ipc.send('error', message)
} else {
console.error(message)
}
}
}
} }
+2 -19
View File
@@ -7,9 +7,6 @@ function isEven(n) {
return n % 2 == 0 return n % 2 == 0
} }
function printTileCacheInfos() {
Tile.printInfos()
}
/** /**
* A utility class that holds information typically provided by DZI files, i.e. * A utility class that holds information typically provided by DZI files, i.e.
* height and width of the overall image, overlap, and image type. * height and width of the overall image, overlap, and image type.
@@ -636,9 +633,9 @@ export class DeepZoomImage extends PIXI.Container {
} }
worldBounds() { worldBounds() {
let viewBounds = this.app.scene.bounds // UO: Never use getBounds() let viewBounds = this.app.scene.bounds || this.app.scene.getBounds()
// Using getBounds extends visible scope after loading tiles and leads // Using getBounds extends visible scope after loading tiles and leads
// to excessive loading // to excessive loading. So we prefer bounds over getBounds()
if (this.world != null) { if (this.world != null) {
let bounds = this.world.bounds let bounds = this.world.bounds
let x = Math.max(-bounds.width, bounds.x) let x = Math.max(-bounds.width, bounds.x)
@@ -1083,7 +1080,6 @@ export class DeepZoomImage extends PIXI.Container {
this.active = false this.active = false
this.destroyAllTiles() this.destroyAllTiles()
this.tileContainer.destroy({ children: true }) this.tileContainer.destroy({ children: true })
printTileCacheInfos()
} }
throwFinished() { throwFinished() {
@@ -1093,19 +1089,6 @@ export class DeepZoomImage extends PIXI.Container {
if (typeof currentTiles == 'undefined') { if (typeof currentTiles == 'undefined') {
return return
} }
this.ensureTiles(this.currentLevel) this.ensureTiles(this.currentLevel)
// let all = new Set()
// for (let tile of currentTiles.children) {
// all.add(tile.url)
// }
// let { centerCol, centerRow, needed } = this.neededTiles(currentTiles, this.currentLevel)
// for (let [url, col, row] of needed) {
// all.delete(url)
// }
// for (let url of all) {
// currentTiles.destroyTileByUrl(url)
// }
// currentTiles.loader.loader.reset()
} }
} }
+3 -3
View File
@@ -29,7 +29,7 @@ export class TileLoader {
if (this.loaded.has(url)) return false if (this.loaded.has(url)) return false
if (this.loading.has(url)) return false if (this.loading.has(url)) return false
Tile.schedule(url) //Tile.schedule(url)
this.map.set(url, [col, row]) this.map.set(url, [col, row])
this.loading.add(url) this.loading.add(url)
this.loadQueue.push(url) this.loadQueue.push(url)
@@ -39,7 +39,7 @@ export class TileLoader {
unschedule(url) { unschedule(url) {
if (this.loaded.has(url)) this.loaded.delete(url) if (this.loaded.has(url)) this.loaded.delete(url)
if (this.loading.has(url)) this.loading.delete(url) if (this.loading.has(url)) this.loading.delete(url)
Tile.unschedule(url) //Tile.unschedule(url)
this.loadQueue = this.loadQueue.filter(item => item != url) this.loadQueue = this.loadQueue.filter(item => item != url)
} }
@@ -99,7 +99,7 @@ export class PIXITileLoader extends TileLoader {
if (this.loaded.has(url)) return false if (this.loaded.has(url)) return false
if (this.loading.has(url)) return false if (this.loading.has(url)) return false
Tile.schedule(url) //Tile.schedule(url)
let reusableTexture = Tile.textureAvailable(url) let reusableTexture = Tile.textureAvailable(url)
if (reusableTexture) { if (reusableTexture) {
if (this.debug) console.log('Texture reusable', reusableTexture) if (this.debug) console.log('Texture reusable', reusableTexture)
+55 -43
View File
@@ -2,10 +2,12 @@
/* globals PIXI, console*/ /* globals PIXI, console*/
const registeredTiles = new Map() const registeredTiles = new Map()
const pendingTiles = new Map()
/** Implements a baseTexture cache. The last textures are kept for reuse */ /** Implements a baseTexture cache. The last textures are kept for reuse */
let keepTextures = 0 let keepTextures = 0
const keptTextures = [] const keptTextures = []
const lateTextures = new Map()
let lastSweepTime = 0
let sweepInterval = 2000.0
/** The current Tile implementation simply uses PIXI.Sprites. /** The current Tile implementation simply uses PIXI.Sprites.
* *
@@ -30,22 +32,6 @@ export default class Tile extends PIXI.Sprite {
keepTextures = value keepTextures = value
} }
/**
* Marks the given url as pending. Pending tiles should not be destroyed
*
* @static
* @param {*} url
* @memberof Tile
*/
static schedule(url) {
let count = 0
if (pendingTiles.has(url)) {
count = pendingTiles.get(url)
}
pendingTiles.set(url, count + 1)
// console.log("Tile.scheduled", url, pendingTiles.size)
}
/** /**
* Returns true iff the url is pending * Returns true iff the url is pending
* *
@@ -54,9 +40,9 @@ export default class Tile extends PIXI.Sprite {
* @returns * @returns
* @memberof Tile * @memberof Tile
*/ */
static isPending(url) { /*static isPending(url) {
return pendingTiles.has(url) && pendingTiles.get(url) > 0 return pendingTiles.has(url) && pendingTiles.get(url) > 0
} } */
static isObsolete(url) { static isObsolete(url) {
if (registeredTiles.has(url) && registeredTiles.get(url) > 0) { if (registeredTiles.has(url) && registeredTiles.get(url) > 0) {
@@ -65,26 +51,6 @@ export default class Tile extends PIXI.Sprite {
return true return true
} }
/**
* Removes the given url from pending urls.
*
* @static
* @param {*} url
* @memberof Tile
*/
static unschedule(url) {
if (pendingTiles.has(url)) {
let count = pendingTiles.get(url)
if (count > 1) {
pendingTiles.set(url, count - 1)
}
else {
pendingTiles.clear(url)
}
}
// console.log("Tile.unscheduled", url, pendingTiles.size)
}
/** /**
* Loads a tile from image using the PIXI.Texture.fromImage method. * Loads a tile from image using the PIXI.Texture.fromImage method.
* *
@@ -107,7 +73,7 @@ export default class Tile extends PIXI.Sprite {
* @memberof Tile * @memberof Tile
*/ */
register(url, debug = false) { register(url, debug = false) {
Tile.unschedule(url) //Tile.unschedule(url)
if (registeredTiles.has(url)) { if (registeredTiles.has(url)) {
let tiles = registeredTiles.get(url) let tiles = registeredTiles.get(url)
tiles.add(this) tiles.add(this)
@@ -161,7 +127,7 @@ export default class Tile extends PIXI.Sprite {
} }
else { else {
// No longer registered and not pending // No longer registered and not pending
if (count <= 0 && !Tile.isPending(this.url)) { if (count <= 0) { // && !Tile.isPending(this.url)
let opts = { children: true, texture: true, baseTexture: true } let opts = { children: true, texture: true, baseTexture: true }
super.destroy(opts) super.destroy(opts)
if (debug) console.log("Tile.destroy", registeredTiles.size, opts) if (debug) console.log("Tile.destroy", registeredTiles.size, opts)
@@ -197,14 +163,60 @@ export default class Tile extends PIXI.Sprite {
return null return null
} }
/**
* Specialized renderWebGL to avoid freezing system
*
* @param {*} renderer
* @memberof Tile
*/
renderWebGL(renderer) {
try {
super.renderWebGL(renderer)
}
catch (e) {
// We want persistent logging here
Logging.error("Error in Tile.renderWebGL: " + e.message)
}
}
/**
* Removes lately arrived texture if they have not been touched in the meanwhile.
*
* @static
* @memberof Tile
*/
static sweepLateTextures() {
let now = performance.now()
if (now > lastSweepTime + sweepInterval) {
lastSweepTime = now
let count = 0
for (let [url, texture] of lateTextures.entries()) {
if (texture) {
let base = texture.baseTexture
if (base != null && base.touched == 0) {
texture.destroy(true)
//console.info("Sweeping ", url)
count += 1
lateTextures.delete(url)
}
}
}
if (count > 0)
console.log("Sweeping textures", count)
}
}
/** /**
* Texture received too late. We do not need it. * Texture received too late. We do not need it.
* @param {*} url * @param {*} url
* @param {*} texture * @param {*} texture
*/ */
static lateTexture(url, texture) { static lateTexture(url, texture) {
let destroyBase = !registeredTiles.has(url) lateTextures.set(url, texture)
texture.destroy(destroyBase) //console.warn("Tile.lateTexture")
// We cannot destroy the texture since we got errors in t.renderWebGL.
// Perhaps we can destroy unsed textures later on
Tile.sweepLateTextures()
} }
static printInfos() { static printInfos() {
+39 -11
View File
@@ -1,6 +1,6 @@
import { Cycle, Colors, Dates, isEmpty } from '../utils.js' import { Cycle, Colors, Dates, isEmpty } from '../utils.js'
import { Capabilities } from '../capabilities.js' import { Capabilities } from '../capabilities.js'
import { BitmapLabeledGraphics, LabeledGraphics, FontInfo } from './labeledgraphics.js' import { BitmapLabeledGraphics, FontInfo } from './labeledgraphics.js'
export class Ticks { export class Ticks {
@@ -9,6 +9,10 @@ export class Ticks {
return ['decade', 'year', 'month', 'day', 'hour', 'minute', 'second'] return ['decade', 'year', 'month', 'day', 'hour', 'minute', 'second']
} }
static get largeTickSize() {
return 4.2
}
get minWidth() { get minWidth() {
return 10 return 10
} }
@@ -83,7 +87,7 @@ export class Ticks {
return date.toLocaleDateString('de', format) return date.toLocaleDateString('de', format)
} }
draw(timeline, range, width, height, available, format, nextFormat, level) { draw(timeline, range, width, height, available, format, nextFormat, level, extraTicks=false) {
let first = null let first = null
let last = null let last = null
let keyedFormat = (format) ? format[this.formatKey] : null let keyedFormat = (format) ? format[this.formatKey] : null
@@ -91,7 +95,6 @@ export class Ticks {
let redundant = (nextFormat) ? this.formatKey in nextFormat : false let redundant = (nextFormat) ? this.formatKey in nextFormat : false
let fullyRedundant = keyedFormat != null && keyedFormat == keyedNextFormat let fullyRedundant = keyedFormat != null && keyedFormat == keyedNextFormat
let y = timeline.getY() let y = timeline.getY()
for (let { start, end } of this.iterRanges(range)) { for (let { start, end } of this.iterRanges(range)) {
let x = timeline.toX(start) let x = timeline.toX(start)
let xx = x let xx = x
@@ -100,11 +103,10 @@ export class Ticks {
let key = this.dateKey(start) let key = this.dateKey(start)
let text = this.toLocaleString(start, format) let text = this.toLocaleString(start, format)
let align = 'center' let align = 'center'
let downTick = false
if (nextFormat) { if (nextFormat) {
yy = y + timeline.tickLabelOffset(-1, 1) yy = y + timeline.tickLabelOffset(-1, 1)
align = 'left' align = 'left'
timeline.drawTick(x, 4.2) timeline.drawTick(x, Ticks.largeTickSize)
let nextX = timeline.toX(end) - 100 let nextX = timeline.toX(end) - 100
if (x < 0 && nextX > -100 && !redundant) { if (x < 0 && nextX > -100 && !redundant) {
xx = Math.min(4, nextX) xx = Math.min(4, nextX)
@@ -112,20 +114,18 @@ export class Ticks {
else { else {
xx -= 2 xx -= 2
} }
downTick = true
} }
else if (level > 0) { else if (level > 0) {
xx = x + available / 2 xx = x + available / 2
} }
else {
downTick = true
}
if (!fullyRedundant) { if (!fullyRedundant) {
timeline.ensureLabel(key, text, timeline.ensureLabel(key, text,
{ x: xx, y: yy, align }, { x: xx, y: yy, align },
FontInfo.small) FontInfo.small)
} }
if (downTick) timeline.drawTick(x, -1) if (extraTicks)
timeline.drawTick(x, -level)
} }
if (timeline.visibleRange(start, end)) { if (timeline.visibleRange(start, end)) {
if (first == null) if (first == null)
@@ -137,6 +137,15 @@ export class Ticks {
return null return null
return { start: first, end: last } return { start: first, end: last }
} }
drawExtra(timeline, range, size) {
for (let { start } of this.iterRanges(range)) {
if (timeline.visibleDate(start)) {
let x = timeline.toX(start)
timeline.drawTick(x, -size)
}
}
}
} }
export class DecadeTicks extends Ticks { export class DecadeTicks extends Ticks {
@@ -434,15 +443,34 @@ export class TimeTicks {
visible.push(ticks) visible.push(ticks)
} }
let level = 0 let level = 0
let ranges = []
for (let ticks of visible) { for (let ticks of visible) {
if (range == null) if (range == null)
return continue
range = ticks.draw(timeline, range, width, height, range = ticks.draw(timeline, range, width, height,
availables.get(ticks), availables.get(ticks),
formats.get(ticks), formats.get(ticks),
nextFormats.get(ticks), level) nextFormats.get(ticks), level)
if (range) {
ranges.push({ticks, range})
}
level += 1 level += 1
} }
let extraLevel = ranges.length - 1
let extraStart = extraLevel
for(let {ticks, range} of ranges) {
ticks.drawExtra(timeline, range, extraLevel)
extraLevel -= 1
if (extraLevel <= 0) {
continue
}
}
timeline.drawTick(start, Ticks.largeTickSize)
timeline.drawTick(start, -extraStart)
timeline.drawTick(end, Ticks.largeTickSize)
timeline.drawTick(end, -extraStart)
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "iwmlib", "name": "iwmlib",
"version": "1.0.12", "version": "1.0.14",
"description": "An Open Source library for multi-touch, WebGL powered applications.", "description": "An Open Source library for multi-touch, WebGL powered applications.",
"main": "index.js", "main": "index.js",
"directories": { "directories": {