Restructured library.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
|
||||
<title>DeepZoomImage Doctests</title>
|
||||
|
||||
<link rel="stylesheet" href="../../3rdparty/highlight/styles/default.css">
|
||||
<link rel="stylesheet" href="../../../css/doctest.css">
|
||||
|
||||
<script src="../../3rdparty/highlight/highlight.pack.js"></script>
|
||||
<script src="../../3rdparty/all.js"></script>
|
||||
<script src="../../3rdparty/jquery.min.js"></script>
|
||||
|
||||
<script src="../../all.js"></script>
|
||||
<script src="../all.js"></script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#app > * {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="Doctest.run()">
|
||||
<h1>DeepZoomImage</h1>
|
||||
<p>
|
||||
The main class of a deeply zoomable image that is represented by a hierarchy of tile layers for each zoom level. This gives
|
||||
the user the impression that even huge pictures (up to gigapixel-images) can be zoomed instantaneously, since the
|
||||
tiles at smaller levels are scaled immediately and overloaded by more detailed tiles at the larger level as fast
|
||||
as possible.
|
||||
</p>
|
||||
<br />
|
||||
<div id="app">
|
||||
<button id="change_dpr">Change Pixel Ratio</button>
|
||||
<div id="canvas_container"></div>
|
||||
<div id="info"></div>
|
||||
</div>
|
||||
<script class="doctest">
|
||||
|
||||
// When an element is added, the ScatterApp wrapps it in it's own Scatter Container.
|
||||
// Just as in the doctest: scatter.html
|
||||
class ScatterApp extends PIXIApp {
|
||||
sceneFactory() {
|
||||
return new ScatterContainer(this.renderer, { showBounds: true, app: this })
|
||||
}
|
||||
}
|
||||
|
||||
let app
|
||||
let state = 0
|
||||
|
||||
//Destroys the PIXIApp element and the corresponding canvas,
|
||||
//to reinstantiate the entire application.
|
||||
changePIXI()
|
||||
|
||||
function changePIXI() {
|
||||
if (typeof app != 'undefined') {
|
||||
//The parameter destroys the canvas, when destroying the app.
|
||||
// Not deleting a new canvas resulted in some
|
||||
// weird PIXI error.
|
||||
app.destroy(true)
|
||||
}
|
||||
|
||||
|
||||
//A new canvas has to be created
|
||||
//for the new view.
|
||||
var canvas = document.createElement("canvas")
|
||||
canvas_container.appendChild(canvas);
|
||||
|
||||
app = new ScatterApp({
|
||||
resolution: state + 1,
|
||||
|
||||
//Default parameters
|
||||
view: canvas,
|
||||
autoResize: false,
|
||||
width: 128,
|
||||
height: 128,
|
||||
backgroundColor: 0xFFCCCCCC
|
||||
}).setup().run()
|
||||
|
||||
|
||||
// To create a DeepZoomImage, a DeepZoomInfo has to
|
||||
// be provided. It contains all the necessary informations
|
||||
// for the DeepZoomImage, to behave as intended.
|
||||
// (E.g. that it displays the right level of tiles for the current view distance.)
|
||||
|
||||
deepZoomInfo = new DeepZoomInfo(
|
||||
{
|
||||
"tileSize": 128,
|
||||
"format": "jpg",
|
||||
"overlap": 0,
|
||||
"type": "map",
|
||||
"height": 4096,
|
||||
"width": 4096,
|
||||
"path": "../assets/maps/test",
|
||||
"urlTileTemplate": "{path}/{level}/{column}/{row}.{format}"
|
||||
});
|
||||
|
||||
|
||||
// Create the DeepZoomImage
|
||||
deepZoomImage = new DeepZoomImage(deepZoomInfo, {
|
||||
highResolution: !!state,
|
||||
app
|
||||
});
|
||||
|
||||
|
||||
|
||||
deepZoomImage.scatter = new DisplayObjectScatter(deepZoomImage, app.renderer, {
|
||||
// Allow more flexible scaling for debugging purposes.
|
||||
minScale: 0,
|
||||
maxScale: 100,
|
||||
// Notify the DeepZoomImage, when it's container has
|
||||
// been transformed (translated, scaled, rotated, ...)
|
||||
onTransform: (event) => {
|
||||
deepZoomImage.transformed(event)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Add the DeepZoomImage to the scene.
|
||||
app.scene.addChild(deepZoomImage)
|
||||
|
||||
//Set info text.
|
||||
info.innerHTML = "resolution: " + app._options.resolution +
|
||||
"<br>high resolution: " + !!state;
|
||||
}
|
||||
|
||||
|
||||
// Add functionality to the button.
|
||||
change_dpr.addEventListener("click", (event) => {
|
||||
state = (state + 1) % 2
|
||||
changePIXI()
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Executable
+1118
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,219 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
|
||||
<title>DeepZoomImage Doctests</title>
|
||||
|
||||
<link rel="stylesheet" href="../../3rdparty/highlight/styles/default.css">
|
||||
<link rel="stylesheet" href="../../../css/doctest.css">
|
||||
|
||||
<script src="../../3rdparty/highlight/highlight.pack.js"></script>
|
||||
<script src="../../3rdparty/all.js"></script>
|
||||
<script src="../../3rdparty/jquery.min.js"></script>
|
||||
|
||||
<script src="../../all.js"></script>
|
||||
<script src="../all.js"></script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#app > * {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="Doctest.run()">
|
||||
<h1>Double DeepZoomImage</h1>
|
||||
<p>
|
||||
The main class of a deeply zoomable image that is represented by a hierarchy of tile layers for each zoom level. This gives
|
||||
the user the impression that even huge pictures (up to gigapixel-images) can be zoomed instantaneously, since the
|
||||
tiles at smaller levels are scaled immediately and overloaded by more detailed tiles at the larger level as fast
|
||||
as possible.
|
||||
</p>
|
||||
<br />
|
||||
<div id="div1" style="float: left;"></div>
|
||||
<div id="div2" style="float: right;"></div>
|
||||
<div style="clear: left; margin-top: 540px;" />
|
||||
<script class="doctest">
|
||||
|
||||
// deepZoom
|
||||
//--------------------
|
||||
const deepZoomInfo = new DeepZoomInfo({
|
||||
"tileSize": 128,
|
||||
"format": "jpg",
|
||||
"overlap": 0,
|
||||
"type": "map",
|
||||
"height": 4096,
|
||||
"width": 4096,
|
||||
"path": "../assets/maps/test",
|
||||
"urlTileTemplate": "{path}/{level}/{column}/{row}.{format}"
|
||||
})
|
||||
// const deepZoomInfo = new DeepZoomInfo({
|
||||
// compression: [
|
||||
// "dds"
|
||||
// ],
|
||||
// clip: {
|
||||
// minLevel: 12,
|
||||
// maxLevel: 20,
|
||||
// startCol: 275215,
|
||||
// startRow: 181050,
|
||||
// bounds: {
|
||||
// min: [48.458353, 8.96484374976547],
|
||||
// max: [48.5747899110263, 9.14062499976523]
|
||||
// }
|
||||
// },
|
||||
// tileSize: 512,
|
||||
// format: "png",
|
||||
// overlap: 0,
|
||||
// type: "map",
|
||||
// height: 131072,
|
||||
// width: 131072,
|
||||
// path: "../../../var/tuesch/luftbild_2016_full",
|
||||
// urlTileTemplate: "{path}/{level}/{row}/{column}.{format}"
|
||||
// })
|
||||
|
||||
// app
|
||||
//--------------------
|
||||
window.app = new PIXIApp({
|
||||
width: 400,
|
||||
height: 500,
|
||||
backgroundColor: 0xFFCCCCCC
|
||||
}).setup().run()
|
||||
|
||||
div1.appendChild(app.view)
|
||||
|
||||
// create the ScatterContainer
|
||||
//--------------------
|
||||
const scatterContainer1 = new ScatterContainer(app.renderer, {showBounds: true, app: app})
|
||||
app.scene.addChild(scatterContainer1)
|
||||
|
||||
// Create the DeepZoomImage
|
||||
//--------------------
|
||||
setTimeout(() => {
|
||||
const deepZoomImage1 = new DeepZoomImage(deepZoomInfo, {app, world: scatterContainer1})
|
||||
deepZoomImage1.scatter = new DisplayObjectScatter(deepZoomImage1, app.renderer, {
|
||||
minScale: 0,
|
||||
maxScale: 50,
|
||||
onTransform: event => {
|
||||
console.log('currentLevel', deepZoomImage1.currentLevel)
|
||||
deepZoomImage1.transformed(event)
|
||||
}
|
||||
})
|
||||
|
||||
scatterContainer1.addChild(deepZoomImage1)
|
||||
}, 1000)
|
||||
|
||||
|
||||
// app2
|
||||
//--------------------
|
||||
const app2 = new PIXIApp({
|
||||
width: 400,
|
||||
height: 500,
|
||||
backgroundColor: 0xFFCCCCCC
|
||||
}).setup().run()
|
||||
|
||||
div2.appendChild(app2.view)
|
||||
|
||||
// create the ScatterContainer
|
||||
//--------------------
|
||||
const scatterContainer2 = new ScatterContainer(app2.renderer, {showBounds: true, app: app2})
|
||||
app2.scene.addChild(scatterContainer2)
|
||||
|
||||
// Create the DeepZoomImage
|
||||
//--------------------
|
||||
const deepZoomImage2 = new DeepZoomImage(deepZoomInfo, {app: app2})
|
||||
deepZoomImage2.scatter = new DisplayObjectScatter(deepZoomImage2, app2.renderer, {
|
||||
minScale: 0,
|
||||
maxScale: 100,
|
||||
onTransform: (event) => {
|
||||
deepZoomImage2.transformed(event)
|
||||
}
|
||||
})
|
||||
|
||||
scatterContainer2.addChild(deepZoomImage2)
|
||||
|
||||
</script>
|
||||
|
||||
<h1>DeepZoomImage in DeepZoomImage</h1>
|
||||
<p>
|
||||
The main class of a deeply zoomable image that is represented by a hierarchy of tile layers for each zoom level. This gives
|
||||
the user the impression that even huge pictures (up to gigapixel-images) can be zoomed instantaneously, since the
|
||||
tiles at smaller levels are scaled immediately and overloaded by more detailed tiles at the larger level as fast
|
||||
as possible.
|
||||
</p>
|
||||
<br />
|
||||
<div id="div3"></div>
|
||||
<script class="doctest">
|
||||
|
||||
// app3
|
||||
//--------------------
|
||||
const app3 = new PIXIApp({
|
||||
width: 900,
|
||||
height: 500,
|
||||
backgroundColor: 0xFFCCCCCC
|
||||
}).setup().run()
|
||||
|
||||
window.app3 = app3
|
||||
|
||||
div3.appendChild(app3.view)
|
||||
|
||||
// create the ScatterContainer
|
||||
//--------------------
|
||||
const scatterContainer3 = new ScatterContainer(app3.renderer, {app: app3, showBounds: true, claimEvent: false, stopEvents: false})
|
||||
app3.scene.addChild(scatterContainer3)
|
||||
|
||||
// Create the DeepZoomImage
|
||||
//--------------------
|
||||
const deepZoomImage3 = new DeepZoomImage(deepZoomInfo, {app: app3})
|
||||
deepZoomImage3.scatter = new DisplayObjectScatter(deepZoomImage3, app3.renderer, {
|
||||
minScale: 0,
|
||||
maxScale: 100,
|
||||
startScale: 2,
|
||||
autoBringToFront: false,
|
||||
onTransform: (event) => {
|
||||
deepZoomImage3.transformed(event)
|
||||
}
|
||||
})
|
||||
|
||||
app3._deepZoomImage3 = deepZoomImage3
|
||||
|
||||
scatterContainer3.addChild(deepZoomImage3)
|
||||
|
||||
// Create the second DeepZoomImage
|
||||
//--------------------
|
||||
const border = new PIXI.Graphics()
|
||||
border.beginFill(0x282828, 1)
|
||||
border.drawRect(0, 0, 264, 244)
|
||||
scatterContainer3.addChild(border)
|
||||
|
||||
const mask = new PIXI.Graphics()
|
||||
mask.beginFill(0x282828, 1)
|
||||
mask.drawRect(0, 0, 260, 240)
|
||||
scatterContainer3.addChild(mask)
|
||||
|
||||
const deepZoomImage4 = new DeepZoomImage(deepZoomInfo, {app: app3})
|
||||
deepZoomImage4.x = 4
|
||||
deepZoomImage4.y = 4
|
||||
deepZoomImage4.scatter = new DisplayObjectScatter(deepZoomImage4, app3.renderer, {
|
||||
minScale: 0,
|
||||
maxScale: 100,
|
||||
onTransform: (event) => {
|
||||
deepZoomImage4.transformed(event)
|
||||
}
|
||||
})
|
||||
deepZoomImage4.mask = mask
|
||||
|
||||
app3._deepZoomImage4 = deepZoomImage4
|
||||
|
||||
scatterContainer3.addChild(deepZoomImage4)
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,371 @@
|
||||
import { deepZoomTileCache, Tile } from './tile.js'
|
||||
|
||||
/**
|
||||
* A Tile Loader component that can be plugged into a Tiles Layer.
|
||||
*/
|
||||
export class TileLoader {
|
||||
constructor(tiles) {
|
||||
this.debug = false
|
||||
this.tiles = tiles
|
||||
this.setup()
|
||||
}
|
||||
|
||||
/** Setup collections and instance vars. */
|
||||
setup() {
|
||||
this.map = new Map() // Map {url : [ col, row]}
|
||||
this.loading = new Set() // Set url
|
||||
this.loaded = new Map() // Map {url : sprite }
|
||||
this.loadQueue = []
|
||||
}
|
||||
|
||||
/** Schedules a tile url for loading. The loading itself must be triggered
|
||||
by a call to loadOneTile or loadAll
|
||||
|
||||
* @param {String} url - the url of the texture / tile
|
||||
* @param {Number} col - the tile col
|
||||
* @param {Number} row - the tile row
|
||||
**/
|
||||
schedule(url, col, row) {
|
||||
if (this.loaded.has(url)) return false
|
||||
if (this.loading.has(url)) return false
|
||||
this.map.set(url, [col, row])
|
||||
this.loading.add(url)
|
||||
this.loadQueue.push(url)
|
||||
return true
|
||||
}
|
||||
|
||||
unschedule(url) {
|
||||
if (this.loaded.has(url)) this.loaded.delete(url)
|
||||
if (this.loading.has(url)) this.loading.delete(url)
|
||||
this.loadQueue = this.loadQueue.filter(item => item != url)
|
||||
}
|
||||
|
||||
/** Cancels loading by clearing the load queue **/
|
||||
cancel() {
|
||||
this.loadQueue = []
|
||||
this.loading.clear()
|
||||
}
|
||||
|
||||
/** Destroys alls collections. **/
|
||||
destroy() {
|
||||
this.setup()
|
||||
}
|
||||
|
||||
/** Private method. Informs the tile layer about a texture for a given url.
|
||||
* Creates the sprite for the loaded texture and informs the tile layer.
|
||||
* @param {String} url - the url
|
||||
* @param {Object} texture - the loaded resource
|
||||
**/
|
||||
_textureAvailable(url, col, row, texture) {
|
||||
let tile = this.loaded.get(url)
|
||||
if (tile != null) {
|
||||
console.warn("Tile already loaded")
|
||||
tile.unregister()
|
||||
}
|
||||
tile = new Tile(texture, url)
|
||||
this.loaded.set(url, tile)
|
||||
this.tiles.tileAvailable(tile, col, row, url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the PIXI Loader but can be replaced with othe loaders implementing
|
||||
* the public methods without underscore.
|
||||
* Calls the Tiles.tileAvailable method if the texture is available.
|
||||
**/
|
||||
export class PIXITileLoader extends TileLoader {
|
||||
|
||||
constructor(tiles, compression) {
|
||||
super(tiles)
|
||||
this.destroyed = false
|
||||
this.loader = new PIXI.loaders.Loader()
|
||||
this.loader.on('load', this._onLoaded.bind(this))
|
||||
this.loader.on('error', this._onError.bind(this))
|
||||
if (compression) {
|
||||
this.loader.pre(PIXI.compressedTextures.imageParser())
|
||||
}
|
||||
}
|
||||
|
||||
schedule(url, col, row) {
|
||||
// Overwritten schedule to avoid BaseTexture and Texture already loaded errors.
|
||||
if (this.loaded.has(url)) return false
|
||||
if (this.loading.has(url)) return false
|
||||
|
||||
if (deepZoomTileCache.has(url)) {
|
||||
let tiles = deepZoomTileCache.get(url)
|
||||
for (let tile of tiles.values()) {
|
||||
//console.log("Reusing cached texture", tile.parent)
|
||||
let texture = tile.texture
|
||||
this._textureAvailable(url, col, row, texture)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let texture = PIXI.utils.TextureCache[url]
|
||||
if (texture) {
|
||||
if (this.debug) console.log('Texture already loaded', texture)
|
||||
this._textureAvailable(url, col, row, texture)
|
||||
return false
|
||||
}
|
||||
let base = PIXI.utils.BaseTextureCache[url]
|
||||
if (base) {
|
||||
if (this.debug) console.log('BaseTexture already loaded', base)
|
||||
let texture = new PIXI.Texture(base)
|
||||
this._textureAvailable(url, col, row, texture)
|
||||
return false
|
||||
}
|
||||
return super.schedule(url, col, row)
|
||||
}
|
||||
|
||||
/** Load one and only one of the scheduled tiles **/
|
||||
loadOneTile() {
|
||||
if (this.destroyed)
|
||||
return
|
||||
this._loadOneTile()
|
||||
}
|
||||
|
||||
/** Load all scheduled tiles **/
|
||||
loadAll() {
|
||||
if (this.destroyed)
|
||||
return
|
||||
this._loadAllTiles()
|
||||
}
|
||||
|
||||
/** Destroys the loader completly **/
|
||||
destroy() {
|
||||
this.destroyed = true
|
||||
super.destroy()
|
||||
try {
|
||||
this.loader.reset()
|
||||
} catch (error) {
|
||||
console.warn("Error while resetting loader", error)
|
||||
}
|
||||
}
|
||||
|
||||
_onError(loader, error) {
|
||||
console.warn('Cannot load', error)
|
||||
}
|
||||
|
||||
/** Private method. Called by the PIXI loader after each successfull
|
||||
* loading of a single tile.
|
||||
* Creates the sprite for the loaded texture and informs the tile layer.
|
||||
* @param {Object} loader - the loader instance
|
||||
* @param {Object} resource - the loaded resource with url and texture attr
|
||||
**/
|
||||
_onLoaded(loader, resource) {
|
||||
if (this.destroyed) {
|
||||
let texture = resource.texture
|
||||
let destroyBase = !deepZoomTileCache.has(resource.url)
|
||||
texture.destroy(destroyBase)
|
||||
console.warn("Received resource after destroy", texture)
|
||||
return
|
||||
}
|
||||
try {
|
||||
let [col, row] = this.map.get(resource.url)
|
||||
this._textureAvailable(resource.url, col, row, resource.texture)
|
||||
}
|
||||
catch (err) {
|
||||
console.warn("Texture unavailable: " + err.message)
|
||||
}
|
||||
}
|
||||
|
||||
/** Private method: loads one tile from the queue. **/
|
||||
_loadOneTile(retry = 1) {
|
||||
//console.log("_loadOneTile")
|
||||
if (this.destroyed) {
|
||||
//console.warn("_loadOneTile after destroy")
|
||||
return
|
||||
}
|
||||
if (this.loader.loading) {
|
||||
setTimeout(() => {
|
||||
this._loadOneTile()
|
||||
}, retry)
|
||||
return
|
||||
}
|
||||
if (this.loadQueue.length > 0) {
|
||||
let url = this.loadQueue.pop()
|
||||
this.loader.add(url, url)
|
||||
this.loader.load()
|
||||
}
|
||||
}
|
||||
|
||||
/** Private method: loads all tiles from the queue in batches. Batches are
|
||||
helpfull to avoid loading tiles that are no longer needed because the
|
||||
user has already zoomed to a different level.**/
|
||||
_loadAllTiles(batchSize = 8, retry = 16) {
|
||||
if (this.destroyed) {
|
||||
return
|
||||
}
|
||||
if (this.loadQueue.length > 0) {
|
||||
if (this.loader.loading) {
|
||||
//console.log("Loader busy", this.loadQueue.length)
|
||||
setTimeout(() => {
|
||||
this._loadAllTiles()
|
||||
}, retry)
|
||||
return
|
||||
}
|
||||
let i = 0
|
||||
let urls = []
|
||||
while (i < batchSize && this.loadQueue.length > 0) {
|
||||
let url = this.loadQueue.pop()
|
||||
if (!this.loaded.has(url)) {
|
||||
let resource = this.loader.resources[url]
|
||||
if (resource) {
|
||||
console.log("Resource already added", url)
|
||||
}
|
||||
else {
|
||||
urls.push(url)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
this.loader.add(urls).load(() => {
|
||||
this._loadAllTiles()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses XMLHttpRequests but can be replaced with other loaders implementing
|
||||
* the public methods without underscore.
|
||||
* Calls the Tiles.tileAvailable method if the texture is available.
|
||||
**/
|
||||
export class RequestTileLoader extends TileLoader {
|
||||
|
||||
constructor(tiles, compression) {
|
||||
super(tiles)
|
||||
this.compression = compression
|
||||
}
|
||||
|
||||
schedule(url, col, row) {
|
||||
this._load(url, col, row)
|
||||
return super.schedule(url, col, row)
|
||||
}
|
||||
|
||||
_load(url, col, row, callback = null) {
|
||||
if (this.compression) {
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', url, false)
|
||||
xhr.responseType = 'arraybuffer'
|
||||
xhr.onload = e => {
|
||||
let CompressedImage = PIXI.compressedTextures.CompressedImage
|
||||
let compressed = CompressedImage.loadFromArrayBuffer(
|
||||
xhr.response,
|
||||
url
|
||||
)
|
||||
let base = new PIXI.BaseTexture(compressed)
|
||||
let texture = new PIXI.Texture(base)
|
||||
this._textureAvailable(url, col, row, texture)
|
||||
if (callback) callback()
|
||||
}
|
||||
xhr.send()
|
||||
} else {
|
||||
let texture = PIXI.Texture.fromImage('assets/image.png')
|
||||
this._textureAvailable(url, col, row, texture)
|
||||
if (callback) callback()
|
||||
}
|
||||
}
|
||||
|
||||
/** Load one and only one of the scheduled tiles **/
|
||||
loadOneTile() {
|
||||
this._loadOneTile()
|
||||
}
|
||||
|
||||
/** Load all scheduled tiles **/
|
||||
loadAll() {
|
||||
this._loadAllTiles()
|
||||
}
|
||||
|
||||
/** Private method: loads one tile from the queue. **/
|
||||
_loadOneTile(retry = 1) {
|
||||
if (this.loadQueue.length > 0) {
|
||||
let url = this.loadQueue.pop()
|
||||
let [col, row] = this.map.get(url)
|
||||
this._load(url, col, row)
|
||||
}
|
||||
}
|
||||
|
||||
/** Private method: loads all tiles from the queue in batches. Batches are
|
||||
helpfull to avoid loading tiles that are no longer needed because the
|
||||
user has already zoomed to a different level.**/
|
||||
_loadAllTiles(batchSize = 8, retry = 16) {
|
||||
if (this.loadQueue.length > 0) {
|
||||
let i = 0
|
||||
let urls = []
|
||||
while (i < batchSize && this.loadQueue.length > 0) {
|
||||
let url = this.loadQueue.pop()
|
||||
if (this.debug) console.time(url)
|
||||
if (!this.loaded.has(url)) {
|
||||
urls.push(url)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
let total = urls.length
|
||||
let count = 0
|
||||
for (let url of urls) {
|
||||
let [col, row] = this.map.get(url)
|
||||
this._load(url, col, row, () => {
|
||||
count++
|
||||
if (count == total) this._loadAllTiles()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uses Workers but can be replaced with other loaders implementing
|
||||
* the public methods without underscore.
|
||||
* Calls the Tiles.tileAvailable method if the texture is available.
|
||||
**/
|
||||
export class WorkerTileLoader extends TileLoader {
|
||||
|
||||
constructor(tiles) {
|
||||
super(tiles)
|
||||
let worker = this.worker = new Worker("../../lib/pixi/deepzoom/tileloader.js")
|
||||
worker.onmessage = (event) => {
|
||||
if (event.data.success) {
|
||||
let { url, col, row, buffer } = event.data
|
||||
//console.log("WorkerTileLoader.loaded", url, buffer)
|
||||
let CompressedImage = PIXI.compressedTextures.CompressedImage
|
||||
let compressed = CompressedImage.loadFromArrayBuffer(buffer, url)
|
||||
let base = new PIXI.BaseTexture(compressed)
|
||||
let texture = new PIXI.Texture(base)
|
||||
this._textureAvailable(url, col, row, texture)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadOne() {
|
||||
if (this.loadQueue.length > 0) {
|
||||
let url = this.loadQueue.pop()
|
||||
let [col, row] = this.map.get(url)
|
||||
let tile = [col, row, url]
|
||||
this.worker.postMessage({ command: "load", tiles: [tile] })
|
||||
}
|
||||
}
|
||||
|
||||
loadAll() {
|
||||
let tiles = []
|
||||
while (this.loadQueue.length > 0) {
|
||||
let url = this.loadQueue.pop()
|
||||
let [col, row] = this.map.get(url)
|
||||
tiles.push([col, row, url])
|
||||
}
|
||||
this.worker.postMessage({ command: "load", tiles })
|
||||
}
|
||||
|
||||
cancel() {
|
||||
super.cancel()
|
||||
this.worker.postMessage({ command: "abort" })
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.worker.postMessage({ command: "abort" })
|
||||
this.worker.terminate()
|
||||
this.worker = null
|
||||
super.destroy()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="has-navbar-fixed-top">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DeepZoom Tests</title>
|
||||
<!-- disable zooming -->
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
||||
|
||||
<script src="../../../lib/3rdparty/all.js"></script>
|
||||
<script src="../../../lib/all.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="../test/lib/bulma.css">
|
||||
<link rel="stylesheet" href="../test/lib/mocha.min.css">
|
||||
|
||||
<link rel="stylesheet" href="../test/test.css">
|
||||
|
||||
<script src="../test/lib/fontawesome.js"></script>
|
||||
<script src="../test/lib/chai.js"></script>
|
||||
<script src="../test/lib/mocha.min.js"></script>
|
||||
<script src="../test/lib/Chart.bundle.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar is-fixed-top has-shadow">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="../../../index.html">
|
||||
<img class="image is-32x32" src="../../../assets/icons/icon.png">
|
||||
</a>
|
||||
<div class="navbar-item">
|
||||
DeepZoom Tests
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="section">
|
||||
<div class="columns">
|
||||
<div class="column" id="main"></div>
|
||||
<div class="column">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">
|
||||
All tests
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a onclick="uiTestSuite.run()">Run all tests</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="menu-label">
|
||||
Memory tests
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a onclick="uiTestSuite.run('zoomInZoomOut')">Zoom in, Zoom out, Zoom in, Zoom out, ...</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="menu-label">Test report</p>
|
||||
<div id="mocha"></div>
|
||||
<div id="chart" style="position: relative; height: 400px; width: 100%;">
|
||||
<canvas id="memoryChart"></canvas>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
|
||||
// testFrame
|
||||
//--------------------
|
||||
function loadTestFrame(cb) {
|
||||
|
||||
// remove old iframe
|
||||
const element = document.getElementById('testFrame')
|
||||
if (element) {
|
||||
main.removeChild(element)
|
||||
}
|
||||
|
||||
// create new iframe
|
||||
const iframe = document.createElement("iframe")
|
||||
iframe.setAttribute('id', 'testFrame')
|
||||
iframe.setAttribute('src', './index.html')
|
||||
iframe.setAttribute('style', 'border: 2px solid gray;')
|
||||
iframe.setAttribute('width', '1024')
|
||||
iframe.setAttribute('height', '768')
|
||||
if (cb) {
|
||||
iframe.addEventListener('load', function _callback(event) {
|
||||
cb.call(this, event, iframe)
|
||||
iframe.removeEventListener('load', _callback, true) // execute callback only once
|
||||
}, true)
|
||||
}
|
||||
main.appendChild(iframe)
|
||||
|
||||
// bind to main window (testFrame is not bound automatically)
|
||||
window.testFrame = iframe
|
||||
}
|
||||
|
||||
loadTestFrame()
|
||||
|
||||
// chart
|
||||
//--------------------
|
||||
const data = {
|
||||
datasets: [{
|
||||
label: 'Total HeapSize',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1,
|
||||
pointRadius: 1
|
||||
}, {
|
||||
label: 'Used HeapSize',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||
borderColor: 'rgba(255, 206, 86, 1)',
|
||||
borderWidth: 1,
|
||||
pointRadius: 1
|
||||
}, {
|
||||
label: 'HeapSize Limit',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
borderColor: 'rgba(255,99,132,1)',
|
||||
borderWidth: 1,
|
||||
pointRadius: 1
|
||||
}]
|
||||
}
|
||||
|
||||
function addChart() {
|
||||
|
||||
const ctx = document.getElementById('memoryChart').getContext('2d')
|
||||
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'scatter',
|
||||
data,
|
||||
options: {
|
||||
responsive: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'JavaScript Memory'
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false
|
||||
},
|
||||
hover: {
|
||||
enabled: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Time [min]'
|
||||
},
|
||||
ticks: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
stepSize: 2
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'HeapSize [MB]'
|
||||
},
|
||||
ticks: {
|
||||
maxTicksLimit: 6
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const start = Date.now()
|
||||
|
||||
setInterval(() => {
|
||||
|
||||
data.datasets.forEach(function(dataset) {
|
||||
const memory = window.performance.memory
|
||||
const x = (Date.now() - start) / 1000 / 60
|
||||
let y = 0
|
||||
if (dataset.label === 'Total HeapSize') {
|
||||
y = memory.totalJSHeapSize / 1000 / 1000
|
||||
} else if (dataset.label === 'Used HeapSize') {
|
||||
y = memory.usedJSHeapSize / 1000 / 1000
|
||||
} else {
|
||||
y = memory.jsHeapSizeLimit / 1000 / 1000
|
||||
}
|
||||
dataset.data.push({x, y})
|
||||
});
|
||||
|
||||
chart.update()
|
||||
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
if (/chrome/i.test(navigator.userAgent)) {
|
||||
addChart()
|
||||
} else {
|
||||
console.log('No Chrome, no Chart :-(')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script src="../../../lib/bootstrap.js"></script>
|
||||
<script>
|
||||
Bootstrap.import('../test/testsuite.js')
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 363 KiB |
@@ -0,0 +1,77 @@
|
||||
|
||||
export const deepZoomTileCache = new Map()
|
||||
|
||||
|
||||
/** The current Tile implementation simply uses PIXI.Sprites.
|
||||
*
|
||||
* BTW: PIXI.extras.TilingSprite is not appropriate. It should be used for
|
||||
* repeating patterns.
|
||||
**/
|
||||
export class Tile extends PIXI.Sprite {
|
||||
constructor(texture, url) {
|
||||
super(texture)
|
||||
this.url = url
|
||||
this.register(url)
|
||||
}
|
||||
|
||||
static fromImage(imageId, crossorigin, scaleMode) {
|
||||
return new Tile(PIXI.Texture.fromImage(imageId, crossorigin, scaleMode), imageId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the tile in the global reference counter for textures
|
||||
*
|
||||
* @param {*} url
|
||||
* @param {boolean} [debug=false]
|
||||
* @memberof Tile
|
||||
*/
|
||||
register(url, debug = false) {
|
||||
if (deepZoomTileCache.has(url)) {
|
||||
let tiles = deepZoomTileCache.get(url)
|
||||
tiles.add(this)
|
||||
if (debug) console.log("Tile.register", url, tiles.size)
|
||||
}
|
||||
else {
|
||||
deepZoomTileCache.set(url, new Set([this]))
|
||||
if (debug) console.log("Tile.register", url, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the rile in the global reference counter for textures
|
||||
*
|
||||
* @returns {number} The number of how often a texture is used.
|
||||
* @memberof Tile
|
||||
*/
|
||||
unregister() {
|
||||
let tiles = deepZoomTileCache.get(this.url)
|
||||
tiles.delete(this)
|
||||
if (tiles.size == 0) {
|
||||
deepZoomTileCache.delete(this.url)
|
||||
}
|
||||
return tiles.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys this sprite and optionally its texture and children
|
||||
*
|
||||
* @param {*} options Part of the PIXI API, but ignored in the implementation
|
||||
* @memberof Tile
|
||||
*/
|
||||
destroy(options, debug = false) {
|
||||
if (this.parent != null) {
|
||||
|
||||
}
|
||||
let count = this.unregister()
|
||||
if (count <= 0) {
|
||||
let opts = { children: true, texture: true, baseTexture: true }
|
||||
super.destroy(opts)
|
||||
if (debug) console.log("Tile.destroy", deepZoomTileCache.size, opts)
|
||||
}
|
||||
else {
|
||||
let opts = { children: true, texture: false, baseTexture: false }
|
||||
if (debug) console.log("Tile.destroy", deepZoomTileCache.size, opts)
|
||||
super.destroy(opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
let loadQueue = []
|
||||
let pendingRequests = new Map()
|
||||
const batchSize = 8
|
||||
const debug = false
|
||||
|
||||
function load() {
|
||||
|
||||
while(loadQueue.length>0 && pendingRequests.size<batchSize) {
|
||||
let tile = loadQueue.shift()
|
||||
let [col, row, url] = tile
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.responseType = "arraybuffer"
|
||||
xhr.onload = (event) => {
|
||||
pendingRequests.delete(url)
|
||||
let buffer = xhr.response
|
||||
postMessage({ success: true, url, col, row, buffer}, [buffer])
|
||||
}
|
||||
xhr.onerror = (event) => {
|
||||
pendingRequests.delete(url)
|
||||
let buffer = null
|
||||
postMessage({ success: false, url, col, row, buffer})
|
||||
}
|
||||
xhr.open('GET', url, true)
|
||||
xhr.send()
|
||||
pendingRequests.set(url, xhr)
|
||||
}
|
||||
if (loadQueue.length>0)
|
||||
setTimeout(load, 1000/120)
|
||||
else {
|
||||
if (debug) console.log("Ready")
|
||||
}
|
||||
}
|
||||
|
||||
self.onmessage = (event) => {
|
||||
let msg = event.data
|
||||
switch(msg.command) {
|
||||
case 'load':
|
||||
for(let tile of msg.tiles) {
|
||||
loadQueue.push(tile)
|
||||
}
|
||||
load()
|
||||
break
|
||||
case 'abort':
|
||||
loadQueue = []
|
||||
for(let xhr of pendingRequests.values()) {
|
||||
xhr.abort()
|
||||
}
|
||||
if (debug) console.log('Abort')
|
||||
break
|
||||
default:
|
||||
console.warn('Unknown worker command: ' + msg.command)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
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.preferWorker && view.info.compression.length > 0)
|
||||
this.loader = new WorkerTileLoader(this)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user