| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 | 
/** 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 zrUtil = require("zrender/lib/core/util");var VisualMapModel = require("./VisualMapModel");var VisualMapping = require("../../visual/VisualMapping");var visualDefault = require("../../visual/visualDefault");var _number = require("../../util/number");var reformIntervals = _number.reformIntervals;/** 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 PiecewiseModel = VisualMapModel.extend({  type: 'visualMap.piecewise',  /**   * Order Rule:   *   * option.categories / option.pieces / option.text / option.selected:   *     If !option.inverse,   *     Order when vertical: ['top', ..., 'bottom'].   *     Order when horizontal: ['left', ..., 'right'].   *     If option.inverse, the meaning of   *     the order should be reversed.   *   * this._pieceList:   *     The order is always [low, ..., high].   *   * Mapping from location to low-high:   *     If !option.inverse   *     When vertical, top is high.   *     When horizontal, right is high.   *     If option.inverse, reverse.   */  /**   * @protected   */  defaultOption: {    selected: null,    // Object. If not specified, means selected.    // When pieces and splitNumber: {'0': true, '5': true}    // When categories: {'cate1': false, 'cate3': true}    // When selected === false, means all unselected.    minOpen: false,    // Whether include values that smaller than `min`.    maxOpen: false,    // Whether include values that bigger than `max`.    align: 'auto',    // 'auto', 'left', 'right'    itemWidth: 20,    // When put the controller vertically, it is the length of    // horizontal side of each item. Otherwise, vertical side.    itemHeight: 14,    // When put the controller vertically, it is the length of    // vertical side of each item. Otherwise, horizontal side.    itemSymbol: 'roundRect',    pieceList: null,    // Each item is Object, with some of those attrs:    // {min, max, lt, gt, lte, gte, value,    // color, colorSaturation, colorAlpha, opacity,    // symbol, symbolSize}, which customize the range or visual    // coding of the certain piece. Besides, see "Order Rule".    categories: null,    // category names, like: ['some1', 'some2', 'some3'].    // Attr min/max are ignored when categories set. See "Order Rule"    splitNumber: 5,    // If set to 5, auto split five pieces equally.    // If set to 0 and component type not set, component type will be    // determined as "continuous". (It is less reasonable but for ec2    // compatibility, see echarts/component/visualMap/typeDefaulter)    selectedMode: 'multiple',    // Can be 'multiple' or 'single'.    itemGap: 10,    // The gap between two items, in px.    hoverLink: true,    // Enable hover highlight.    showLabel: null // By default, when text is used, label will hide (the logic    // is remained for compatibility reason)  },  /**   * @override   */  optionUpdated: function (newOption, isInit) {    PiecewiseModel.superApply(this, 'optionUpdated', arguments);    /**     * The order is always [low, ..., high].     * [{text: string, interval: Array.<number>}, ...]     * @private     * @type {Array.<Object>}     */    this._pieceList = [];    this.resetExtent();    /**     * 'pieces', 'categories', 'splitNumber'     * @type {string}     */    var mode = this._mode = this._determineMode();    resetMethods[this._mode].call(this);    this._resetSelected(newOption, isInit);    var categories = this.option.categories;    this.resetVisual(function (mappingOption, state) {      if (mode === 'categories') {        mappingOption.mappingMethod = 'category';        mappingOption.categories = zrUtil.clone(categories);      } else {        mappingOption.dataExtent = this.getExtent();        mappingOption.mappingMethod = 'piecewise';        mappingOption.pieceList = zrUtil.map(this._pieceList, function (piece) {          var piece = zrUtil.clone(piece);          if (state !== 'inRange') {            // FIXME            // outOfRange do not support special visual in pieces.            piece.visual = null;          }          return piece;        });      }    });  },  /**   * @protected   * @override   */  completeVisualOption: function () {    // Consider this case:    // visualMap: {    //      pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}]    // }    // where no inRange/outOfRange set but only pieces. So we should make    // default inRange/outOfRange for this case, otherwise visuals that only    // appear in `pieces` will not be taken into account in visual encoding.    var option = this.option;    var visualTypesInPieces = {};    var visualTypes = VisualMapping.listVisualTypes();    var isCategory = this.isCategory();    zrUtil.each(option.pieces, function (piece) {      zrUtil.each(visualTypes, function (visualType) {        if (piece.hasOwnProperty(visualType)) {          visualTypesInPieces[visualType] = 1;        }      });    });    zrUtil.each(visualTypesInPieces, function (v, visualType) {      var exists = 0;      zrUtil.each(this.stateList, function (state) {        exists |= has(option, state, visualType) || has(option.target, state, visualType);      }, this);      !exists && zrUtil.each(this.stateList, function (state) {        (option[state] || (option[state] = {}))[visualType] = visualDefault.get(visualType, state === 'inRange' ? 'active' : 'inactive', isCategory);      });    }, this);    function has(obj, state, visualType) {      return obj && obj[state] && (zrUtil.isObject(obj[state]) ? obj[state].hasOwnProperty(visualType) : obj[state] === visualType // e.g., inRange: 'symbol'      );    }    VisualMapModel.prototype.completeVisualOption.apply(this, arguments);  },  _resetSelected: function (newOption, isInit) {    var thisOption = this.option;    var pieceList = this._pieceList; // Selected do not merge but all override.    var selected = (isInit ? thisOption : newOption).selected || {};    thisOption.selected = selected; // Consider 'not specified' means true.    zrUtil.each(pieceList, function (piece, index) {      var key = this.getSelectedMapKey(piece);      if (!selected.hasOwnProperty(key)) {        selected[key] = true;      }    }, this);    if (thisOption.selectedMode === 'single') {      // Ensure there is only one selected.      var hasSel = false;      zrUtil.each(pieceList, function (piece, index) {        var key = this.getSelectedMapKey(piece);        if (selected[key]) {          hasSel ? selected[key] = false : hasSel = true;        }      }, this);    } // thisOption.selectedMode === 'multiple', default: all selected.  },  /**   * @public   */  getSelectedMapKey: function (piece) {    return this._mode === 'categories' ? piece.value + '' : piece.index + '';  },  /**   * @public   */  getPieceList: function () {    return this._pieceList;  },  /**   * @private   * @return {string}   */  _determineMode: function () {    var option = this.option;    return option.pieces && option.pieces.length > 0 ? 'pieces' : this.option.categories ? 'categories' : 'splitNumber';  },  /**   * @public   * @override   */  setSelected: function (selected) {    this.option.selected = zrUtil.clone(selected);  },  /**   * @public   * @override   */  getValueState: function (value) {    var index = VisualMapping.findPieceIndex(value, this._pieceList);    return index != null ? this.option.selected[this.getSelectedMapKey(this._pieceList[index])] ? 'inRange' : 'outOfRange' : 'outOfRange';  },  /**   * @public   * @params {number} pieceIndex piece index in visualMapModel.getPieceList()   * @return {Array.<Object>} [{seriesId, dataIndex: <Array.<number>>}, ...]   */  findTargetDataIndices: function (pieceIndex) {    var result = [];    this.eachTargetSeries(function (seriesModel) {      var dataIndices = [];      var data = seriesModel.getData();      data.each(this.getDataDimension(data), function (value, dataIndex) {        // Should always base on model pieceList, because it is order sensitive.        var pIdx = VisualMapping.findPieceIndex(value, this._pieceList);        pIdx === pieceIndex && dataIndices.push(dataIndex);      }, this);      result.push({        seriesId: seriesModel.id,        dataIndex: dataIndices      });    }, this);    return result;  },  /**   * @private   * @param {Object} piece piece.value or piece.interval is required.   * @return {number} Can be Infinity or -Infinity   */  getRepresentValue: function (piece) {    var representValue;    if (this.isCategory()) {      representValue = piece.value;    } else {      if (piece.value != null) {        representValue = piece.value;      } else {        var pieceInterval = piece.interval || [];        representValue = pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity ? 0 : (pieceInterval[0] + pieceInterval[1]) / 2;      }    }    return representValue;  },  getVisualMeta: function (getColorVisual) {    // Do not support category. (category axis is ordinal, numerical)    if (this.isCategory()) {      return;    }    var stops = [];    var outerColors = [];    var visualMapModel = this;    function setStop(interval, valueState) {      var representValue = visualMapModel.getRepresentValue({        interval: interval      });      if (!valueState) {        valueState = visualMapModel.getValueState(representValue);      }      var color = getColorVisual(representValue, valueState);      if (interval[0] === -Infinity) {        outerColors[0] = color;      } else if (interval[1] === Infinity) {        outerColors[1] = color;      } else {        stops.push({          value: interval[0],          color: color        }, {          value: interval[1],          color: color        });      }    } // Suplement    var pieceList = this._pieceList.slice();    if (!pieceList.length) {      pieceList.push({        interval: [-Infinity, Infinity]      });    } else {      var edge = pieceList[0].interval[0];      edge !== -Infinity && pieceList.unshift({        interval: [-Infinity, edge]      });      edge = pieceList[pieceList.length - 1].interval[1];      edge !== Infinity && pieceList.push({        interval: [edge, Infinity]      });    }    var curr = -Infinity;    zrUtil.each(pieceList, function (piece) {      var interval = piece.interval;      if (interval) {        // Fulfill gap.        interval[0] > curr && setStop([curr, interval[0]], 'outOfRange');        setStop(interval.slice());        curr = interval[1];      }    }, this);    return {      stops: stops,      outerColors: outerColors    };  }});/** * Key is this._mode * @type {Object} * @this {module:echarts/component/viusalMap/PiecewiseMode} */var resetMethods = {  splitNumber: function () {    var thisOption = this.option;    var pieceList = this._pieceList;    var precision = Math.min(thisOption.precision, 20);    var dataExtent = this.getExtent();    var splitNumber = thisOption.splitNumber;    splitNumber = Math.max(parseInt(splitNumber, 10), 1);    thisOption.splitNumber = splitNumber;    var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; // Precision auto-adaption    while (+splitStep.toFixed(precision) !== splitStep && precision < 5) {      precision++;    }    thisOption.precision = precision;    splitStep = +splitStep.toFixed(precision);    if (thisOption.minOpen) {      pieceList.push({        interval: [-Infinity, dataExtent[0]],        close: [0, 0]      });    }    for (var index = 0, curr = dataExtent[0]; index < splitNumber; curr += splitStep, index++) {      var max = index === splitNumber - 1 ? dataExtent[1] : curr + splitStep;      pieceList.push({        interval: [curr, max],        close: [1, 1]      });    }    if (thisOption.maxOpen) {      pieceList.push({        interval: [dataExtent[1], Infinity],        close: [0, 0]      });    }    reformIntervals(pieceList);    zrUtil.each(pieceList, function (piece, index) {      piece.index = index;      piece.text = this.formatValueText(piece.interval);    }, this);  },  categories: function () {    var thisOption = this.option;    zrUtil.each(thisOption.categories, function (cate) {      // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。      // 是否改一致。      this._pieceList.push({        text: this.formatValueText(cate, true),        value: cate      });    }, this); // See "Order Rule".    normalizeReverse(thisOption, this._pieceList);  },  pieces: function () {    var thisOption = this.option;    var pieceList = this._pieceList;    zrUtil.each(thisOption.pieces, function (pieceListItem, index) {      if (!zrUtil.isObject(pieceListItem)) {        pieceListItem = {          value: pieceListItem        };      }      var item = {        text: '',        index: index      };      if (pieceListItem.label != null) {        item.text = pieceListItem.label;      }      if (pieceListItem.hasOwnProperty('value')) {        var value = item.value = pieceListItem.value;        item.interval = [value, value];        item.close = [1, 1];      } else {        // `min` `max` is legacy option.        // `lt` `gt` `lte` `gte` is recommanded.        var interval = item.interval = [];        var close = item.close = [0, 0];        var closeList = [1, 0, 1];        var infinityList = [-Infinity, Infinity];        var useMinMax = [];        for (var lg = 0; lg < 2; lg++) {          var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg];          for (var i = 0; i < 3 && interval[lg] == null; i++) {            interval[lg] = pieceListItem[names[i]];            close[lg] = closeList[i];            useMinMax[lg] = i === 2;          }          interval[lg] == null && (interval[lg] = infinityList[lg]);        }        useMinMax[0] && interval[1] === Infinity && (close[0] = 0);        useMinMax[1] && interval[0] === -Infinity && (close[1] = 0);        if (interval[0] === interval[1] && close[0] && close[1]) {          // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}],          // we use value to lift the priority when min === max          item.value = interval[0];        }      }      item.visual = VisualMapping.retrieveVisuals(pieceListItem);      pieceList.push(item);    }, this); // See "Order Rule".    normalizeReverse(thisOption, pieceList); // Only pieces    reformIntervals(pieceList);    zrUtil.each(pieceList, function (piece) {      var close = piece.close;      var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]];      piece.text = piece.text || this.formatValueText(piece.value != null ? piece.value : piece.interval, false, edgeSymbols);    }, this);  }};function normalizeReverse(thisOption, pieceList) {  var inverse = thisOption.inverse;  if (thisOption.orient === 'vertical' ? !inverse : inverse) {    pieceList.reverse();  }}var _default = PiecewiseModel;module.exports = _default;
 |