hefeihvac_java/node_modules/echarts-gl/lib/util/ZRTextureAtlasSurface.js

344 lines
7.9 KiB
JavaScript
Raw Permalink Normal View History

2024-04-07 18:15:00 +08:00
/**
* Texture Atlas for the sprites.
* It uses zrender for 2d element management and rendering
* @module echarts-gl/util/ZRTextureAtlasSurface
*/
// TODO Expand.
import * as echarts from 'echarts/lib/echarts';
import Texture2D from 'claygl/src/Texture2D';
function ZRTextureAtlasSurfaceNode(zr, offsetX, offsetY, width, height, gap, dpr) {
this._zr = zr;
/**
* Current cursor x
* @type {number}
* @private
*/
this._x = 0;
/**
* Current cursor y
* @type {number}
*/
this._y = 0;
this._rowHeight = 0;
/**
* width without dpr.
* @type {number}
* @private
*/
this.width = width;
/**
* height without dpr.
* @type {number}
* @private
*/
this.height = height;
/**
* offsetX without dpr
* @type {number}
*/
this.offsetX = offsetX;
/**
* offsetY without dpr
* @type {number}
*/
this.offsetY = offsetY;
this.dpr = dpr;
this.gap = gap;
}
ZRTextureAtlasSurfaceNode.prototype = {
constructor: ZRTextureAtlasSurfaceNode,
clear: function () {
this._x = 0;
this._y = 0;
this._rowHeight = 0;
},
/**
* Add shape to atlas
* @param {module:zrender/graphic/Displayable} shape
* @param {number} width
* @param {number} height
* @return {Array}
*/
add: function (el, width, height) {
// FIXME Text element not consider textAlign and textVerticalAlign.
// TODO, inner text, shadow
var rect = el.getBoundingRect(); // FIXME aspect ratio
if (width == null) {
width = rect.width;
}
if (height == null) {
height = rect.height;
}
width *= this.dpr;
height *= this.dpr;
this._fitElement(el, width, height); // var aspect = el.scale[1] / el.scale[0];
// Adjust aspect ratio to make the text more clearly
// FIXME If height > width, width is useless ?
// width = height * aspect;
// el.position[0] *= aspect;
// el.scale[0] = el.scale[1];
var x = this._x;
var y = this._y;
var canvasWidth = this.width * this.dpr;
var canvasHeight = this.height * this.dpr;
var gap = this.gap;
if (x + width + gap > canvasWidth) {
// Change a new row
x = this._x = 0;
y += this._rowHeight + gap;
this._y = y; // Reset row height
this._rowHeight = 0;
}
this._x += width + gap;
this._rowHeight = Math.max(this._rowHeight, height);
if (y + height + gap > canvasHeight) {
// There is no space anymore
return null;
} // Shift the el
el.x += this.offsetX * this.dpr + x;
el.y += this.offsetY * this.dpr + y;
this._zr.add(el);
var coordsOffset = [this.offsetX / this.width, this.offsetY / this.height];
var coords = [[x / canvasWidth + coordsOffset[0], y / canvasHeight + coordsOffset[1]], [(x + width) / canvasWidth + coordsOffset[0], (y + height) / canvasHeight + coordsOffset[1]]];
return coords;
},
/**
* Fit element size by correct its position and scaling
* @param {module:zrender/graphic/Displayable} el
* @param {number} spriteWidth
* @param {number} spriteHeight
*/
_fitElement: function (el, spriteWidth, spriteHeight) {
// TODO, inner text, shadow
var rect = el.getBoundingRect();
var scaleX = spriteWidth / rect.width;
var scaleY = spriteHeight / rect.height;
el.x = -rect.x * scaleX;
el.y = -rect.y * scaleY;
el.scaleX = scaleX;
el.scaleY = scaleY;
el.update();
}
};
/**
* constructor
* @alias module:echarts-gl/util/ZRTextureAtlasSurface
* @param {number} opt.width
* @param {number} opt.height
* @param {number} opt.devicePixelRatio
* @param {number} opt.gap Gap for safe.
* @param {Function} opt.onupdate
*/
function ZRTextureAtlasSurface(opt) {
opt = opt || {};
opt.width = opt.width || 512;
opt.height = opt.height || 512;
opt.devicePixelRatio = opt.devicePixelRatio || 1;
opt.gap = opt.gap == null ? 2 : opt.gap;
var canvas = document.createElement('canvas');
canvas.width = opt.width * opt.devicePixelRatio;
canvas.height = opt.height * opt.devicePixelRatio;
this._canvas = canvas;
this._texture = new Texture2D({
image: canvas,
flipY: false
});
var self = this;
/**
* zrender instance in the Chart
* @type {zrender~ZRender}
*/
this._zr = echarts.zrender.init(canvas);
var oldRefreshImmediately = this._zr.refreshImmediately;
this._zr.refreshImmediately = function () {
oldRefreshImmediately.call(this);
self._texture.dirty();
self.onupdate && self.onupdate();
};
this._dpr = opt.devicePixelRatio;
/**
* Texture coords map for each sprite image
* @type {Object}
*/
this._coords = {};
this.onupdate = opt.onupdate;
this._gap = opt.gap; // Left sub atlas.
this._textureAtlasNodes = [new ZRTextureAtlasSurfaceNode(this._zr, 0, 0, opt.width, opt.height, this._gap, this._dpr)];
this._nodeWidth = opt.width;
this._nodeHeight = opt.height;
this._currentNodeIdx = 0;
}
ZRTextureAtlasSurface.prototype = {
/**
* Clear the texture atlas
*/
clear: function () {
for (var i = 0; i < this._textureAtlasNodes.length; i++) {
this._textureAtlasNodes[i].clear();
}
this._currentNodeIdx = 0;
this._zr.clear();
this._coords = {};
},
/**
* @return {number}
*/
getWidth: function () {
return this._width;
},
/**
* @return {number}
*/
getHeight: function () {
return this._height;
},
/**
* @return {number}
*/
getTexture: function () {
return this._texture;
},
/**
* @return {number}
*/
getDevicePixelRatio: function () {
return this._dpr;
},
getZr: function () {
return this._zr;
},
_getCurrentNode: function () {
return this._textureAtlasNodes[this._currentNodeIdx];
},
_expand: function () {
this._currentNodeIdx++;
if (this._textureAtlasNodes[this._currentNodeIdx]) {
// Use the node created previously.
return this._textureAtlasNodes[this._currentNodeIdx];
}
var maxSize = 4096 / this._dpr;
var textureAtlasNodes = this._textureAtlasNodes;
var nodeLen = textureAtlasNodes.length;
var offsetX = nodeLen * this._nodeWidth % maxSize;
var offsetY = Math.floor(nodeLen * this._nodeWidth / maxSize) * this._nodeHeight;
if (offsetY >= maxSize) {
// Failed if image is too large.
if (process.env.NODE_ENV !== 'production') {
console.error('Too much labels. Some will be ignored.');
}
return;
}
var width = (offsetX + this._nodeWidth) * this._dpr;
var height = (offsetY + this._nodeHeight) * this._dpr;
try {
// Resize will error in node.
this._zr.resize({
width: width,
height: height
});
} catch (e) {
this._canvas.width = width;
this._canvas.height = height;
}
var newNode = new ZRTextureAtlasSurfaceNode(this._zr, offsetX, offsetY, this._nodeWidth, this._nodeHeight, this._gap, this._dpr);
this._textureAtlasNodes.push(newNode);
return newNode;
},
add: function (el, width, height) {
if (this._coords[el.id]) {
if (process.env.NODE_ENV !== 'production') {
console.warn('Element already been add');
}
return this._coords[el.id];
}
var coords = this._getCurrentNode().add(el, width, height);
if (!coords) {
var newNode = this._expand();
if (!newNode) {
// To maximum
return;
}
coords = newNode.add(el, width, height);
}
this._coords[el.id] = coords;
return coords;
},
/**
* Get coord scale after texture atlas is expanded.
* @return {Array.<number>}
*/
getCoordsScale: function () {
var dpr = this._dpr;
return [this._nodeWidth / this._canvas.width * dpr, this._nodeHeight / this._canvas.height * dpr];
},
/**
* Get texture coords of sprite image
* @param {string} id Image id
* @return {Array}
*/
getCoords: function (id) {
return this._coords[id];
},
dispose: function () {
this._zr.dispose();
}
};
export default ZRTextureAtlasSurface;