You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					504 lines
				
				16 KiB
			
		
		
			
		
	
	
					504 lines
				
				16 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								* 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 _config = require("../config");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var __DEV__ = _config.__DEV__;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var echarts = require("../echarts");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var zrUtil = require("zrender/lib/core/util");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var modelUtil = require("../util/model");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var graphicUtil = require("../util/graphic");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var layoutUtil = require("../util/layout");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								* 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.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								// -------------
							 | 
						||
| 
								 | 
							
								// Preprocessor
							 | 
						||
| 
								 | 
							
								// -------------
							 | 
						||
| 
								 | 
							
								echarts.registerPreprocessor(function (option) {
							 | 
						||
| 
								 | 
							
								  var graphicOption = option.graphic; // Convert
							 | 
						||
| 
								 | 
							
								  // {graphic: [{left: 10, type: 'circle'}, ...]}
							 | 
						||
| 
								 | 
							
								  // or
							 | 
						||
| 
								 | 
							
								  // {graphic: {left: 10, type: 'circle'}}
							 | 
						||
| 
								 | 
							
								  // to
							 | 
						||
| 
								 | 
							
								  // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (zrUtil.isArray(graphicOption)) {
							 | 
						||
| 
								 | 
							
								    if (!graphicOption[0] || !graphicOption[0].elements) {
							 | 
						||
| 
								 | 
							
								      option.graphic = [{
							 | 
						||
| 
								 | 
							
								        elements: graphicOption
							 | 
						||
| 
								 | 
							
								      }];
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      // Only one graphic instance can be instantiated. (We dont
							 | 
						||
| 
								 | 
							
								      // want that too many views are created in echarts._viewMap)
							 | 
						||
| 
								 | 
							
								      option.graphic = [option.graphic[0]];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if (graphicOption && !graphicOption.elements) {
							 | 
						||
| 
								 | 
							
								    option.graphic = [{
							 | 
						||
| 
								 | 
							
								      elements: [graphicOption]
							 | 
						||
| 
								 | 
							
								    }];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}); // ------
							 | 
						||
| 
								 | 
							
								// Model
							 | 
						||
| 
								 | 
							
								// ------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var GraphicModel = echarts.extendComponentModel({
							 | 
						||
| 
								 | 
							
								  type: 'graphic',
							 | 
						||
| 
								 | 
							
								  defaultOption: {
							 | 
						||
| 
								 | 
							
								    // Extra properties for each elements:
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // left/right/top/bottom: (like 12, '22%', 'center', default undefined)
							 | 
						||
| 
								 | 
							
								    //      If left/rigth is set, shape.x/shape.cx/position will not be used.
							 | 
						||
| 
								 | 
							
								    //      If top/bottom is set, shape.y/shape.cy/position will not be used.
							 | 
						||
| 
								 | 
							
								    //      This mechanism is useful when you want to position a group/element
							 | 
						||
| 
								 | 
							
								    //      against the right side or the center of this container.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // width/height: (can only be pixel value, default 0)
							 | 
						||
| 
								 | 
							
								    //      Only be used to specify contianer(group) size, if needed. And
							 | 
						||
| 
								 | 
							
								    //      can not be percentage value (like '33%'). See the reason in the
							 | 
						||
| 
								 | 
							
								    //      layout algorithm below.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // bounding: (enum: 'all' (default) | 'raw')
							 | 
						||
| 
								 | 
							
								    //      Specify how to calculate boundingRect when locating.
							 | 
						||
| 
								 | 
							
								    //      'all': Get uioned and transformed boundingRect
							 | 
						||
| 
								 | 
							
								    //          from both itself and its descendants.
							 | 
						||
| 
								 | 
							
								    //          This mode simplies confining a group of elements in the bounding
							 | 
						||
| 
								 | 
							
								    //          of their ancester container (e.g., using 'right: 0').
							 | 
						||
| 
								 | 
							
								    //      'raw': Only use the boundingRect of itself and before transformed.
							 | 
						||
| 
								 | 
							
								    //          This mode is similar to css behavior, which is useful when you
							 | 
						||
| 
								 | 
							
								    //          want an element to be able to overflow its container. (Consider
							 | 
						||
| 
								 | 
							
								    //          a rotated circle needs to be located in a corner.)
							 | 
						||
| 
								 | 
							
								    // info: custom info. enables user to mount some info on elements and use them
							 | 
						||
| 
								 | 
							
								    //      in event handlers. Update them only when user specified, otherwise, remain.
							 | 
						||
| 
								 | 
							
								    // Note: elements is always behind its ancestors in this elements array.
							 | 
						||
| 
								 | 
							
								    elements: [],
							 | 
						||
| 
								 | 
							
								    parentId: null
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Save el options for the sake of the performance (only update modified graphics).
							 | 
						||
| 
								 | 
							
								   * The order is the same as those in option. (ancesters -> descendants)
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @private
							 | 
						||
| 
								 | 
							
								   * @type {Array.<Object>}
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  _elOptionsToUpdate: null,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @override
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  mergeOption: function (option) {
							 | 
						||
| 
								 | 
							
								    // Prevent default merge to elements
							 | 
						||
| 
								 | 
							
								    var elements = this.option.elements;
							 | 
						||
| 
								 | 
							
								    this.option.elements = null;
							 | 
						||
| 
								 | 
							
								    GraphicModel.superApply(this, 'mergeOption', arguments);
							 | 
						||
| 
								 | 
							
								    this.option.elements = elements;
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @override
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  optionUpdated: function (newOption, isInit) {
							 | 
						||
| 
								 | 
							
								    var thisOption = this.option;
							 | 
						||
| 
								 | 
							
								    var newList = (isInit ? thisOption : newOption).elements;
							 | 
						||
| 
								 | 
							
								    var existList = thisOption.elements = isInit ? [] : thisOption.elements;
							 | 
						||
| 
								 | 
							
								    var flattenedList = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._flatten(newList, flattenedList);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var mappingResult = modelUtil.mappingToExists(existList, flattenedList);
							 | 
						||
| 
								 | 
							
								    modelUtil.makeIdAndName(mappingResult); // Clear elOptionsToUpdate
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var elOptionsToUpdate = this._elOptionsToUpdate = [];
							 | 
						||
| 
								 | 
							
								    zrUtil.each(mappingResult, function (resultItem, index) {
							 | 
						||
| 
								 | 
							
								      var newElOption = resultItem.option;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!newElOption) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      elOptionsToUpdate.push(newElOption);
							 | 
						||
| 
								 | 
							
								      setKeyInfoToNewElOption(resultItem, newElOption);
							 | 
						||
| 
								 | 
							
								      mergeNewElOptionToExist(existList, index, newElOption);
							 | 
						||
| 
								 | 
							
								      setLayoutInfoToExist(existList[index], newElOption);
							 | 
						||
| 
								 | 
							
								    }, this); // Clean
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (var i = existList.length - 1; i >= 0; i--) {
							 | 
						||
| 
								 | 
							
								      if (existList[i] == null) {
							 | 
						||
| 
								 | 
							
								        existList.splice(i, 1);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        // $action should be volatile, otherwise option gotten from
							 | 
						||
| 
								 | 
							
								        // `getOption` will contain unexpected $action.
							 | 
						||
| 
								 | 
							
								        delete existList[i].$action;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Convert
							 | 
						||
| 
								 | 
							
								   * [{
							 | 
						||
| 
								 | 
							
								   *  type: 'group',
							 | 
						||
| 
								 | 
							
								   *  id: 'xx',
							 | 
						||
| 
								 | 
							
								   *  children: [{type: 'circle'}, {type: 'polygon'}]
							 | 
						||
| 
								 | 
							
								   * }]
							 | 
						||
| 
								 | 
							
								   * to
							 | 
						||
| 
								 | 
							
								   * [
							 | 
						||
| 
								 | 
							
								   *  {type: 'group', id: 'xx'},
							 | 
						||
| 
								 | 
							
								   *  {type: 'circle', parentId: 'xx'},
							 | 
						||
| 
								 | 
							
								   *  {type: 'polygon', parentId: 'xx'}
							 | 
						||
| 
								 | 
							
								   * ]
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @private
							 | 
						||
| 
								 | 
							
								   * @param {Array.<Object>} optionList option list
							 | 
						||
| 
								 | 
							
								   * @param {Array.<Object>} result result of flatten
							 | 
						||
| 
								 | 
							
								   * @param {Object} parentOption parent option
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  _flatten: function (optionList, result, parentOption) {
							 | 
						||
| 
								 | 
							
								    zrUtil.each(optionList, function (option) {
							 | 
						||
| 
								 | 
							
								      if (!option) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (parentOption) {
							 | 
						||
| 
								 | 
							
								        option.parentOption = parentOption;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      result.push(option);
							 | 
						||
| 
								 | 
							
								      var children = option.children;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (option.type === 'group' && children) {
							 | 
						||
| 
								 | 
							
								        this._flatten(children, result, option);
							 | 
						||
| 
								 | 
							
								      } // Deleting for JSON output, and for not affecting group creation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      delete option.children;
							 | 
						||
| 
								 | 
							
								    }, this);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  // FIXME
							 | 
						||
| 
								 | 
							
								  // Pass to view using payload? setOption has a payload?
							 | 
						||
| 
								 | 
							
								  useElOptionsToUpdate: function () {
							 | 
						||
| 
								 | 
							
								    var els = this._elOptionsToUpdate; // Clear to avoid render duplicately when zooming.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._elOptionsToUpdate = null;
							 | 
						||
| 
								 | 
							
								    return els;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}); // -----
							 | 
						||
| 
								 | 
							
								// View
							 | 
						||
| 
								 | 
							
								// -----
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								echarts.extendComponentView({
							 | 
						||
| 
								 | 
							
								  type: 'graphic',
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @override
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  init: function (ecModel, api) {
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @private
							 | 
						||
| 
								 | 
							
								     * @type {module:zrender/core/util.HashMap}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    this._elMap = zrUtil.createHashMap();
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @private
							 | 
						||
| 
								 | 
							
								     * @type {module:echarts/graphic/GraphicModel}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._lastGraphicModel;
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @override
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  render: function (graphicModel, ecModel, api) {
							 | 
						||
| 
								 | 
							
								    // Having leveraged between use cases and algorithm complexity, a very
							 | 
						||
| 
								 | 
							
								    // simple layout mechanism is used:
							 | 
						||
| 
								 | 
							
								    // The size(width/height) can be determined by itself or its parent (not
							 | 
						||
| 
								 | 
							
								    // implemented yet), but can not by its children. (Top-down travel)
							 | 
						||
| 
								 | 
							
								    // The location(x/y) can be determined by the bounding rect of itself
							 | 
						||
| 
								 | 
							
								    // (can including its descendants or not) and the size of its parent.
							 | 
						||
| 
								 | 
							
								    // (Bottom-up travel)
							 | 
						||
| 
								 | 
							
								    // When `chart.clear()` or `chart.setOption({...}, true)` with the same id,
							 | 
						||
| 
								 | 
							
								    // view will be reused.
							 | 
						||
| 
								 | 
							
								    if (graphicModel !== this._lastGraphicModel) {
							 | 
						||
| 
								 | 
							
								      this._clear();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._lastGraphicModel = graphicModel;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._updateElements(graphicModel);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._relocate(graphicModel, api);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Update graphic elements.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @private
							 | 
						||
| 
								 | 
							
								   * @param {Object} graphicModel graphic model
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  _updateElements: function (graphicModel) {
							 | 
						||
| 
								 | 
							
								    var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!elOptionsToUpdate) {
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var elMap = this._elMap;
							 | 
						||
| 
								 | 
							
								    var rootGroup = this.group; // Top-down tranverse to assign graphic settings to each elements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    zrUtil.each(elOptionsToUpdate, function (elOption) {
							 | 
						||
| 
								 | 
							
								      var $action = elOption.$action;
							 | 
						||
| 
								 | 
							
								      var id = elOption.id;
							 | 
						||
| 
								 | 
							
								      var existEl = elMap.get(id);
							 | 
						||
| 
								 | 
							
								      var parentId = elOption.parentId;
							 | 
						||
| 
								 | 
							
								      var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup;
							 | 
						||
| 
								 | 
							
								      var elOptionStyle = elOption.style;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (elOption.type === 'text' && elOptionStyle) {
							 | 
						||
| 
								 | 
							
								        // In top/bottom mode, textVerticalAlign should not be used, which cause
							 | 
						||
| 
								 | 
							
								        // inaccurately locating.
							 | 
						||
| 
								 | 
							
								        if (elOption.hv && elOption.hv[1]) {
							 | 
						||
| 
								 | 
							
								          elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = null;
							 | 
						||
| 
								 | 
							
								        } // Compatible with previous setting: both support fill and textFill,
							 | 
						||
| 
								 | 
							
								        // stroke and textStroke.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && (elOptionStyle.textFill = elOptionStyle.fill);
							 | 
						||
| 
								 | 
							
								        !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && (elOptionStyle.textStroke = elOptionStyle.stroke);
							 | 
						||
| 
								 | 
							
								      } // Remove unnecessary props to avoid potential problems.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var elOptionCleaned = getCleanedElOption(elOption); // For simple, do not support parent change, otherwise reorder is needed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!$action || $action === 'merge') {
							 | 
						||
| 
								 | 
							
								        existEl ? existEl.attr(elOptionCleaned) : createEl(id, targetElParent, elOptionCleaned, elMap);
							 | 
						||
| 
								 | 
							
								      } else if ($action === 'replace') {
							 | 
						||
| 
								 | 
							
								        removeEl(existEl, elMap);
							 | 
						||
| 
								 | 
							
								        createEl(id, targetElParent, elOptionCleaned, elMap);
							 | 
						||
| 
								 | 
							
								      } else if ($action === 'remove') {
							 | 
						||
| 
								 | 
							
								        removeEl(existEl, elMap);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var el = elMap.get(id);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (el) {
							 | 
						||
| 
								 | 
							
								        el.__ecGraphicWidth = elOption.width;
							 | 
						||
| 
								 | 
							
								        el.__ecGraphicHeight = elOption.height;
							 | 
						||
| 
								 | 
							
								        setEventData(el, graphicModel, elOption);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Locate graphic elements.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @private
							 | 
						||
| 
								 | 
							
								   * @param {Object} graphicModel graphic model
							 | 
						||
| 
								 | 
							
								   * @param {module:echarts/ExtensionAPI} api extension API
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  _relocate: function (graphicModel, api) {
							 | 
						||
| 
								 | 
							
								    var elOptions = graphicModel.option.elements;
							 | 
						||
| 
								 | 
							
								    var rootGroup = this.group;
							 | 
						||
| 
								 | 
							
								    var elMap = this._elMap; // Bottom-up tranvese all elements (consider ec resize) to locate elements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (var i = elOptions.length - 1; i >= 0; i--) {
							 | 
						||
| 
								 | 
							
								      var elOption = elOptions[i];
							 | 
						||
| 
								 | 
							
								      var el = elMap.get(elOption.id);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!el) {
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var parentEl = el.parent;
							 | 
						||
| 
								 | 
							
								      var containerInfo = parentEl === rootGroup ? {
							 | 
						||
| 
								 | 
							
								        width: api.getWidth(),
							 | 
						||
| 
								 | 
							
								        height: api.getHeight()
							 | 
						||
| 
								 | 
							
								      } : {
							 | 
						||
| 
								 | 
							
								        // Like 'position:absolut' in css, default 0.
							 | 
						||
| 
								 | 
							
								        width: parentEl.__ecGraphicWidth || 0,
							 | 
						||
| 
								 | 
							
								        height: parentEl.__ecGraphicHeight || 0
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								      layoutUtil.positionElement(el, elOption, containerInfo, null, {
							 | 
						||
| 
								 | 
							
								        hv: elOption.hv,
							 | 
						||
| 
								 | 
							
								        boundingMode: elOption.bounding
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Clear all elements.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @private
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  _clear: function () {
							 | 
						||
| 
								 | 
							
								    var elMap = this._elMap;
							 | 
						||
| 
								 | 
							
								    elMap.each(function (el) {
							 | 
						||
| 
								 | 
							
								      removeEl(el, elMap);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    this._elMap = zrUtil.createHashMap();
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @override
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  dispose: function () {
							 | 
						||
| 
								 | 
							
								    this._clear();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function createEl(id, targetElParent, elOption, elMap) {
							 | 
						||
| 
								 | 
							
								  var graphicType = elOption.type;
							 | 
						||
| 
								 | 
							
								  var Clz = graphicUtil[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)];
							 | 
						||
| 
								 | 
							
								  var el = new Clz(elOption);
							 | 
						||
| 
								 | 
							
								  targetElParent.add(el);
							 | 
						||
| 
								 | 
							
								  elMap.set(id, el);
							 | 
						||
| 
								 | 
							
								  el.__ecGraphicId = id;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function removeEl(existEl, elMap) {
							 | 
						||
| 
								 | 
							
								  var existElParent = existEl && existEl.parent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (existElParent) {
							 | 
						||
| 
								 | 
							
								    existEl.type === 'group' && existEl.traverse(function (el) {
							 | 
						||
| 
								 | 
							
								      removeEl(el, elMap);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    elMap.removeKey(existEl.__ecGraphicId);
							 | 
						||
| 
								 | 
							
								    existElParent.remove(existEl);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								} // Remove unnecessary props to avoid potential problems.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getCleanedElOption(elOption) {
							 | 
						||
| 
								 | 
							
								  elOption = zrUtil.extend({}, elOption);
							 | 
						||
| 
								 | 
							
								  zrUtil.each(['id', 'parentId', '$action', 'hv', 'bounding'].concat(layoutUtil.LOCATION_PARAMS), function (name) {
							 | 
						||
| 
								 | 
							
								    delete elOption[name];
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  return elOption;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isSetLoc(obj, props) {
							 | 
						||
| 
								 | 
							
								  var isSet;
							 | 
						||
| 
								 | 
							
								  zrUtil.each(props, function (prop) {
							 | 
						||
| 
								 | 
							
								    obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  return isSet;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function setKeyInfoToNewElOption(resultItem, newElOption) {
							 | 
						||
| 
								 | 
							
								  var existElOption = resultItem.exist; // Set id and type after id assigned.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  newElOption.id = resultItem.keyInfo.id;
							 | 
						||
| 
								 | 
							
								  !newElOption.type && existElOption && (newElOption.type = existElOption.type); // Set parent id if not specified
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (newElOption.parentId == null) {
							 | 
						||
| 
								 | 
							
								    var newElParentOption = newElOption.parentOption;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (newElParentOption) {
							 | 
						||
| 
								 | 
							
								      newElOption.parentId = newElParentOption.id;
							 | 
						||
| 
								 | 
							
								    } else if (existElOption) {
							 | 
						||
| 
								 | 
							
								      newElOption.parentId = existElOption.parentId;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } // Clear
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  newElOption.parentOption = null;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function mergeNewElOptionToExist(existList, index, newElOption) {
							 | 
						||
| 
								 | 
							
								  // Update existing options, for `getOption` feature.
							 | 
						||
| 
								 | 
							
								  var newElOptCopy = zrUtil.extend({}, newElOption);
							 | 
						||
| 
								 | 
							
								  var existElOption = existList[index];
							 | 
						||
| 
								 | 
							
								  var $action = newElOption.$action || 'merge';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ($action === 'merge') {
							 | 
						||
| 
								 | 
							
								    if (existElOption) {
							 | 
						||
| 
								 | 
							
								      // We can ensure that newElOptCopy and existElOption are not
							 | 
						||
| 
								 | 
							
								      // the same object, so `merge` will not change newElOptCopy.
							 | 
						||
| 
								 | 
							
								      zrUtil.merge(existElOption, newElOptCopy, true); // Rigid body, use ignoreSize.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      layoutUtil.mergeLayoutParam(existElOption, newElOptCopy, {
							 | 
						||
| 
								 | 
							
								        ignoreSize: true
							 | 
						||
| 
								 | 
							
								      }); // Will be used in render.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      layoutUtil.copyLayoutParams(newElOption, existElOption);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      existList[index] = newElOptCopy;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if ($action === 'replace') {
							 | 
						||
| 
								 | 
							
								    existList[index] = newElOptCopy;
							 | 
						||
| 
								 | 
							
								  } else if ($action === 'remove') {
							 | 
						||
| 
								 | 
							
								    // null will be cleaned later.
							 | 
						||
| 
								 | 
							
								    existElOption && (existList[index] = null);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function setLayoutInfoToExist(existItem, newElOption) {
							 | 
						||
| 
								 | 
							
								  if (!existItem) {
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  existItem.hv = newElOption.hv = [// Rigid body, dont care `width`.
							 | 
						||
| 
								 | 
							
								  isSetLoc(newElOption, ['left', 'right']), // Rigid body, dont care `height`.
							 | 
						||
| 
								 | 
							
								  isSetLoc(newElOption, ['top', 'bottom'])]; // Give default group size. Otherwise layout error may occur.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (existItem.type === 'group') {
							 | 
						||
| 
								 | 
							
								    existItem.width == null && (existItem.width = newElOption.width = 0);
							 | 
						||
| 
								 | 
							
								    existItem.height == null && (existItem.height = newElOption.height = 0);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function setEventData(el, graphicModel, elOption) {
							 | 
						||
| 
								 | 
							
								  var eventData = el.eventData; // Simple optimize for large amount of elements that no need event.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!el.silent && !el.ignore && !eventData) {
							 | 
						||
| 
								 | 
							
								    eventData = el.eventData = {
							 | 
						||
| 
								 | 
							
								      componentType: 'graphic',
							 | 
						||
| 
								 | 
							
								      componentIndex: graphicModel.componentIndex,
							 | 
						||
| 
								 | 
							
								      name: el.name
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  } // `elOption.info` enables user to mount some info on
							 | 
						||
| 
								 | 
							
								  // elements and use them in event handlers.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (eventData) {
							 | 
						||
| 
								 | 
							
								    eventData.info = el.info;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |