| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 | 
/** 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 zrUtil = require("zrender/lib/core/util");var zrColor = require("zrender/lib/tool/color");var _number = require("../util/number");var linearMap = _number.linearMap;/** 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 each = zrUtil.each;var isObject = zrUtil.isObject;var CATEGORY_DEFAULT_VISUAL_INDEX = -1;/** * @param {Object} option * @param {string} [option.type] See visualHandlers. * @param {string} [option.mappingMethod] 'linear' or 'piecewise' or 'category' or 'fixed' * @param {Array.<number>=} [option.dataExtent] [minExtent, maxExtent], *                                              required when mappingMethod is 'linear' * @param {Array.<Object>=} [option.pieceList] [ *                                             {value: someValue}, *                                             {interval: [min1, max1], visual: {...}}, *                                             {interval: [min2, max2]} *                                             ], *                                            required when mappingMethod is 'piecewise'. *                                            Visual for only each piece can be specified. * @param {Array.<string|Object>=} [option.categories] ['cate1', 'cate2'] *                                            required when mappingMethod is 'category'. *                                            If no option.categories, categories is set *                                            as [0, 1, 2, ...]. * @param {boolean} [option.loop=false] Whether loop mapping when mappingMethod is 'category'. * @param {(Array|Object|*)} [option.visual]  Visual data. *                                            when mappingMethod is 'category', *                                            visual data can be array or object *                                            (like: {cate1: '#222', none: '#fff'}) *                                            or primary types (which represents *                                            default category visual), otherwise visual *                                            can be array or primary (which will be *                                            normalized to array). * */var VisualMapping = function (option) {  var mappingMethod = option.mappingMethod;  var visualType = option.type;  /**   * @readOnly   * @type {Object}   */  var thisOption = this.option = zrUtil.clone(option);  /**   * @readOnly   * @type {string}   */  this.type = visualType;  /**   * @readOnly   * @type {string}   */  this.mappingMethod = mappingMethod;  /**   * @private   * @type {Function}   */  this._normalizeData = normalizers[mappingMethod];  var visualHandler = visualHandlers[visualType];  /**   * @public   * @type {Function}   */  this.applyVisual = visualHandler.applyVisual;  /**   * @public   * @type {Function}   */  this.getColorMapper = visualHandler.getColorMapper;  /**   * @private   * @type {Function}   */  this._doMap = visualHandler._doMap[mappingMethod];  if (mappingMethod === 'piecewise') {    normalizeVisualRange(thisOption);    preprocessForPiecewise(thisOption);  } else if (mappingMethod === 'category') {    thisOption.categories ? preprocessForSpecifiedCategory(thisOption) // categories is ordinal when thisOption.categories not specified,    // which need no more preprocess except normalize visual.    : normalizeVisualRange(thisOption, true);  } else {    // mappingMethod === 'linear' or 'fixed'    zrUtil.assert(mappingMethod !== 'linear' || thisOption.dataExtent);    normalizeVisualRange(thisOption);  }};VisualMapping.prototype = {  constructor: VisualMapping,  mapValueToVisual: function (value) {    var normalized = this._normalizeData(value);    return this._doMap(normalized, value);  },  getNormalizer: function () {    return zrUtil.bind(this._normalizeData, this);  }};var visualHandlers = VisualMapping.visualHandlers = {  color: {    applyVisual: makeApplyVisual('color'),    /**     * Create a mapper function     * @return {Function}     */    getColorMapper: function () {      var thisOption = this.option;      return zrUtil.bind(thisOption.mappingMethod === 'category' ? function (value, isNormalized) {        !isNormalized && (value = this._normalizeData(value));        return doMapCategory.call(this, value);      } : function (value, isNormalized, out) {        // If output rgb array        // which will be much faster and useful in pixel manipulation        var returnRGBArray = !!out;        !isNormalized && (value = this._normalizeData(value));        out = zrColor.fastLerp(value, thisOption.parsedVisual, out);        return returnRGBArray ? out : zrColor.stringify(out, 'rgba');      }, this);    },    _doMap: {      linear: function (normalized) {        return zrColor.stringify(zrColor.fastLerp(normalized, this.option.parsedVisual), 'rgba');      },      category: doMapCategory,      piecewise: function (normalized, value) {        var result = getSpecifiedVisual.call(this, value);        if (result == null) {          result = zrColor.stringify(zrColor.fastLerp(normalized, this.option.parsedVisual), 'rgba');        }        return result;      },      fixed: doMapFixed    }  },  colorHue: makePartialColorVisualHandler(function (color, value) {    return zrColor.modifyHSL(color, value);  }),  colorSaturation: makePartialColorVisualHandler(function (color, value) {    return zrColor.modifyHSL(color, null, value);  }),  colorLightness: makePartialColorVisualHandler(function (color, value) {    return zrColor.modifyHSL(color, null, null, value);  }),  colorAlpha: makePartialColorVisualHandler(function (color, value) {    return zrColor.modifyAlpha(color, value);  }),  opacity: {    applyVisual: makeApplyVisual('opacity'),    _doMap: makeDoMap([0, 1])  },  liftZ: {    applyVisual: makeApplyVisual('liftZ'),    _doMap: {      linear: doMapFixed,      category: doMapFixed,      piecewise: doMapFixed,      fixed: doMapFixed    }  },  symbol: {    applyVisual: function (value, getter, setter) {      var symbolCfg = this.mapValueToVisual(value);      if (zrUtil.isString(symbolCfg)) {        setter('symbol', symbolCfg);      } else if (isObject(symbolCfg)) {        for (var name in symbolCfg) {          if (symbolCfg.hasOwnProperty(name)) {            setter(name, symbolCfg[name]);          }        }      }    },    _doMap: {      linear: doMapToArray,      category: doMapCategory,      piecewise: function (normalized, value) {        var result = getSpecifiedVisual.call(this, value);        if (result == null) {          result = doMapToArray.call(this, normalized);        }        return result;      },      fixed: doMapFixed    }  },  symbolSize: {    applyVisual: makeApplyVisual('symbolSize'),    _doMap: makeDoMap([0, 1])  }};function preprocessForPiecewise(thisOption) {  var pieceList = thisOption.pieceList;  thisOption.hasSpecialVisual = false;  zrUtil.each(pieceList, function (piece, index) {    piece.originIndex = index; // piece.visual is "result visual value" but not    // a visual range, so it does not need to be normalized.    if (piece.visual != null) {      thisOption.hasSpecialVisual = true;    }  });}function preprocessForSpecifiedCategory(thisOption) {  // Hash categories.  var categories = thisOption.categories;  var visual = thisOption.visual;  var categoryMap = thisOption.categoryMap = {};  each(categories, function (cate, index) {    categoryMap[cate] = index;  }); // Process visual map input.  if (!zrUtil.isArray(visual)) {    var visualArr = [];    if (zrUtil.isObject(visual)) {      each(visual, function (v, cate) {        var index = categoryMap[cate];        visualArr[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v;      });    } else {      // Is primary type, represents default visual.      visualArr[CATEGORY_DEFAULT_VISUAL_INDEX] = visual;    }    visual = setVisualToOption(thisOption, visualArr);  } // Remove categories that has no visual,  // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX.  for (var i = categories.length - 1; i >= 0; i--) {    if (visual[i] == null) {      delete categoryMap[categories[i]];      categories.pop();    }  }}function normalizeVisualRange(thisOption, isCategory) {  var visual = thisOption.visual;  var visualArr = [];  if (zrUtil.isObject(visual)) {    each(visual, function (v) {      visualArr.push(v);    });  } else if (visual != null) {    visualArr.push(visual);  }  var doNotNeedPair = {    color: 1,    symbol: 1  };  if (!isCategory && visualArr.length === 1 && !doNotNeedPair.hasOwnProperty(thisOption.type)) {    // Do not care visualArr.length === 0, which is illegal.    visualArr[1] = visualArr[0];  }  setVisualToOption(thisOption, visualArr);}function makePartialColorVisualHandler(applyValue) {  return {    applyVisual: function (value, getter, setter) {      value = this.mapValueToVisual(value); // Must not be array value      setter('color', applyValue(getter('color'), value));    },    _doMap: makeDoMap([0, 1])  };}function doMapToArray(normalized) {  var visual = this.option.visual;  return visual[Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true))] || {};}function makeApplyVisual(visualType) {  return function (value, getter, setter) {    setter(visualType, this.mapValueToVisual(value));  };}function doMapCategory(normalized) {  var visual = this.option.visual;  return visual[this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX ? normalized % visual.length : normalized];}function doMapFixed() {  return this.option.visual[0];}function makeDoMap(sourceExtent) {  return {    linear: function (normalized) {      return linearMap(normalized, sourceExtent, this.option.visual, true);    },    category: doMapCategory,    piecewise: function (normalized, value) {      var result = getSpecifiedVisual.call(this, value);      if (result == null) {        result = linearMap(normalized, sourceExtent, this.option.visual, true);      }      return result;    },    fixed: doMapFixed  };}function getSpecifiedVisual(value) {  var thisOption = this.option;  var pieceList = thisOption.pieceList;  if (thisOption.hasSpecialVisual) {    var pieceIndex = VisualMapping.findPieceIndex(value, pieceList);    var piece = pieceList[pieceIndex];    if (piece && piece.visual) {      return piece.visual[this.type];    }  }}function setVisualToOption(thisOption, visualArr) {  thisOption.visual = visualArr;  if (thisOption.type === 'color') {    thisOption.parsedVisual = zrUtil.map(visualArr, function (item) {      return zrColor.parse(item);    });  }  return visualArr;}/** * Normalizers by mapping methods. */var normalizers = {  linear: function (value) {    return linearMap(value, this.option.dataExtent, [0, 1], true);  },  piecewise: function (value) {    var pieceList = this.option.pieceList;    var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true);    if (pieceIndex != null) {      return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true);    }  },  category: function (value) {    var index = this.option.categories ? this.option.categoryMap[value] : value; // ordinal    return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index;  },  fixed: zrUtil.noop};/** * List available visual types. * * @public * @return {Array.<string>} */VisualMapping.listVisualTypes = function () {  var visualTypes = [];  zrUtil.each(visualHandlers, function (handler, key) {    visualTypes.push(key);  });  return visualTypes;};/** * @public */VisualMapping.addVisualHandler = function (name, handler) {  visualHandlers[name] = handler;};/** * @public */VisualMapping.isValidType = function (visualType) {  return visualHandlers.hasOwnProperty(visualType);};/** * Convinent method. * Visual can be Object or Array or primary type. * * @public */VisualMapping.eachVisual = function (visual, callback, context) {  if (zrUtil.isObject(visual)) {    zrUtil.each(visual, callback, context);  } else {    callback.call(context, visual);  }};VisualMapping.mapVisual = function (visual, callback, context) {  var isPrimary;  var newVisual = zrUtil.isArray(visual) ? [] : zrUtil.isObject(visual) ? {} : (isPrimary = true, null);  VisualMapping.eachVisual(visual, function (v, key) {    var newVal = callback.call(context, v, key);    isPrimary ? newVisual = newVal : newVisual[key] = newVal;  });  return newVisual;};/** * @public * @param {Object} obj * @return {Object} new object containers visual values. *                 If no visuals, return null. */VisualMapping.retrieveVisuals = function (obj) {  var ret = {};  var hasVisual;  obj && each(visualHandlers, function (h, visualType) {    if (obj.hasOwnProperty(visualType)) {      ret[visualType] = obj[visualType];      hasVisual = true;    }  });  return hasVisual ? ret : null;};/** * Give order to visual types, considering colorSaturation, colorAlpha depends on color. * * @public * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...} *                                     IF Array, like: ['color', 'symbol', 'colorSaturation'] * @return {Array.<string>} Sorted visual types. */VisualMapping.prepareVisualTypes = function (visualTypes) {  if (isObject(visualTypes)) {    var types = [];    each(visualTypes, function (item, type) {      types.push(type);    });    visualTypes = types;  } else if (zrUtil.isArray(visualTypes)) {    visualTypes = visualTypes.slice();  } else {    return [];  }  visualTypes.sort(function (type1, type2) {    // color should be front of colorSaturation, colorAlpha, ...    // symbol and symbolSize do not matter.    return type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0 ? 1 : -1;  });  return visualTypes;};/** * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'. * Other visuals are only depends on themself. * * @public * @param {string} visualType1 * @param {string} visualType2 * @return {boolean} */VisualMapping.dependsOn = function (visualType1, visualType2) {  return visualType2 === 'color' ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) : visualType1 === visualType2;};/** * @param {number} value * @param {Array.<Object>} pieceList [{value: ..., interval: [min, max]}, ...] *                         Always from small to big. * @param {boolean} [findClosestWhenOutside=false] * @return {number} index */VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) {  var possibleI;  var abs = Infinity; // value has the higher priority.  for (var i = 0, len = pieceList.length; i < len; i++) {    var pieceValue = pieceList[i].value;    if (pieceValue != null) {      if (pieceValue === value // FIXME      // It is supposed to compare value according to value type of dimension,      // but currently value type can exactly be string or number.      // Compromise for numeric-like string (like '12'), especially      // in the case that visualMap.categories is ['22', '33'].      || typeof pieceValue === 'string' && pieceValue === value + '') {        return i;      }      findClosestWhenOutside && updatePossible(pieceValue, i);    }  }  for (var i = 0, len = pieceList.length; i < len; i++) {    var piece = pieceList[i];    var interval = piece.interval;    var close = piece.close;    if (interval) {      if (interval[0] === -Infinity) {        if (littleThan(close[1], value, interval[1])) {          return i;        }      } else if (interval[1] === Infinity) {        if (littleThan(close[0], interval[0], value)) {          return i;        }      } else if (littleThan(close[0], interval[0], value) && littleThan(close[1], value, interval[1])) {        return i;      }      findClosestWhenOutside && updatePossible(interval[0], i);      findClosestWhenOutside && updatePossible(interval[1], i);    }  }  if (findClosestWhenOutside) {    return value === Infinity ? pieceList.length - 1 : value === -Infinity ? 0 : possibleI;  }  function updatePossible(val, index) {    var newAbs = Math.abs(val - value);    if (newAbs < abs) {      abs = newAbs;      possibleI = index;    }  }};function littleThan(close, a, b) {  return close ? a <= b : a < b;}var _default = VisualMapping;module.exports = _default;
 |