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
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |