123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var graphic = require("../../util/graphic");
- var _symbol = require("../../util/symbol");
- var createSymbol = _symbol.createSymbol;
- var IncrementalDisplayable = require("zrender/lib/graphic/IncrementalDisplayable");
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- /* global Float32Array */
- // TODO Batch by color
- var BOOST_SIZE_THRESHOLD = 4;
- var LargeSymbolPath = graphic.extendShape({
- shape: {
- points: null
- },
- symbolProxy: null,
- softClipShape: null,
- buildPath: function (path, shape) {
- var points = shape.points;
- var size = shape.size;
- var symbolProxy = this.symbolProxy;
- var symbolProxyShape = symbolProxy.shape;
- var ctx = path.getContext ? path.getContext() : path;
- var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD; // Do draw in afterBrush.
- if (canBoost) {
- return;
- }
- for (var i = 0; i < points.length;) {
- var x = points[i++];
- var y = points[i++];
- if (isNaN(x) || isNaN(y)) {
- continue;
- }
- if (this.softClipShape && !this.softClipShape.contain(x, y)) {
- continue;
- }
- symbolProxyShape.x = x - size[0] / 2;
- symbolProxyShape.y = y - size[1] / 2;
- symbolProxyShape.width = size[0];
- symbolProxyShape.height = size[1];
- symbolProxy.buildPath(path, symbolProxyShape, true);
- }
- },
- afterBrush: function (ctx) {
- var shape = this.shape;
- var points = shape.points;
- var size = shape.size;
- var canBoost = size[0] < BOOST_SIZE_THRESHOLD;
- if (!canBoost) {
- return;
- }
- this.setTransform(ctx); // PENDING If style or other canvas status changed?
- for (var i = 0; i < points.length;) {
- var x = points[i++];
- var y = points[i++];
- if (isNaN(x) || isNaN(y)) {
- continue;
- }
- if (this.softClipShape && !this.softClipShape.contain(x, y)) {
- continue;
- } // fillRect is faster than building a rect path and draw.
- // And it support light globalCompositeOperation.
- ctx.fillRect(x - size[0] / 2, y - size[1] / 2, size[0], size[1]);
- }
- this.restoreTransform(ctx);
- },
- findDataIndex: function (x, y) {
- // TODO ???
- // Consider transform
- var shape = this.shape;
- var points = shape.points;
- var size = shape.size;
- var w = Math.max(size[0], 4);
- var h = Math.max(size[1], 4); // Not consider transform
- // Treat each element as a rect
- // top down traverse
- for (var idx = points.length / 2 - 1; idx >= 0; idx--) {
- var i = idx * 2;
- var x0 = points[i] - w / 2;
- var y0 = points[i + 1] - h / 2;
- if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {
- return idx;
- }
- }
- return -1;
- }
- });
- function LargeSymbolDraw() {
- this.group = new graphic.Group();
- }
- var largeSymbolProto = LargeSymbolDraw.prototype;
- largeSymbolProto.isPersistent = function () {
- return !this._incremental;
- };
- /**
- * Update symbols draw by new data
- * @param {module:echarts/data/List} data
- * @param {Object} opt
- * @param {Object} [opt.clipShape]
- */
- largeSymbolProto.updateData = function (data, opt) {
- this.group.removeAll();
- var symbolEl = new LargeSymbolPath({
- rectHover: true,
- cursor: 'default'
- });
- symbolEl.setShape({
- points: data.getLayout('symbolPoints')
- });
- this._setCommon(symbolEl, data, false, opt);
- this.group.add(symbolEl);
- this._incremental = null;
- };
- largeSymbolProto.updateLayout = function (data) {
- if (this._incremental) {
- return;
- }
- var points = data.getLayout('symbolPoints');
- this.group.eachChild(function (child) {
- if (child.startIndex != null) {
- var len = (child.endIndex - child.startIndex) * 2;
- var byteOffset = child.startIndex * 4 * 2;
- points = new Float32Array(points.buffer, byteOffset, len);
- }
- child.setShape('points', points);
- });
- };
- largeSymbolProto.incrementalPrepareUpdate = function (data) {
- this.group.removeAll();
- this._clearIncremental(); // Only use incremental displayables when data amount is larger than 2 million.
- // PENDING Incremental data?
- if (data.count() > 2e6) {
- if (!this._incremental) {
- this._incremental = new IncrementalDisplayable({
- silent: true
- });
- }
- this.group.add(this._incremental);
- } else {
- this._incremental = null;
- }
- };
- largeSymbolProto.incrementalUpdate = function (taskParams, data, opt) {
- var symbolEl;
- if (this._incremental) {
- symbolEl = new LargeSymbolPath();
- this._incremental.addDisplayable(symbolEl, true);
- } else {
- symbolEl = new LargeSymbolPath({
- rectHover: true,
- cursor: 'default',
- startIndex: taskParams.start,
- endIndex: taskParams.end
- });
- symbolEl.incremental = true;
- this.group.add(symbolEl);
- }
- symbolEl.setShape({
- points: data.getLayout('symbolPoints')
- });
- this._setCommon(symbolEl, data, !!this._incremental, opt);
- };
- largeSymbolProto._setCommon = function (symbolEl, data, isIncremental, opt) {
- var hostModel = data.hostModel;
- opt = opt || {}; // TODO
- // if (data.hasItemVisual.symbolSize) {
- // // TODO typed array?
- // symbolEl.setShape('sizes', data.mapArray(
- // function (idx) {
- // var size = data.getItemVisual(idx, 'symbolSize');
- // return (size instanceof Array) ? size : [size, size];
- // }
- // ));
- // }
- // else {
- var size = data.getVisual('symbolSize');
- symbolEl.setShape('size', size instanceof Array ? size : [size, size]); // }
- symbolEl.softClipShape = opt.clipShape || null; // Create symbolProxy to build path for each data
- symbolEl.symbolProxy = createSymbol(data.getVisual('symbol'), 0, 0, 0, 0); // Use symbolProxy setColor method
- symbolEl.setColor = symbolEl.symbolProxy.setColor;
- var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;
- symbolEl.useStyle( // Draw shadow when doing fillRect is extremely slow.
- hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']));
- var visualColor = data.getVisual('color');
- if (visualColor) {
- symbolEl.setColor(visualColor);
- }
- if (!isIncremental) {
- // Enable tooltip
- // PENDING May have performance issue when path is extremely large
- symbolEl.seriesIndex = hostModel.seriesIndex;
- symbolEl.on('mousemove', function (e) {
- symbolEl.dataIndex = null;
- var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY);
- if (dataIndex >= 0) {
- // Provide dataIndex for tooltip
- symbolEl.dataIndex = dataIndex + (symbolEl.startIndex || 0);
- }
- });
- }
- };
- largeSymbolProto.remove = function () {
- this._clearIncremental();
- this._incremental = null;
- this.group.removeAll();
- };
- largeSymbolProto._clearIncremental = function () {
- var incremental = this._incremental;
- if (incremental) {
- incremental.clearDisplaybles();
- }
- };
- var _default = LargeSymbolDraw;
- module.exports = _default;
|