320 lines
10 KiB
JavaScript
320 lines
10 KiB
JavaScript
import { Colors } from '../../utils.js'
|
|
import { WorkerTileLoader, PIXITileLoader } from './loader.js'
|
|
|
|
/**
|
|
* A layer of tiles that represents a zoom level of a DeepZoomImage as a grid
|
|
* of sprites.
|
|
* @constructor
|
|
* @param {number} level - the zoom level of the tile layer
|
|
* @param {DeepZoomImage} view - the zoomable image the layer belongs to
|
|
* @param {number} scale - the scale of the tile layer
|
|
* @param {number} cols - the number of columns of the layer
|
|
* @param {number} rows - the number of rows of the layer
|
|
* @param {number} width - the width of the layer in pixel
|
|
* @param {number} height - the height of the layer in pixel
|
|
* @param {number} tileSize - the size of a single tile in pixel
|
|
* @param {number} overlap - the overlap of the tiles in pixel
|
|
* @param {number} fadeInTime - time needed to fade in tiles if TweenLite is set
|
|
**/
|
|
export class Tiles extends PIXI.Container {
|
|
constructor(level, view, scale, cols, rows, width, height, tileSize, overlap, fadeInTime = 0.33) {
|
|
super()
|
|
this.debug = false
|
|
this.showGrid = false
|
|
this.view = view
|
|
this.level = level
|
|
this.cols = cols
|
|
this.rows = rows
|
|
this.pixelWidth = width
|
|
this.pixelHeight = height
|
|
this.tileSize = tileSize
|
|
this.overlap = overlap
|
|
this.needed = new Map() // url as key, [col, row] as value
|
|
this.requested = new Set()
|
|
this.available = new Map()
|
|
this.scale.set(scale, scale)
|
|
this.tileScale = scale
|
|
this.fadeInTime = fadeInTime
|
|
this.keep = false
|
|
if (this.view.useWorker && view.info.compression && view.info.compression.length > 0) {
|
|
this.loader = new WorkerTileLoader(this, this.view.useWorker)
|
|
} else {
|
|
this.loader = new PIXITileLoader(this, view.info.compression)
|
|
}
|
|
this.interactive = false
|
|
this._highlight = null
|
|
|
|
this._info = null
|
|
|
|
this._centerPoint = null
|
|
this._boundsRect = null
|
|
|
|
this.infoColor = Colors.random()
|
|
this.pprint()
|
|
this.destroyed = false
|
|
}
|
|
|
|
/** Tests whether all tiles are loaded. **/
|
|
isComplete() {
|
|
return this.cols * this.rows === this.children.length
|
|
}
|
|
|
|
/** Returns the highligh graphics layer for debugging purposes.
|
|
**/
|
|
get highlight() {
|
|
if (this._highlight == null) {
|
|
let graphics = new PIXI.Graphics()
|
|
graphics.beginFill(0xffff00, 0.1)
|
|
graphics.lineStyle(2, 0xffff00)
|
|
graphics.drawRect(1, 1, this.tileSize - 2, this.tileSize - 2)
|
|
graphics.endFill()
|
|
graphics.interactive = false
|
|
this._highlight = graphics
|
|
}
|
|
return this._highlight
|
|
}
|
|
|
|
/** Returns the highligh graphics layer for debugging purposes.
|
|
**/
|
|
get info() {
|
|
if (this._info == null) {
|
|
let graphics = new PIXI.Graphics()
|
|
graphics.lineStyle(4, 0xff0000)
|
|
graphics.interactive = false
|
|
this._info = graphics
|
|
this.addChild(this._info)
|
|
}
|
|
return this._info
|
|
}
|
|
|
|
/** Helper method pretty printing debug information. **/
|
|
pprint() {
|
|
if (this.debug)
|
|
console.log(
|
|
'Tiles level: ' +
|
|
this.level +
|
|
' scale: ' +
|
|
this.scale.x +
|
|
' cols: ' +
|
|
this.cols +
|
|
' rows: ' +
|
|
this.rows +
|
|
' w: ' +
|
|
this.pixelWidth +
|
|
' h: ' +
|
|
this.pixelHeight +
|
|
' tsize:' +
|
|
this.tileSize
|
|
)
|
|
}
|
|
|
|
/** Computes the tile position and obeys the overlap.
|
|
* @param {number} col - The column of the tile
|
|
* @param {number} row - The row of the tile
|
|
* @returns {PIXI.Point} obj
|
|
**/
|
|
tilePosition(col, row) {
|
|
let x = col * this.tileSize
|
|
let y = row * this.tileSize
|
|
let overlap = this.overlap
|
|
if (col != 0) {
|
|
x -= overlap
|
|
}
|
|
if (row != 0) {
|
|
y -= overlap
|
|
}
|
|
return new PIXI.Point(x, y)
|
|
}
|
|
|
|
/** Computes the tile size without overlap
|
|
* @param {number} col - The column of the tile
|
|
* @param {number} row - The row of the tile
|
|
* @returns {PIXI.Point} obj
|
|
**/
|
|
tileDimensions(col, row) {
|
|
let w = this.tileSize
|
|
let h = this.tileSize
|
|
let pos = this.tilePosition(col, row)
|
|
if (col == this.cols - 1) {
|
|
w = this.pixelWidth - pos.x
|
|
}
|
|
if (row == this.rows - 1) {
|
|
h = this.pixelHeight - pos.y
|
|
}
|
|
return new PIXI.Point(w, h)
|
|
}
|
|
|
|
/** Method to support debugging. Highlights the specified tile at col, row **/
|
|
highlightTile(col, row) {
|
|
if (col > -1 && row > -1 && col < this.cols && row < this.rows) {
|
|
let graphics = this.highlight
|
|
let dim = this.tileDimensions(col, row)
|
|
graphics.position = this.tilePosition(col, row)
|
|
graphics.clear()
|
|
graphics.beginFill(0xff00ff, 0.1)
|
|
graphics.lineStyle(2, 0xffff00)
|
|
graphics.drawRect(1, 1, dim.x - 2, dim.y - 2)
|
|
graphics.endFill()
|
|
this.addChild(this.highlight)
|
|
} else {
|
|
this.removeChild(this.highlight)
|
|
}
|
|
}
|
|
|
|
/** Loads the tiles for the given urls and adds the tiles as sprites.
|
|
* @param {array} urlpos - An array of URL, pos pairs
|
|
* @param {boolean} onlyone - Loads only on tile at a time if true
|
|
**/
|
|
loadTiles(urlpos, onlyone, refCol, refRow) {
|
|
if (this.showGrid) {
|
|
this.highlightTile(refCol, refRow)
|
|
}
|
|
urlpos.forEach(d => {
|
|
let [url, col, row] = d
|
|
if (this.loader.schedule(url, col, row)) {
|
|
if (onlyone) {
|
|
return this.loader.loadOneTile()
|
|
}
|
|
}
|
|
})
|
|
this.loader.loadAll()
|
|
}
|
|
|
|
/** Private method: add a red border to a tile for debugging purposes. **/
|
|
_addTileBorder(tile, col, row) {
|
|
let dim = this.tileDimensions(col, row)
|
|
let graphics = new PIXI.Graphics()
|
|
graphics.beginFill(0, 0)
|
|
graphics.lineStyle(2, 0xff0000)
|
|
graphics.drawRect(1, 1, dim.x - 2, dim.y - 2)
|
|
graphics.endFill()
|
|
tile.addChild(graphics)
|
|
}
|
|
|
|
/** Adds a tile. **/
|
|
addTile(tile, col, row, url) {
|
|
if (this.available.has(url)) {
|
|
console.warn('Trying to add available tile')
|
|
return
|
|
}
|
|
this.addChildAt(tile, 0)
|
|
this.available.set(url, tile)
|
|
if (this.destroyed) {
|
|
console.warn('Adding to destroyed tiles layer')
|
|
}
|
|
// this._addTileBorder(tile, col, row)
|
|
}
|
|
|
|
/** Called by the loader after each successfull loading of a single tile.
|
|
* Adds the sprite to the tile layer.
|
|
* @param {Object} tile - the loaded tile sprite
|
|
* @param {Number} col - the col position
|
|
* @param {Number} row - the rowposition
|
|
**/
|
|
tileAvailable(tile, col, row, url) {
|
|
let pos = this.tilePosition(col, row)
|
|
if (this.showGrid) {
|
|
this._addTileBorder(tile, col, row)
|
|
}
|
|
tile.position = pos
|
|
tile.interactive = false
|
|
if (TweenLite) {
|
|
tile.alpha = 0
|
|
TweenLite.to(tile, this.fadeInTime, { alpha: this.alpha })
|
|
}
|
|
this.addTile(tile, col, row, url)
|
|
}
|
|
|
|
/** Destroys the tiles layer and destroys the loader. Async load calls are
|
|
* cancelled.
|
|
**/
|
|
destroy() {
|
|
this.destroyed = true
|
|
this.loader.destroy()
|
|
super.destroy({ children: true }) // Calls destroyChildren
|
|
this.available.clear()
|
|
this.requested.clear()
|
|
this.needed.clear()
|
|
}
|
|
|
|
destroyTile(url, tile) {
|
|
this.loader.unschedule(url)
|
|
this.removeChild(tile)
|
|
tile.destroy()
|
|
this.available.delete(url)
|
|
}
|
|
|
|
destroyTileByUrl(url) {
|
|
if (this.available.has(url)) {
|
|
let tile = this.available.get(url)
|
|
this.destroyTile(url, tile)
|
|
}
|
|
}
|
|
|
|
/* Destroys the tiles which are not with the bounds of the app to free
|
|
* memory.
|
|
**/
|
|
destroyTiles(quadTrees) {
|
|
let count = 0
|
|
for (let [url, tile] of this.available.entries()) {
|
|
if (!quadTrees.has(url)) {
|
|
this.destroyTile(url, tile)
|
|
count += 1
|
|
}
|
|
}
|
|
if (count && this.debug) console.log('destroyTiles', this.level, count)
|
|
}
|
|
|
|
destroyUnneededTiles() {
|
|
let count = 0
|
|
for (let [url, tile] of this.available.entries()) {
|
|
if (!this.needed.has(url)) {
|
|
this.destroyTile(url, tile)
|
|
count += 1
|
|
}
|
|
}
|
|
if (count && this.debug) console.log('destroyUnneededTiles', this.level, count)
|
|
}
|
|
|
|
highlightInfos() {
|
|
let graphics = this.info
|
|
let color = this.infoColor
|
|
graphics.clear()
|
|
graphics.lineStyle(2, color)
|
|
for (let [col, row] of this.needed.values()) {
|
|
let dim = this.tileDimensions(col, row)
|
|
let pos = this.tilePosition(col, row)
|
|
graphics.beginFill(color, 0.2)
|
|
graphics.drawRect(pos.x + 1, pos.y + 1, dim.x - 2, dim.y - 2)
|
|
graphics.endFill()
|
|
}
|
|
let r = this._boundsRect
|
|
if (r != null) {
|
|
graphics.lineStyle(20, color)
|
|
graphics.drawRect(r.x, r.y, r.width, r.height)
|
|
graphics.moveTo(r.x, r.y)
|
|
graphics.lineTo(r.x + r.width, r.y + r.height)
|
|
|
|
graphics.moveTo(r.x, r.y + r.height)
|
|
graphics.lineTo(r.x + r.width, r.y)
|
|
}
|
|
|
|
let p = this._centerPoint
|
|
if (p != null) {
|
|
graphics.drawCircle(p.x, p.y, 20)
|
|
}
|
|
}
|
|
|
|
tintTiles(quadTrees) {
|
|
for (let [url, tile] of this.available.entries()) {
|
|
if (!quadTrees.has(url)) tile.tint = 0xff0000
|
|
}
|
|
}
|
|
|
|
untintTiles() {
|
|
for (let [url, tile] of this.available.entries()) {
|
|
tile.tint = 0xffffff
|
|
}
|
|
}
|
|
}
|