mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 14:46:10 +03:00
ThreeJSRenderer nearly complete, starting refactor of core to support plugin drawers.
This commit is contained in:
parent
ca5cc84988
commit
376ee38d3c
168
src/drawer.js
168
src/drawer.js
@ -94,7 +94,7 @@ $.Drawer = function( options ) {
|
|||||||
* @member {Object} context
|
* @member {Object} context
|
||||||
* @memberof OpenSeadragon.Drawer#
|
* @memberof OpenSeadragon.Drawer#
|
||||||
*/
|
*/
|
||||||
this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null;
|
this.context = this.useCanvas ? this.canvas.getContext( this.useCanvas.contextType || "2d" ) : null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
|
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
|
||||||
@ -142,33 +142,6 @@ $.Drawer = function( options ) {
|
|||||||
|
|
||||||
/** @lends OpenSeadragon.Drawer.prototype */
|
/** @lends OpenSeadragon.Drawer.prototype */
|
||||||
$.Drawer.prototype = {
|
$.Drawer.prototype = {
|
||||||
// deprecated
|
|
||||||
addOverlay: function( element, location, placement, onDraw ) {
|
|
||||||
$.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead.");
|
|
||||||
this.viewer.addOverlay( element, location, placement, onDraw );
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
updateOverlay: function( element, location, placement ) {
|
|
||||||
$.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.");
|
|
||||||
this.viewer.updateOverlay( element, location, placement );
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
removeOverlay: function( element ) {
|
|
||||||
$.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.");
|
|
||||||
this.viewer.removeOverlay( element );
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
clearOverlays: function() {
|
|
||||||
$.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.");
|
|
||||||
this.viewer.clearOverlays();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function converts the given point from to the drawer coordinate by
|
* This function converts the given point from to the drawer coordinate by
|
||||||
@ -206,63 +179,9 @@ $.Drawer.prototype = {
|
|||||||
context.clip();
|
context.clip();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the opacity of the drawer.
|
|
||||||
* @param {Number} opacity
|
|
||||||
* @returns {OpenSeadragon.Drawer} Chainable.
|
|
||||||
*/
|
|
||||||
setOpacity: function( opacity ) {
|
|
||||||
$.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.");
|
|
||||||
var world = this.viewer.world;
|
|
||||||
for (var i = 0; i < world.getItemCount(); i++) {
|
|
||||||
world.getItemAt( i ).setOpacity( opacity );
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the opacity of the drawer.
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
getOpacity: function() {
|
|
||||||
$.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead.");
|
|
||||||
var world = this.viewer.world;
|
|
||||||
var maxOpacity = 0;
|
|
||||||
for (var i = 0; i < world.getItemCount(); i++) {
|
|
||||||
var opacity = world.getItemAt( i ).getOpacity();
|
|
||||||
if ( opacity > maxOpacity ) {
|
|
||||||
maxOpacity = opacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxOpacity;
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
needsUpdate: function() {
|
|
||||||
$.console.error( "[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead." );
|
|
||||||
return this.viewer.world.needsDraw();
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
numTilesLoaded: function() {
|
|
||||||
$.console.error( "[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead." );
|
|
||||||
return this.viewer.tileCache.numTilesLoaded();
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
reset: function() {
|
|
||||||
$.console.error( "[Drawer.reset] this function is deprecated. Use World.resetItems instead." );
|
|
||||||
this.viewer.world.resetItems();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
update: function() {
|
|
||||||
$.console.error( "[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead." );
|
|
||||||
this.clear();
|
|
||||||
this.viewer.world.draw();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Boolean} True if rotation is supported.
|
* @returns {Boolean} True if rotation is supported.
|
||||||
@ -761,7 +680,92 @@ $.Drawer.prototype = {
|
|||||||
x: sketchCanvasSize,
|
x: sketchCanvasSize,
|
||||||
y: sketchCanvasSize
|
y: sketchCanvasSize
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated functions
|
||||||
|
// deprecated
|
||||||
|
addOverlay: function( element, location, placement, onDraw ) {
|
||||||
|
$.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead.");
|
||||||
|
this.viewer.addOverlay( element, location, placement, onDraw );
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
updateOverlay: function( element, location, placement ) {
|
||||||
|
$.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.");
|
||||||
|
this.viewer.updateOverlay( element, location, placement );
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
removeOverlay: function( element ) {
|
||||||
|
$.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.");
|
||||||
|
this.viewer.removeOverlay( element );
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
clearOverlays: function() {
|
||||||
|
$.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.");
|
||||||
|
this.viewer.clearOverlays();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// deprecated
|
||||||
|
needsUpdate: function() {
|
||||||
|
$.console.error( "[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead." );
|
||||||
|
return this.viewer.world.needsDraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
numTilesLoaded: function() {
|
||||||
|
$.console.error( "[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead." );
|
||||||
|
return this.viewer.tileCache.numTilesLoaded();
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
reset: function() {
|
||||||
|
$.console.error( "[Drawer.reset] this function is deprecated. Use World.resetItems instead." );
|
||||||
|
this.viewer.world.resetItems();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
update: function() {
|
||||||
|
$.console.error( "[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead." );
|
||||||
|
this.clear();
|
||||||
|
this.viewer.world.draw();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Set the opacity of the drawer.
|
||||||
|
* @param {Number} opacity
|
||||||
|
* @returns {OpenSeadragon.Drawer} Chainable.
|
||||||
|
*/
|
||||||
|
setOpacity: function( opacity ) {
|
||||||
|
$.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.");
|
||||||
|
var world = this.viewer.world;
|
||||||
|
for (var i = 0; i < world.getItemCount(); i++) {
|
||||||
|
world.getItemAt( i ).setOpacity( opacity );
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the opacity of the drawer.
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
getOpacity: function() {
|
||||||
|
$.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead.");
|
||||||
|
var world = this.viewer.world;
|
||||||
|
var maxOpacity = 0;
|
||||||
|
for (var i = 0; i < world.getItemCount(); i++) {
|
||||||
|
var opacity = world.getItemAt( i ).getOpacity();
|
||||||
|
if ( opacity > maxOpacity ) {
|
||||||
|
maxOpacity = opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxOpacity;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
}( OpenSeadragon ));
|
}( OpenSeadragon ));
|
||||||
|
@ -301,6 +301,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
|
|
||||||
if (xUpdated || yUpdated || scaleUpdated || degreesUpdated) {
|
if (xUpdated || yUpdated || scaleUpdated || degreesUpdated) {
|
||||||
this._updateForScale();
|
this._updateForScale();
|
||||||
|
this._raiseBoundsChange();
|
||||||
this._needsDraw = true;
|
this._needsDraw = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -407,7 +408,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
var yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
|
var yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
|
||||||
var bounds = this.source.getTileBounds(level, xMod, yMod);
|
var bounds = this.source.getTileBounds(level, xMod, yMod);
|
||||||
if (this.getFlip()) {
|
if (this.getFlip()) {
|
||||||
bounds.x = 1 - bounds.x - bounds.width;
|
bounds.x = Math.max(0, 1 - bounds.x - bounds.width);
|
||||||
}
|
}
|
||||||
bounds.x += (x - xMod) / numTiles.x;
|
bounds.x += (x - xMod) / numTiles.x;
|
||||||
bounds.y += (this._worldHeightCurrent / this._worldWidthCurrent) * ((y - yMod) / numTiles.y);
|
bounds.y += (this._worldHeightCurrent / this._worldWidthCurrent) * ((y - yMod) / numTiles.y);
|
||||||
@ -421,6 +422,46 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
return new $.Point(this.source.dimensions.x, this.source.dimensions.y);
|
return new $.Point(this.source.dimensions.x, this.source.dimensions.y);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the x position and width of each column and y position and height of each row
|
||||||
|
* @function
|
||||||
|
* @param {Number} level
|
||||||
|
* @returns {Object} Dictionary which defines numRows, numColumns,
|
||||||
|
* dimensions (in pixels), normalizedDimensions (by image width), rowInfo (Array of [x, width]),
|
||||||
|
* and columnInfo (Array of [y, height]) for this level of the image
|
||||||
|
*/
|
||||||
|
getGridDefinition: function( level ) {
|
||||||
|
var numTiles = this.source.getNumTiles(level),
|
||||||
|
levelScale = this.source.getLevelScale(level),
|
||||||
|
tileWidth = this.source.getTileWidth(level) / levelScale,
|
||||||
|
tileHeight = this.source.getTileHeight(level) / levelScale,
|
||||||
|
size = this.getContentSize(),
|
||||||
|
|
||||||
|
def = {
|
||||||
|
numRows: numTiles.y,
|
||||||
|
numColumns: numTiles.x,
|
||||||
|
dimensions: size,
|
||||||
|
normalizedDimensions: size.divide(size.x),
|
||||||
|
rowInfo: [],
|
||||||
|
columnInfo: []
|
||||||
|
};
|
||||||
|
var i;
|
||||||
|
for(i = 0; i < numTiles.x; i++){
|
||||||
|
def.columnInfo[i] = {
|
||||||
|
x: i * tileWidth / size.x, // x is defined by regular grid spacing
|
||||||
|
width: (i === (numTiles.x - 1) ? Math.min(size.x - i * tileWidth, size.x) : tileWidth) / size.x, // width is standard except for last column
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for(i = 0; i < numTiles.y; i++){
|
||||||
|
def.rowInfo[i] = {
|
||||||
|
y: i * tileHeight / size.x, // y is defined by regular grid spacing
|
||||||
|
height: (i === (numTiles.y - 1) ? Math.min(size.y - i * tileHeight, size.y) : tileHeight) / size.x, // height is standard except for last row
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return def;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {OpenSeadragon.Point} The TiledImage's content size, in window coordinates.
|
* @returns {OpenSeadragon.Point} The TiledImage's content size, in window coordinates.
|
||||||
*/
|
*/
|
||||||
@ -1182,8 +1223,10 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform the actual drawing
|
// Perform the actual drawing
|
||||||
|
|
||||||
this._drawTiles(this.lastDrawn);
|
this._drawTiles(this.lastDrawn);
|
||||||
|
|
||||||
|
|
||||||
// Load the new 'best' tile
|
// Load the new 'best' tile
|
||||||
if (bestTile && !bestTile.context2D) {
|
if (bestTile && !bestTile.context2D) {
|
||||||
this._loadTile(bestTile, currentTime);
|
this._loadTile(bestTile, currentTime);
|
||||||
@ -1656,9 +1699,14 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
*/
|
*/
|
||||||
_setTileLoaded: function(tile, data, cutoff, tileRequest) {
|
_setTileLoaded: function(tile, data, cutoff, tileRequest) {
|
||||||
var increment = 0,
|
var increment = 0,
|
||||||
|
eventFinished = false,
|
||||||
_this = this;
|
_this = this;
|
||||||
|
|
||||||
function getCompletionCallback() {
|
function getCompletionCallback() {
|
||||||
|
if (eventFinished) {
|
||||||
|
$.console.error("Event 'tile-loaded' argument getCompletionCallback must be called synchronously. " +
|
||||||
|
"Its return value should be called asynchronously.");
|
||||||
|
}
|
||||||
increment++;
|
increment++;
|
||||||
return completionCallback;
|
return completionCallback;
|
||||||
}
|
}
|
||||||
@ -1679,6 +1727,24 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
tiledImage: _this
|
tiledImage: _this
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Triggered when a tile has just been loaded in memory. That means that the
|
||||||
|
* image has been downloaded and can be modified before being drawn to the canvas.
|
||||||
|
*
|
||||||
|
* @event tile-ready
|
||||||
|
* @memberof OpenSeadragon.Viewer
|
||||||
|
* @type {object}
|
||||||
|
* @property {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
|
||||||
|
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
|
||||||
|
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
|
||||||
|
* @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
|
||||||
|
*/
|
||||||
|
_this.viewer.raiseEvent("tile-ready", {
|
||||||
|
tile: tile,
|
||||||
|
tiledImage: _this,
|
||||||
|
tileRequest: tileRequest,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
_this._needsDraw = true;
|
_this._needsDraw = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1700,6 +1766,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* marked as entirely loaded when the callback has been called once for each
|
* marked as entirely loaded when the callback has been called once for each
|
||||||
* call to getCompletionCallback.
|
* call to getCompletionCallback.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var fallbackCompletion = getCompletionCallback();
|
||||||
this.viewer.raiseEvent("tile-loaded", {
|
this.viewer.raiseEvent("tile-loaded", {
|
||||||
tile: tile,
|
tile: tile,
|
||||||
tiledImage: this,
|
tiledImage: this,
|
||||||
@ -1711,8 +1779,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
data: data,
|
data: data,
|
||||||
getCompletionCallback: getCompletionCallback
|
getCompletionCallback: getCompletionCallback
|
||||||
});
|
});
|
||||||
|
eventFinished = true;
|
||||||
// In case the completion callback is never called, we at least force it once.
|
// In case the completion callback is never called, we at least force it once.
|
||||||
getCompletionCallback()();
|
fallbackCompletion();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -420,6 +420,41 @@ $.TileSource.prototype = {
|
|||||||
return new $.Rect( px * scale, py * scale, sx * scale, sy * scale );
|
return new $.Rect( px * scale, py * scale, sx * scale, sy * scale );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* @param {Number} level
|
||||||
|
* @param {Number} x
|
||||||
|
* @param {Number} y
|
||||||
|
* @param {Boolean} [isSource=false] Whether to return the source bounds of the tile.
|
||||||
|
* @returns {OpenSeadragon.Rect} Either where this tile fits (in normalized coordinates unless imageCoordinates == true) or the
|
||||||
|
* portion of the tile to use as the source of the drawing operation (in pixels), depending on
|
||||||
|
* the isSource parameter, without overlap.
|
||||||
|
*/
|
||||||
|
getTileBoundsNoOverlap: function( level, x, y, isSource, imageCoordinates ) {
|
||||||
|
var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ),
|
||||||
|
tileWidth = this.getTileWidth(level),
|
||||||
|
tileHeight = this.getTileHeight(level),
|
||||||
|
tileOverlap = this.tileOverlap || 0,
|
||||||
|
px = tileWidth * x,
|
||||||
|
py = tileHeight * y,
|
||||||
|
sx = tileWidth,
|
||||||
|
sy = tileHeight,
|
||||||
|
scale = 1.0 / dimensionsScaled.x;
|
||||||
|
|
||||||
|
sx = Math.min( sx, dimensionsScaled.x - px );
|
||||||
|
sy = Math.min( sy, dimensionsScaled.y - py );
|
||||||
|
|
||||||
|
if (isSource) {
|
||||||
|
return new $.Rect((x === 0 ? 0 : tileOverlap), (y === 0 ? 0 : tileOverlap), sx, sy);
|
||||||
|
} else if ( imageCoordinates ){
|
||||||
|
return new $.Rect( px, py, sx, sy).times(scale * this.width);
|
||||||
|
} else {
|
||||||
|
return new $.Rect( px, py, sx, sy).times(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for retrieving, and caching the
|
* Responsible for retrieving, and caching the
|
||||||
|
@ -18,18 +18,34 @@
|
|||||||
<input type="button" value="Rotate" id="rotate">
|
<input type="button" value="Rotate" id="rotate">
|
||||||
<span id="degrees">0deg</span>
|
<span id="degrees">0deg</span>
|
||||||
</div>
|
</div>
|
||||||
|
<canvas id="test-canvas" width = "400" height="400" style="image-rendering: pixelated; position:fixed;right:10px;top:10px;width:400px;height:400px;background-color:white;border:thin gray solid;"></canvas>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var canvas = document.getElementById('test-canvas');// width == height == 400
|
||||||
|
var context = canvas.getContext("2d");
|
||||||
|
context.fillRect(0, 0, 400, 400); // fill entire canvas with black rectangle
|
||||||
|
context.translate(200, 200); // translate to center
|
||||||
|
context.rotate(1 * Math.PI/180); // rotate by 1 degree
|
||||||
|
context.clearRect(-100, -100, 200, 200); // clear a 200 x 200 rectangle
|
||||||
|
context.fillRect(-100, -100, 100, 200); // draw a 100 x 200 rectangle in the left half
|
||||||
|
context.fillRect(-00, -100, 100, 200); // draw a 100 x 200 rectangle in the right half
|
||||||
|
|
||||||
|
window.context = context;
|
||||||
|
|
||||||
var viewer = OpenSeadragon({
|
var viewer = OpenSeadragon({
|
||||||
id: "contentDiv",
|
id: "contentDiv",
|
||||||
prefixUrl: "../../build/openseadragon/images/",
|
prefixUrl: "../../build/openseadragon/images/",
|
||||||
tileSources: "../data/testpattern.dzi",
|
tileSources: "../data/testpattern.dzi",
|
||||||
minZoomImageRatio: 0,
|
minZoomImageRatio: 0,
|
||||||
maxZoomPixelRatio: 10
|
maxZoomPixelRatio: 10,
|
||||||
|
subPixelRoundingForTransparency: OpenSeadragon.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS
|
||||||
});
|
});
|
||||||
|
window.c = viewer.drawer.canvas.getContext("2d");
|
||||||
|
|
||||||
viewer.addHandler("open", function(event) {
|
viewer.addHandler("open", function(event) {
|
||||||
|
viewer.world.getItemAt(0).source.hasTransparency = function(){ return true; }
|
||||||
|
viewer.viewport.rotateTo(1);
|
||||||
var elt = document.createElement("div");
|
var elt = document.createElement("div");
|
||||||
elt.className = "runtime-overlay";
|
elt.className = "runtime-overlay";
|
||||||
elt.style.background = "green";
|
elt.style.background = "green";
|
||||||
|
841
test/demo/webgl-renderer.js
Normal file
841
test/demo/webgl-renderer.js
Normal file
@ -0,0 +1,841 @@
|
|||||||
|
// import 'https://cdnjs.cloudflare.com/ajax/libs/three.js/0.149.0/three.min.js';
|
||||||
|
import '../lib/three.js';
|
||||||
|
const THREE = window.THREE;
|
||||||
|
const DEPTH_MULTIPLIER = 0.1;
|
||||||
|
|
||||||
|
export class ThreeJSRenderer extends OpenSeadragon.Drawer{
|
||||||
|
constructor(openSeadragonViewer, canvas){
|
||||||
|
this._viewer = openSeadragonViewer;
|
||||||
|
this._camera = null;
|
||||||
|
this._scene = null;
|
||||||
|
this._imageContainer = null;
|
||||||
|
this._renderer = null;
|
||||||
|
|
||||||
|
this._renderingContinously = false;
|
||||||
|
this._animationFrame = null;
|
||||||
|
|
||||||
|
if(canvas){
|
||||||
|
this._canvas = canvas;
|
||||||
|
} else {
|
||||||
|
let viewerCanvas = viewer.drawer.canvas;
|
||||||
|
this._canvas = viewer.drawer.canvas;
|
||||||
|
// let canvas = this._canvas = document.createElement('canvas');
|
||||||
|
// canvas.insertBefore(viewerCanvas);
|
||||||
|
|
||||||
|
// canvas.style.width = viewerCanvas.clientWidth+'px';
|
||||||
|
// canvas.style.height = viewerCanvas.clientHeight+'px';
|
||||||
|
// canvas.width = viewerCanvas.width;
|
||||||
|
// canvas.height = viewerCanvas.height;
|
||||||
|
|
||||||
|
// //make the test canvas mirror all changes to the viewer canvas
|
||||||
|
// viewer.addHandler("resize", function(){
|
||||||
|
// canvas.style.width = viewerCanvas.clientWidth+'px';
|
||||||
|
// canvas.style.height = viewerCanvas.clientHeight+'px';
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
createThreeViewer(this);
|
||||||
|
}
|
||||||
|
renderFrame(){
|
||||||
|
if(this._animationFrame) {
|
||||||
|
cancelAnimationFrame(this._animationFrame);
|
||||||
|
}
|
||||||
|
this._animationFrame = requestAnimationFrame(()=>this.render());
|
||||||
|
}
|
||||||
|
render(){
|
||||||
|
// this.camera.updateProjectionMatrix();
|
||||||
|
this._renderer.render(this._scene, this._camera);
|
||||||
|
this._animationFrame = null;
|
||||||
|
if(this._renderingContinuously){
|
||||||
|
this.renderFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderContinuously(continuously){
|
||||||
|
if(continuously){
|
||||||
|
this._renderingContinuously = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this._renderingContinuously = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destroy(){
|
||||||
|
//clear all resources used by the renderer, geometries, textures etc
|
||||||
|
//to do: remove handlers from viewer and dispose of any remaining textures/materials
|
||||||
|
cleanupObject(this._scene);
|
||||||
|
cleanupObject(this._renderer);
|
||||||
|
cleanupObject(this._camera);
|
||||||
|
this._scene = null;
|
||||||
|
this._renderer = null;
|
||||||
|
this._camera = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Override API from OpenSeadragon.Drawer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will create multiple polygon paths on the drawing context by provided polygons,
|
||||||
|
* then clip the context to the paths.
|
||||||
|
* @param {OpenSeadragon.Point[][]} polygons - an array of polygons. A polygon is an array of OpenSeadragon.Point
|
||||||
|
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
|
||||||
|
*/
|
||||||
|
clipWithPolygons(polygons, useSketch) {
|
||||||
|
if (!this.useCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var context = this._getContext(useSketch);
|
||||||
|
context.beginPath();
|
||||||
|
polygons.forEach(function (polygon) {
|
||||||
|
polygon.forEach(function (coord, i) {
|
||||||
|
context[i === 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
context.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the drawer (unload current loaded tiles)
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
|
||||||
|
this.canvas.width = 1;
|
||||||
|
this.canvas.height = 1;
|
||||||
|
this.sketchCanvas = null;
|
||||||
|
this.sketchContext = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the Drawer so it's ready to draw another frame.
|
||||||
|
*/
|
||||||
|
clear(){
|
||||||
|
this.canvas.innerHTML = "";
|
||||||
|
if ( this.useCanvas ) {
|
||||||
|
var viewportSize = this._calculateCanvasSize();
|
||||||
|
if( this.canvas.width !== viewportSize.x ||
|
||||||
|
this.canvas.height !== viewportSize.y ) {
|
||||||
|
this.canvas.width = viewportSize.x;
|
||||||
|
this.canvas.height = viewportSize.y;
|
||||||
|
this._updateImageSmoothingEnabled(this.context);
|
||||||
|
if ( this.sketchCanvas !== null ) {
|
||||||
|
var sketchCanvasSize = this._calculateSketchCanvasSize();
|
||||||
|
this.sketchCanvas.width = sketchCanvasSize.x;
|
||||||
|
this.sketchCanvas.height = sketchCanvasSize.y;
|
||||||
|
this._updateImageSmoothingEnabled(this.sketchContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_clear(useSketch, bounds) {
|
||||||
|
if (!this.useCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var context = this._getContext(useSketch);
|
||||||
|
if (bounds) {
|
||||||
|
context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||||
|
} else {
|
||||||
|
var canvas = context.canvas;
|
||||||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the given tile.
|
||||||
|
* @param {OpenSeadragon.Tile} tile - The tile to draw.
|
||||||
|
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
|
||||||
|
* drawingHandler({context, tile, rendered})
|
||||||
|
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
|
||||||
|
* where <code>rendered</code> is the context with the pre-drawn image.
|
||||||
|
* @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1.
|
||||||
|
* @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position
|
||||||
|
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
|
||||||
|
* position and size of tiles supporting alpha channel in non-transparency
|
||||||
|
* context.
|
||||||
|
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
|
||||||
|
*/
|
||||||
|
drawTile( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
|
||||||
|
$.console.assert(tile, '[Drawer.drawTile] tile is required');
|
||||||
|
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
|
||||||
|
|
||||||
|
if (this.useCanvas) {
|
||||||
|
var context = this._getContext(useSketch);
|
||||||
|
scale = scale || 1;
|
||||||
|
tile.drawCanvas(context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source);
|
||||||
|
} else {
|
||||||
|
tile.drawHTML( this.canvas );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_getContext( useSketch ) {
|
||||||
|
var context = this.context;
|
||||||
|
if ( useSketch ) {
|
||||||
|
if (this.sketchCanvas === null) {
|
||||||
|
this.sketchCanvas = document.createElement( "canvas" );
|
||||||
|
var sketchCanvasSize = this._calculateSketchCanvasSize();
|
||||||
|
this.sketchCanvas.width = sketchCanvasSize.x;
|
||||||
|
this.sketchCanvas.height = sketchCanvasSize.y;
|
||||||
|
this.sketchContext = this.sketchCanvas.getContext( "2d" );
|
||||||
|
|
||||||
|
// If the viewport is not currently rotated, the sketchCanvas
|
||||||
|
// will have the same size as the main canvas. However, if
|
||||||
|
// the viewport get rotated later on, we will need to resize it.
|
||||||
|
if (this.viewport.getRotation() === 0) {
|
||||||
|
var self = this;
|
||||||
|
this.viewer.addHandler('rotate', function resizeSketchCanvas() {
|
||||||
|
if (self.viewport.getRotation() === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.viewer.removeHandler('rotate', resizeSketchCanvas);
|
||||||
|
var sketchCanvasSize = self._calculateSketchCanvasSize();
|
||||||
|
self.sketchCanvas.width = sketchCanvasSize.x;
|
||||||
|
self.sketchCanvas.height = sketchCanvasSize.y;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this._updateImageSmoothingEnabled(this.sketchContext);
|
||||||
|
}
|
||||||
|
context = this.sketchContext;
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
saveContext() {
|
||||||
|
if (!this.useCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._getContext( useSketch ).save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
restoreContext( useSketch ) {
|
||||||
|
if (!this.useCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._getContext( useSketch ).restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
setClip(rect, useSketch) {
|
||||||
|
if (!this.useCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = this._getContext( useSketch );
|
||||||
|
context.beginPath();
|
||||||
|
context.rect(rect.x, rect.y, rect.width, rect.height);
|
||||||
|
context.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
drawRectangle(rect, fillStyle, useSketch) {
|
||||||
|
if (!this.useCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = this._getContext( useSketch );
|
||||||
|
context.save();
|
||||||
|
context.fillStyle = fillStyle;
|
||||||
|
context.fillRect(rect.x, rect.y, rect.width, rect.height);
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blends the sketch canvas in the main canvas.
|
||||||
|
* @param {Object} options The options
|
||||||
|
* @param {Float} options.opacity The opacity of the blending.
|
||||||
|
* @param {Float} [options.scale=1] The scale at which tiles were drawn on
|
||||||
|
* the sketch. Default is 1.
|
||||||
|
* Use scale to draw at a lower scale and then enlarge onto the main canvas.
|
||||||
|
* @param {OpenSeadragon.Point} [options.translate] A translation vector
|
||||||
|
* that was used to draw the tiles
|
||||||
|
* @param {String} [options.compositeOperation] - How the image is
|
||||||
|
* composited onto other images; see compositeOperation in
|
||||||
|
* {@link OpenSeadragon.Options} for possible values.
|
||||||
|
* @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch
|
||||||
|
* canvas to blend in the main canvas. If specified, options.scale and
|
||||||
|
* options.translate get ignored.
|
||||||
|
*/
|
||||||
|
blendSketch(opacity, scale, translate, compositeOperation) {
|
||||||
|
var options = opacity;
|
||||||
|
if (!$.isPlainObject(options)) {
|
||||||
|
options = {
|
||||||
|
opacity: opacity,
|
||||||
|
scale: scale,
|
||||||
|
translate: translate,
|
||||||
|
compositeOperation: compositeOperation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!this.useCanvas || !this.sketchCanvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
opacity = options.opacity;
|
||||||
|
compositeOperation = options.compositeOperation;
|
||||||
|
var bounds = options.bounds;
|
||||||
|
|
||||||
|
this.context.save();
|
||||||
|
this.context.globalAlpha = opacity;
|
||||||
|
if (compositeOperation) {
|
||||||
|
this.context.globalCompositeOperation = compositeOperation;
|
||||||
|
}
|
||||||
|
if (bounds) {
|
||||||
|
// Internet Explorer, Microsoft Edge, and Safari have problems
|
||||||
|
// when you call context.drawImage with negative x or y
|
||||||
|
// or x + width or y + height greater than the canvas width or height respectively.
|
||||||
|
if (bounds.x < 0) {
|
||||||
|
bounds.width += bounds.x;
|
||||||
|
bounds.x = 0;
|
||||||
|
}
|
||||||
|
if (bounds.x + bounds.width > this.canvas.width) {
|
||||||
|
bounds.width = this.canvas.width - bounds.x;
|
||||||
|
}
|
||||||
|
if (bounds.y < 0) {
|
||||||
|
bounds.height += bounds.y;
|
||||||
|
bounds.y = 0;
|
||||||
|
}
|
||||||
|
if (bounds.y + bounds.height > this.canvas.height) {
|
||||||
|
bounds.height = this.canvas.height - bounds.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.drawImage(
|
||||||
|
this.sketchCanvas,
|
||||||
|
bounds.x,
|
||||||
|
bounds.y,
|
||||||
|
bounds.width,
|
||||||
|
bounds.height,
|
||||||
|
bounds.x,
|
||||||
|
bounds.y,
|
||||||
|
bounds.width,
|
||||||
|
bounds.height
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
scale = options.scale || 1;
|
||||||
|
translate = options.translate;
|
||||||
|
var position = translate instanceof $.Point ?
|
||||||
|
translate : new $.Point(0, 0);
|
||||||
|
|
||||||
|
var widthExt = 0;
|
||||||
|
var heightExt = 0;
|
||||||
|
if (translate) {
|
||||||
|
var widthDiff = this.sketchCanvas.width - this.canvas.width;
|
||||||
|
var heightDiff = this.sketchCanvas.height - this.canvas.height;
|
||||||
|
widthExt = Math.round(widthDiff / 2);
|
||||||
|
heightExt = Math.round(heightDiff / 2);
|
||||||
|
}
|
||||||
|
this.context.drawImage(
|
||||||
|
this.sketchCanvas,
|
||||||
|
position.x - widthExt * scale,
|
||||||
|
position.y - heightExt * scale,
|
||||||
|
(this.canvas.width + 2 * widthExt) * scale,
|
||||||
|
(this.canvas.height + 2 * heightExt) * scale,
|
||||||
|
-widthExt,
|
||||||
|
-heightExt,
|
||||||
|
this.canvas.width + 2 * widthExt,
|
||||||
|
this.canvas.height + 2 * heightExt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
drawDebugInfo(tile, count, i, tiledImage) {
|
||||||
|
if ( !this.useCanvas ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
|
||||||
|
var context = this.context;
|
||||||
|
context.save();
|
||||||
|
context.lineWidth = 2 * $.pixelDensityRatio;
|
||||||
|
context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
|
||||||
|
context.strokeStyle = this.debugGridColor[colorIndex];
|
||||||
|
context.fillStyle = this.debugGridColor[colorIndex];
|
||||||
|
|
||||||
|
if (this.viewport.getRotation(true) % 360 !== 0 ) {
|
||||||
|
this._offsetForRotation({degrees: this.viewport.getRotation(true)});
|
||||||
|
}
|
||||||
|
if (tiledImage.getRotation(true) % 360 !== 0) {
|
||||||
|
this._offsetForRotation({
|
||||||
|
degrees: tiledImage.getRotation(true),
|
||||||
|
point: tiledImage.viewport.pixelFromPointNoRotate(
|
||||||
|
tiledImage._getRotationPoint(true), true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
|
||||||
|
tiledImage.getRotation(true) % 360 === 0) {
|
||||||
|
if(tiledImage._drawer.viewer.viewport.getFlip()) {
|
||||||
|
tiledImage._drawer._flip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.strokeRect(
|
||||||
|
tile.position.x * $.pixelDensityRatio,
|
||||||
|
tile.position.y * $.pixelDensityRatio,
|
||||||
|
tile.size.x * $.pixelDensityRatio,
|
||||||
|
tile.size.y * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
|
||||||
|
var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio;
|
||||||
|
var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio;
|
||||||
|
|
||||||
|
// Rotate the text the right way around.
|
||||||
|
context.translate( tileCenterX, tileCenterY );
|
||||||
|
context.rotate( Math.PI / 180 * -this.viewport.getRotation(true) );
|
||||||
|
context.translate( -tileCenterX, -tileCenterY );
|
||||||
|
|
||||||
|
if( tile.x === 0 && tile.y === 0 ){
|
||||||
|
context.fillText(
|
||||||
|
"Zoom: " + this.viewport.getZoom(),
|
||||||
|
tile.position.x * $.pixelDensityRatio,
|
||||||
|
(tile.position.y - 30) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
context.fillText(
|
||||||
|
"Pan: " + this.viewport.getBounds().toString(),
|
||||||
|
tile.position.x * $.pixelDensityRatio,
|
||||||
|
(tile.position.y - 20) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
context.fillText(
|
||||||
|
"Level: " + tile.level,
|
||||||
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||||
|
(tile.position.y + 20) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
context.fillText(
|
||||||
|
"Column: " + tile.x,
|
||||||
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||||
|
(tile.position.y + 30) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
context.fillText(
|
||||||
|
"Row: " + tile.y,
|
||||||
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||||
|
(tile.position.y + 40) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
context.fillText(
|
||||||
|
"Order: " + i + " of " + count,
|
||||||
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||||
|
(tile.position.y + 50) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
context.fillText(
|
||||||
|
"Size: " + tile.size.toString(),
|
||||||
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||||
|
(tile.position.y + 60) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
context.fillText(
|
||||||
|
"Position: " + tile.position.toString(),
|
||||||
|
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||||
|
(tile.position.y + 70) * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.viewport.getRotation(true) % 360 !== 0 ) {
|
||||||
|
this._restoreRotationChanges();
|
||||||
|
}
|
||||||
|
if (tiledImage.getRotation(true) % 360 !== 0) {
|
||||||
|
this._restoreRotationChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
|
||||||
|
tiledImage.getRotation(true) % 360 === 0) {
|
||||||
|
if(tiledImage._drawer.viewer.viewport.getFlip()) {
|
||||||
|
tiledImage._drawer._flip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
debugRect(rect) {
|
||||||
|
if ( this.useCanvas ) {
|
||||||
|
var context = this.context;
|
||||||
|
context.save();
|
||||||
|
context.lineWidth = 2 * $.pixelDensityRatio;
|
||||||
|
context.strokeStyle = this.debugGridColor[0];
|
||||||
|
context.fillStyle = this.debugGridColor[0];
|
||||||
|
|
||||||
|
context.strokeRect(
|
||||||
|
rect.x * $.pixelDensityRatio,
|
||||||
|
rect.y * $.pixelDensityRatio,
|
||||||
|
rect.width * $.pixelDensityRatio,
|
||||||
|
rect.height * $.pixelDensityRatio
|
||||||
|
);
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Create THREE.js version of rendering tiled images using WebGL ****/
|
||||||
|
function createThreeViewer(instance){
|
||||||
|
// Add listeners for events that require modifying the scene or camera
|
||||||
|
instance._viewer.addHandler("close", ()=>instance.destroy());
|
||||||
|
instance._viewer.world.addHandler("add-item", ev => addTiledImage(ev, instance));
|
||||||
|
instance._viewer.world.addHandler("remove-item", ev => removeTiledImage(ev, instance));
|
||||||
|
instance._viewer.world.addHandler("item-index-change", ev => setItemOrder(ev, instance));
|
||||||
|
instance._viewer.addHandler("tile-ready", ev => tileReady(ev, instance));
|
||||||
|
instance._viewer.addHandler("tile-unloaded", ev => tileUnloaded(ev, instance));
|
||||||
|
instance._viewer.addHandler("viewport-change", ev => viewportChange(ev, instance));
|
||||||
|
// instance._viewer.addHandler("update-viewport", ev => instance.renderFrame());
|
||||||
|
|
||||||
|
//to do: support pages of sequence mode
|
||||||
|
|
||||||
|
//to do: add handler for resize event to auto-sync
|
||||||
|
|
||||||
|
let viewerBounds = instance._viewer.viewport.getBoundsNoRotate();
|
||||||
|
instance._scene = new THREE.Scene();
|
||||||
|
instance._imageContainer = new THREE.Group();
|
||||||
|
instance._camera = new THREE.OrthographicCamera(
|
||||||
|
viewerBounds.width / -2,
|
||||||
|
viewerBounds.width / 2,
|
||||||
|
viewerBounds.height / 2,
|
||||||
|
viewerBounds.height / -2,
|
||||||
|
0,
|
||||||
|
10000
|
||||||
|
);
|
||||||
|
instance._camera.position.x = viewerBounds.x + viewerBounds.width/2;
|
||||||
|
instance._camera.position.y = -(viewerBounds.y + viewerBounds.height/2);
|
||||||
|
instance._camera.position.z = 100;
|
||||||
|
instance._camera.lookAt(instance._camera.position.x, instance._camera.position.y, 0);
|
||||||
|
instance._camera.updateProjectionMatrix();
|
||||||
|
|
||||||
|
var light = new THREE.AmbientLight();
|
||||||
|
instance._scene.add(light);
|
||||||
|
instance._scene.add(instance._imageContainer);
|
||||||
|
|
||||||
|
instance._scene.background = new THREE.Color(0.25, 0.25, 0.25);
|
||||||
|
|
||||||
|
instance._renderer = new THREE.WebGLRenderer({canvas: instance._canvas});
|
||||||
|
}
|
||||||
|
|
||||||
|
//private
|
||||||
|
function tileReady(event, instance){
|
||||||
|
let tile = event.tile;
|
||||||
|
let tiledImage = event.tiledImage;
|
||||||
|
|
||||||
|
//create a THREE.Material with the image data for this tile
|
||||||
|
let texture = new THREE.CanvasTexture(event.tile.getCanvasContext().canvas);
|
||||||
|
texture.flipY = false; // To match OSD reference frame
|
||||||
|
let material = new THREE.MeshLambertMaterial({
|
||||||
|
map: texture,
|
||||||
|
transparent: !!tile.hasTransparency || tiledImage.opacity < 1,
|
||||||
|
opacity: tiledImage.opacity
|
||||||
|
});
|
||||||
|
|
||||||
|
//attach the material to the tile so it can be queried by location using OpenSeadragon methods
|
||||||
|
tile._three = material;
|
||||||
|
|
||||||
|
//cache the bounds for this material so it doesn't have to be recomputed every time it is used to update the scene
|
||||||
|
material.userData._tileBounds = tiledImage.source.getTileBounds(tile.level, tile.x, tile.y);
|
||||||
|
material.userData.hasTransparency = !!tile.hasTransparency;
|
||||||
|
material.userData.tile = tile;
|
||||||
|
material.userData.tiledImage = tiledImage;
|
||||||
|
|
||||||
|
//since a new tile is available, update the image (if needed)
|
||||||
|
updateTiledImageRendering(tiledImage, tile, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tileUnloaded(event, instance){
|
||||||
|
let tile = event.tile;
|
||||||
|
cleanupObject(tile._three);
|
||||||
|
delete tile._three;
|
||||||
|
|
||||||
|
updateTiledImageRendering(event.tiledImage, tile, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewportChange(event, instance){
|
||||||
|
let viewer = event.eventSource;
|
||||||
|
|
||||||
|
let viewerBounds = viewer.viewport.getBoundsNoRotate(true);
|
||||||
|
instance._camera.left = viewerBounds.width / -2;
|
||||||
|
instance._camera.right = viewerBounds.width / 2;
|
||||||
|
instance._camera.top = viewerBounds.height / 2;
|
||||||
|
instance._camera.bottom = viewerBounds.height / -2;
|
||||||
|
|
||||||
|
let center = viewer.viewport.getCenter(true);
|
||||||
|
instance._camera.position.x = center.x;
|
||||||
|
instance._camera.position.y = -center.y;
|
||||||
|
instance._camera.rotation.z = viewer.viewport.getRotation(true) * Math.PI / 180;
|
||||||
|
|
||||||
|
instance._camera.updateProjectionMatrix();
|
||||||
|
|
||||||
|
let numItems = viewer.world.getItemCount();
|
||||||
|
let i;
|
||||||
|
for(i = 0; i < numItems; i++){
|
||||||
|
let tiledImage = viewer.world.getItemAt(i);
|
||||||
|
updateMeshIfNeeded(tiledImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.renderFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addTiledImage(event, instance){
|
||||||
|
let tiledImage = event.item;
|
||||||
|
|
||||||
|
//create a Group for the tiles of this tiled image.
|
||||||
|
if(!tiledImage._three){
|
||||||
|
let tileContainer = new THREE.Group();
|
||||||
|
let rotationAxis = new THREE.Group();
|
||||||
|
let positioningGroup = new THREE.Group();
|
||||||
|
rotationAxis.add(tileContainer);
|
||||||
|
positioningGroup.add(rotationAxis);
|
||||||
|
positioningGroup.userData._tileContainer = tileContainer;
|
||||||
|
|
||||||
|
//add the object to the group of images (i.e. add to the scene)
|
||||||
|
instance._imageContainer.add(positioningGroup);
|
||||||
|
|
||||||
|
//save mutual references between OpenSceneGraph and ThreeJSRenderer versions of tiledImages
|
||||||
|
tiledImage._three = positioningGroup;
|
||||||
|
positioningGroup._tiledImage = tiledImage;
|
||||||
|
|
||||||
|
//offset the tileContainer so the center of the image is at the origin of the parent group
|
||||||
|
tileContainer.position.x = -0.5;
|
||||||
|
tileContainer.position.y = -0.5 / tiledImage.source.aspectRatio;
|
||||||
|
|
||||||
|
//undo the offset of the tileContainer, moving this back into original viewport coordinate space
|
||||||
|
rotationAxis.position.x = tileContainer.position.x * -1;
|
||||||
|
rotationAxis.position.y = tileContainer.position.y * -1;
|
||||||
|
|
||||||
|
|
||||||
|
updateTiledImageParameters(instance, tiledImage, positioningGroup, rotationAxis);
|
||||||
|
tiledImage.addHandler('bounds-change',()=>updateTiledImageParameters(instance, tiledImage, positioningGroup, rotationAxis, true));
|
||||||
|
tiledImage.addHandler('opacity-change',()=>updateTiledImageParameters(instance, tiledImage, positioningGroup, rotationAxis, true));
|
||||||
|
|
||||||
|
}
|
||||||
|
setItemOrder(null, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTiledImage(event){
|
||||||
|
let tiledImage = event.item;
|
||||||
|
//to do: make sure all resources for all tiles are unloaded (even if not actively in the tile group)
|
||||||
|
tiledImage._three.removeFromParent();
|
||||||
|
cleanupObject(tiledImage._three);
|
||||||
|
delete tiledImage._three;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTiledImageParameters(instance, tiledImage, positioningGroup, rotationAxis, requestRender){
|
||||||
|
let bounds = tiledImage.getBoundsNoRotate(true);
|
||||||
|
let rotation = tiledImage.getRotation(true);
|
||||||
|
|
||||||
|
//set size and location
|
||||||
|
positioningGroup.scale.x = bounds.width; //scale the normalized image coordinates to match the size within the world
|
||||||
|
positioningGroup.scale.y = -bounds.width; //flip Y
|
||||||
|
positioningGroup.position.x = bounds.x;
|
||||||
|
positioningGroup.position.y = bounds.y * -1;//flip Y
|
||||||
|
|
||||||
|
// rotate about the rotation axis
|
||||||
|
rotationAxis.rotation.z = rotation * Math.PI / 180;
|
||||||
|
rotationAxis.scale.x = tiledImage.getFlip() ? -1 : 1;
|
||||||
|
|
||||||
|
updateOpacity(tiledImage._three.userData._tileContainer, tiledImage.opacity);
|
||||||
|
|
||||||
|
if(requestRender){
|
||||||
|
instance.renderFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOpacity(meshGroup, opacity){
|
||||||
|
meshGroup.children.forEach(mesh=>{
|
||||||
|
mesh.material.opacity = opacity;
|
||||||
|
if(opacity < 1 || mesh.material.userData.hasTransparency){
|
||||||
|
mesh.material.transparent = true;
|
||||||
|
} else {
|
||||||
|
mesh.material.transparent = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTiledImageRendering(tiledImage, tile, instance){
|
||||||
|
updateMeshIfNeeded(tiledImage);
|
||||||
|
|
||||||
|
let tileContainer = tiledImage._three.userData._tileContainer;
|
||||||
|
let level = tileContainer.userData._tiledImageLevel;
|
||||||
|
|
||||||
|
//whether the tile was just loaded or unloaded, update any tiles that it overlaps in the current tileGrid (as needed)
|
||||||
|
let topLeft = tiledImage.source.getTileAtPoint(level, {x: tile.bounds.x, y: tile.bounds.y});
|
||||||
|
let bottomRight = tiledImage.source.getTileAtPoint(level, {x: tile.bounds.x + tile.bounds.width, y: tile.bounds.y + tile.bounds.height});
|
||||||
|
|
||||||
|
//iterate over the tiles overlapped by this one
|
||||||
|
let x, y;
|
||||||
|
for(x = topLeft.x; x<= bottomRight.x; x++){
|
||||||
|
for(y = topLeft.y; y <= bottomRight.y; y++){
|
||||||
|
let mesh = tileContainer.userData._tileMatrix[x][y];
|
||||||
|
loadBestImage(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.renderFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setItemOrder(event, instance){
|
||||||
|
instance._imageContainer.children.forEach(child=>{
|
||||||
|
child.position.z = DEPTH_MULTIPLIER * instance._viewer.world.getIndexOfItem(child._tiledImage);
|
||||||
|
});
|
||||||
|
instance.renderFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMeshIfNeeded(tiledImage){
|
||||||
|
let tileContainer = tiledImage._three.userData._tileContainer;
|
||||||
|
let levelsInterval = tiledImage._getLevelsInterval();
|
||||||
|
let level = levelsInterval.highestLevel;
|
||||||
|
|
||||||
|
if(tileContainer.userData._tiledImageLevel === level){
|
||||||
|
//we are already drawing the highest-resolution tiles, just return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('new level', level);
|
||||||
|
tileContainer.userData._tiledImageLevel = level;
|
||||||
|
//we need to update the grid.
|
||||||
|
//clear the old matrix
|
||||||
|
tileContainer.userData._tileMatrix = [];
|
||||||
|
//remove existing tiles
|
||||||
|
|
||||||
|
tileContainer.children.forEach(cleanupObject);
|
||||||
|
tileContainer.clear();
|
||||||
|
|
||||||
|
//create new set of tiles and add to the tileContainer
|
||||||
|
let gridInfo = tiledImage.getGridDefinition(level);
|
||||||
|
let col, row;
|
||||||
|
for(col = 0; col < gridInfo.numColumns; col += 1){
|
||||||
|
tileContainer.userData._tileMatrix[col] = [];
|
||||||
|
for(row = 0; row < gridInfo.numRows; row += 1){
|
||||||
|
let colInfo = gridInfo.columnInfo[col];
|
||||||
|
let rowInfo = gridInfo.rowInfo[row];
|
||||||
|
|
||||||
|
let left = colInfo.x;
|
||||||
|
let top = rowInfo.y;
|
||||||
|
let x = left + colInfo.width / 2;
|
||||||
|
let y = top + rowInfo.height / 2;
|
||||||
|
let z = 0;
|
||||||
|
|
||||||
|
let tileGeometry = new THREE.PlaneGeometry(colInfo.width, rowInfo.height);
|
||||||
|
let mesh = new THREE.Mesh(tileGeometry);
|
||||||
|
|
||||||
|
|
||||||
|
mesh.position.set(x, y, z);
|
||||||
|
|
||||||
|
mesh._tileInfo = {
|
||||||
|
row: row,
|
||||||
|
col: col,
|
||||||
|
level: level,
|
||||||
|
...rowInfo,
|
||||||
|
...colInfo,
|
||||||
|
tiledImage: tiledImage,
|
||||||
|
center: new OpenSeadragon.Point(x, y),
|
||||||
|
uvMapOriginal: [],
|
||||||
|
// uvMap
|
||||||
|
}
|
||||||
|
|
||||||
|
let i;
|
||||||
|
let uvAttribute = tileGeometry.attributes.uv;
|
||||||
|
|
||||||
|
for(i =0 ; i<uvAttribute.count; ++i){
|
||||||
|
let x = uvAttribute.getX(i);
|
||||||
|
let y = uvAttribute.getY(i);
|
||||||
|
mesh._tileInfo.uvMapOriginal[i] = [x, y];
|
||||||
|
}
|
||||||
|
|
||||||
|
tileContainer.add(mesh);
|
||||||
|
|
||||||
|
tileContainer.userData._tileMatrix[col][row] = mesh;
|
||||||
|
|
||||||
|
//get (current) best image data for the tile
|
||||||
|
loadBestImage(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadBestImage(mesh){
|
||||||
|
let tileInfo = mesh._tileInfo;
|
||||||
|
let tiledImage = tileInfo.tiledImage;
|
||||||
|
let tileSource = tiledImage.source;
|
||||||
|
let tilesMatrix = tiledImage.tilesMatrix;
|
||||||
|
|
||||||
|
//if we have our own texture, use it
|
||||||
|
// let tile = tilesMatrix[tileInfo.level][tileInfo.col][tileInfo.row];
|
||||||
|
let tile = hasMaterial(tilesMatrix, tileInfo.level, tileInfo.col, tileInfo.row);
|
||||||
|
if(tile){
|
||||||
|
addMaterialToMesh(mesh, tile, tiledImage);
|
||||||
|
} else {
|
||||||
|
//start at next highest level and work downward
|
||||||
|
let queryLevel = tileInfo.level - 1;
|
||||||
|
while(queryLevel >= 0){
|
||||||
|
let tileIndex = tileSource.getTileAtPoint(queryLevel, tileInfo.center);
|
||||||
|
let tile = hasMaterial(tilesMatrix, queryLevel, tileIndex.x, tileIndex.y);
|
||||||
|
if(tile){
|
||||||
|
addMaterialToMesh(mesh, tile, tiledImage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
queryLevel--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMaterial(tileMatrix, level, x, y){
|
||||||
|
let tile = tileMatrix[level] && tileMatrix[level][x] && tileMatrix[level][x][y];
|
||||||
|
return tile && tile._three ? tile : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMaterialToMesh(mesh, tile, tiledImage){
|
||||||
|
let regionInfo = mesh._tileInfo;
|
||||||
|
let material = tile._three;
|
||||||
|
let materialBounds = material.userData._tileBounds;
|
||||||
|
|
||||||
|
//update transparent and opacity properties to reflect current state
|
||||||
|
let opacity = tiledImage.opacity;
|
||||||
|
let transparent = opacity < 1 || material.userData.hasTransparency;
|
||||||
|
material.transparent = transparent;
|
||||||
|
material.opacity = opacity;
|
||||||
|
|
||||||
|
mesh.material = material;
|
||||||
|
let uvMap = mesh._tileInfo.uvMapOriginal;
|
||||||
|
let uvAttribute = mesh.geometry.attributes.uv;
|
||||||
|
|
||||||
|
// iterate over UV map for each vertex and calculate position within material/texture
|
||||||
|
let xNew, yNew;
|
||||||
|
let regionLeft = regionInfo.x;
|
||||||
|
let regionTop = regionInfo.y;
|
||||||
|
let regionRight = regionLeft + regionInfo.width;
|
||||||
|
let regionBottom = regionTop + regionInfo.height;
|
||||||
|
|
||||||
|
//what is needed to calculate the right uv index for each vertex?
|
||||||
|
// 1) position of the vertex in normalized coordinates
|
||||||
|
// 2) position of the entire texture area (not just non-overlapped area) in normalized coordinates
|
||||||
|
|
||||||
|
uvMap.forEach(([x,y],i)=>{
|
||||||
|
// x, y describe which corner of the original texture to use
|
||||||
|
if(x==0){
|
||||||
|
xNew = (regionLeft - materialBounds.x) / materialBounds.width;
|
||||||
|
} else {
|
||||||
|
xNew = (regionRight - materialBounds.x) / materialBounds.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(y == 0){
|
||||||
|
yNew = (regionTop - materialBounds.y) / materialBounds.height;
|
||||||
|
} else {
|
||||||
|
yNew = (regionBottom - materialBounds.y) / materialBounds.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
uvAttribute.setXY(i, xNew, yNew);
|
||||||
|
});
|
||||||
|
uvAttribute.needsUpdate = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function cleanupObject(object){
|
||||||
|
if(object.children && object.children.forEach){
|
||||||
|
object.children.forEach(cleanupObject);
|
||||||
|
}
|
||||||
|
if(object.dispose){
|
||||||
|
object.dispose();
|
||||||
|
}
|
||||||
|
if(object.geometry){
|
||||||
|
object.geometry.dispose();
|
||||||
|
}
|
||||||
|
}
|
92
test/demo/webgl.html
Normal file
92
test/demo/webgl.html
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OSD-ThreeJS</title>
|
||||||
|
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
|
||||||
|
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
|
||||||
|
<script type="text/javascript" src='../lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js'></script>
|
||||||
|
<link rel="stylesheet" href="../lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
|
||||||
|
<!-- <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script> -->
|
||||||
|
<!-- <script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.149.0/three.min.js"
|
||||||
|
}
|
||||||
|
} -->
|
||||||
|
</script>
|
||||||
|
<script type="module" src="./webgl.js"></script>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
.openseadragon1 {
|
||||||
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#three-canvas{
|
||||||
|
position:fixed;
|
||||||
|
top:5px;
|
||||||
|
right:5px;
|
||||||
|
border: thin gray solid;
|
||||||
|
}
|
||||||
|
.image-options{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2em repeat(7, 10em);
|
||||||
|
}
|
||||||
|
.image-options input[type=number]{
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
Simple demo page to show three.js based rendering.
|
||||||
|
</div>
|
||||||
|
<div id="contentDiv" class="openseadragon1"></div>
|
||||||
|
|
||||||
|
<div id="image-picker">
|
||||||
|
<div class="image-options">
|
||||||
|
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
|
||||||
|
<label><input type="checkbox" checked data-image="rainbow" class="toggle"> Rainbow Grid</label>
|
||||||
|
<label>X: <input type="number" value="0" data-image="rainbow" data-field="x"> </label>
|
||||||
|
<label>Y: <input type="number" value="0" data-image="rainbow" data-field="y"> </label>
|
||||||
|
<label>Width: <input type="number" value="1" data-image="rainbow" data-field="width" min="0"> </label>
|
||||||
|
<label>Degrees: <input type="number" value="0" data-image="rainbow" data-field="degrees"> </label>
|
||||||
|
<label>Opacity: <input type="number" value="1" data-image="rainbow" data-field="opacity" min="0" max="1" step="0.2"> </label>
|
||||||
|
<label>Flipped: <input type="checkbox" data-image="rainbow" data-field="flipped"></label>
|
||||||
|
</div>
|
||||||
|
<div class="image-options">
|
||||||
|
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
|
||||||
|
<label><input type="checkbox" data-image="leaves" class="toggle"> Leaves</label>
|
||||||
|
<label>X: <input type="number" value="0.5" data-image="leaves" data-field="x"> </label>
|
||||||
|
<label>Y: <input type="number" value="0.5" data-image="leaves" data-field="y"> </label>
|
||||||
|
<label>Width: <input type="number" value="1.5" data-image="leaves" data-field="width" min="0"> </label>
|
||||||
|
<label>Degrees: <input type="number" value="0" data-image="leaves" data-field="degrees"> </label>
|
||||||
|
<label>Opacity: <input type="number" value="0.8" data-image="leaves" data-field="opacity" min="0" max="1" step="0.2"> </label>
|
||||||
|
<label>Flipped: <input type="checkbox" data-image="leaves" data-field="flipped"></label>
|
||||||
|
</div>
|
||||||
|
<div class="image-options">
|
||||||
|
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
|
||||||
|
<label><input type="checkbox" data-image="bblue" class="toggle"> BBlue PNG</label>
|
||||||
|
<label>X: <input type="number" value="0" data-image="bblue" data-field="x"> </label>
|
||||||
|
<label>Y: <input type="number" value="0" data-image="bblue" data-field="y"> </label>
|
||||||
|
<label>Width: <input type="number" value="0.5" data-image="bblue" data-field="width" min="0"> </label>
|
||||||
|
<label>Degrees: <input type="number" value="0" data-image="bblue" data-field="degrees"> </label>
|
||||||
|
<label>Opacity: <input type="number" value="1" data-image="bblue" data-field="opacity" min="0" max="1" step="0.2"> </label>
|
||||||
|
<label>Flipped: <input type="checkbox" data-image="bblue" data-field="flipped"></label>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="image-options">
|
||||||
|
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
|
||||||
|
<label><input type="checkbox" data-image="duomo" class="toggle"> Duomo</label>
|
||||||
|
<label>X: <input type="number" value="0" data-image="duomo" data-field="x"> </label>
|
||||||
|
<label>Y: <input type="number" value="0" data-image="duomo" data-field="y"> </label>
|
||||||
|
<label>Width: <input type="number" value="0.5" data-image="duomo" data-field="width"> </label>
|
||||||
|
<label>Degrees: <input type="number" value="0" data-image="duomo" data-field="degrees"> </label>
|
||||||
|
<label>Opacity: <input type="number" value="1" data-image="duomo" data-field="opacity"> </label>
|
||||||
|
<label>Flipped: <input type="checkbox" data-image="duomo" data-field="flipped"></label>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<canvas id="three-canvas"></canvas>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
126
test/demo/webgl.js
Normal file
126
test/demo/webgl.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
//imports
|
||||||
|
import { ThreeJSRenderer } from './webgl-renderer.js';
|
||||||
|
|
||||||
|
//globals
|
||||||
|
const canvas = document.querySelector('#three-canvas');
|
||||||
|
const sources = {
|
||||||
|
"rainbow":"../data/testpattern.dzi",
|
||||||
|
"leaves":"../data/iiif_2_0_sizes/info.json",
|
||||||
|
"bblue":{
|
||||||
|
type:'image',
|
||||||
|
url: "../data/BBlue.png",
|
||||||
|
},
|
||||||
|
// "duomo":"https://openseadragon.github.io/example-images/highsmith/highsmith.dzi"
|
||||||
|
}
|
||||||
|
var viewer = window.viewer = OpenSeadragon({
|
||||||
|
// debugMode: true,
|
||||||
|
id: "contentDiv",
|
||||||
|
prefixUrl: "../../build/openseadragon/images/",
|
||||||
|
showNavigator:true,
|
||||||
|
minZoomImageRatio:0.001,
|
||||||
|
customRenderer: true, // set this to true to use a renderer plugin instead of the built-in drawer
|
||||||
|
useCanvas: {contextType: 'webgl2'} //set this to match the context type used by the plugin renderer
|
||||||
|
});
|
||||||
|
|
||||||
|
//sync size
|
||||||
|
|
||||||
|
// let viewerCanvas = viewer.drawer.canvas;
|
||||||
|
// canvas.style.width = viewerCanvas.clientWidth+'px';
|
||||||
|
// canvas.style.height = viewerCanvas.clientHeight+'px';
|
||||||
|
// canvas.width = viewerCanvas.width;
|
||||||
|
// canvas.height = viewerCanvas.height;
|
||||||
|
|
||||||
|
// //make the test canvas mirror all changes to the viewer canvas
|
||||||
|
// viewer.addHandler("resize", function(){
|
||||||
|
// canvas.style.width = viewerCanvas.clientWidth+'px';
|
||||||
|
// canvas.style.height = viewerCanvas.clientHeight+'px';
|
||||||
|
// })
|
||||||
|
let noCanvas;
|
||||||
|
|
||||||
|
let threeRenderer = window.threeRenderer = new ThreeJSRenderer(viewer, noCanvas);
|
||||||
|
|
||||||
|
// viewer.addHandler("open", () => viewer.world.getItemAt(0).source.hasTransparency = function(){ return true; });
|
||||||
|
|
||||||
|
$('#contentDiv').resizable(true);
|
||||||
|
$('#image-picker').sortable({
|
||||||
|
update: function(event, ui){
|
||||||
|
let thisItem = ui.item.find('.toggle').data('item');
|
||||||
|
let items = $('#image-picker input.toggle:checked').toArray().map(item=>$(item).data('item'));
|
||||||
|
let newIndex = items.indexOf(thisItem);
|
||||||
|
if(thisItem){
|
||||||
|
viewer.world.setItemIndex(thisItem, newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#image-picker input.toggle').on('change',function(){
|
||||||
|
let data = $(this).data();
|
||||||
|
if(this.checked){
|
||||||
|
addTileSource(data.image, this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(data.item){
|
||||||
|
viewer.world.removeItem(data.item);
|
||||||
|
$(this).data('item',null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).trigger('change');
|
||||||
|
|
||||||
|
$('#image-picker input:not(.toggle)').on('change',function(){
|
||||||
|
let data = $(this).data();
|
||||||
|
let value = $(this).val();
|
||||||
|
let tiledImage = $(`#image-picker input.toggle[data-image=${data.image}]`).data('item');
|
||||||
|
if(tiledImage){
|
||||||
|
//item = tiledImage
|
||||||
|
let field = data.field;
|
||||||
|
if(field == 'x'){
|
||||||
|
let bounds = tiledImage.getBoundsNoRotate();
|
||||||
|
let position = new OpenSeadragon.Point(Number(value), bounds.y);
|
||||||
|
tiledImage.setPosition(position);
|
||||||
|
} else if ( field == 'y'){
|
||||||
|
let bounds = tiledImage.getBoundsNoRotate();
|
||||||
|
let position = new OpenSeadragon.Point(bounds.x, Number(value));
|
||||||
|
tiledImage.setPosition(position);
|
||||||
|
} else if (field == 'width'){
|
||||||
|
tiledImage.setWidth(Number(value));
|
||||||
|
} else if (field == 'degrees'){
|
||||||
|
tiledImage.setRotation(Number(value));
|
||||||
|
} else if (field == 'opacity'){
|
||||||
|
tiledImage.setOpacity(Number(value));
|
||||||
|
} else if (field == 'flipped'){
|
||||||
|
tiledImage.setFlip($(this).prop('checked'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function addTileSource(image, checkbox){
|
||||||
|
let options = $(`#image-picker input[data-image=${image}][type=number]`).toArray().reduce((acc, input)=>{
|
||||||
|
let field = $(input).data('field');
|
||||||
|
if(field){
|
||||||
|
acc[field] = Number(input.value);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
options.flipped = $(`#image-picker input[data-image=${image}][data-type=flipped]`).prop('checked');
|
||||||
|
|
||||||
|
let items = $('#image-picker input.toggle:checked').toArray();
|
||||||
|
let insertionIndex = items.indexOf(checkbox);
|
||||||
|
|
||||||
|
let tileSource = sources[image];
|
||||||
|
if(tileSource){
|
||||||
|
viewer.addTiledImage({tileSource: tileSource, ...options, index: insertionIndex});
|
||||||
|
viewer.world.addOnceHandler('add-item',function(ev){
|
||||||
|
let item = ev.item;
|
||||||
|
$(checkbox).data('item',item);
|
||||||
|
item.source.hasTransparency = ()=>true; //simulate image with transparency, to show seams in default renderer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
50598
test/lib/three.js
Normal file
50598
test/lib/three.js
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user