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.
		
		
		
		
		
			
		
			
				
					
					
						
							381 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							381 lines
						
					
					
						
							11 KiB
						
					
					
				
 | 
						|
/* | 
						|
* 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 Path = require("zrender/lib/graphic/Path"); | 
						|
 | 
						|
var vec2 = require("zrender/lib/core/vector"); | 
						|
 | 
						|
var fixClipWithShadow = require("zrender/lib/graphic/helper/fixClipWithShadow"); | 
						|
 | 
						|
/* | 
						|
* 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. | 
						|
*/ | 
						|
// Poly path support NaN point | 
						|
var vec2Min = vec2.min; | 
						|
var vec2Max = vec2.max; | 
						|
var scaleAndAdd = vec2.scaleAndAdd; | 
						|
var v2Copy = vec2.copy; // Temporary variable | 
						|
 | 
						|
var v = []; | 
						|
var cp0 = []; | 
						|
var cp1 = []; | 
						|
 | 
						|
function isPointNull(p) { | 
						|
  return isNaN(p[0]) || isNaN(p[1]); | 
						|
} | 
						|
 | 
						|
function drawSegment(ctx, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) { | 
						|
  // if (smoothMonotone == null) { | 
						|
  //     if (isMono(points, 'x')) { | 
						|
  //         return drawMono(ctx, points, start, segLen, allLen, | 
						|
  //             dir, smoothMin, smoothMax, smooth, 'x', connectNulls); | 
						|
  //     } | 
						|
  //     else if (isMono(points, 'y')) { | 
						|
  //         return drawMono(ctx, points, start, segLen, allLen, | 
						|
  //             dir, smoothMin, smoothMax, smooth, 'y', connectNulls); | 
						|
  //     } | 
						|
  //     else { | 
						|
  //         return drawNonMono.apply(this, arguments); | 
						|
  //     } | 
						|
  // } | 
						|
  // else if (smoothMonotone !== 'none' && isMono(points, smoothMonotone)) { | 
						|
  //     return drawMono.apply(this, arguments); | 
						|
  // } | 
						|
  // else { | 
						|
  //     return drawNonMono.apply(this, arguments); | 
						|
  // } | 
						|
  if (smoothMonotone === 'none' || !smoothMonotone) { | 
						|
    return drawNonMono.apply(this, arguments); | 
						|
  } else { | 
						|
    return drawMono.apply(this, arguments); | 
						|
  } | 
						|
} | 
						|
/** | 
						|
 * Check if points is in monotone. | 
						|
 * | 
						|
 * @param {number[][]} points         Array of points which is in [x, y] form | 
						|
 * @param {string}     smoothMonotone 'x', 'y', or 'none', stating for which | 
						|
 *                                    dimension that is checking. | 
						|
 *                                    If is 'none', `drawNonMono` should be | 
						|
 *                                    called. | 
						|
 *                                    If is undefined, either being monotone | 
						|
 *                                    in 'x' or 'y' will call `drawMono`. | 
						|
 */ | 
						|
// function isMono(points, smoothMonotone) { | 
						|
//     if (points.length <= 1) { | 
						|
//         return true; | 
						|
//     } | 
						|
//     var dim = smoothMonotone === 'x' ? 0 : 1; | 
						|
//     var last = points[0][dim]; | 
						|
//     var lastDiff = 0; | 
						|
//     for (var i = 1; i < points.length; ++i) { | 
						|
//         var diff = points[i][dim] - last; | 
						|
//         if (!isNaN(diff) && !isNaN(lastDiff) | 
						|
//             && diff !== 0 && lastDiff !== 0 | 
						|
//             && ((diff >= 0) !== (lastDiff >= 0)) | 
						|
//         ) { | 
						|
//             return false; | 
						|
//         } | 
						|
//         if (!isNaN(diff) && diff !== 0) { | 
						|
//             lastDiff = diff; | 
						|
//             last = points[i][dim]; | 
						|
//         } | 
						|
//     } | 
						|
//     return true; | 
						|
// } | 
						|
 | 
						|
/** | 
						|
 * Draw smoothed line in monotone, in which only vertical or horizontal bezier | 
						|
 * control points will be used. This should be used when points are monotone | 
						|
 * either in x or y dimension. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function drawMono(ctx, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) { | 
						|
  var prevIdx = 0; | 
						|
  var idx = start; | 
						|
 | 
						|
  for (var k = 0; k < segLen; k++) { | 
						|
    var p = points[idx]; | 
						|
 | 
						|
    if (idx >= allLen || idx < 0) { | 
						|
      break; | 
						|
    } | 
						|
 | 
						|
    if (isPointNull(p)) { | 
						|
      if (connectNulls) { | 
						|
        idx += dir; | 
						|
        continue; | 
						|
      } | 
						|
 | 
						|
      break; | 
						|
    } | 
						|
 | 
						|
    if (idx === start) { | 
						|
      ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); | 
						|
    } else { | 
						|
      if (smooth > 0) { | 
						|
        var prevP = points[prevIdx]; | 
						|
        var dim = smoothMonotone === 'y' ? 1 : 0; // Length of control point to p, either in x or y, but not both | 
						|
 | 
						|
        var ctrlLen = (p[dim] - prevP[dim]) * smooth; | 
						|
        v2Copy(cp0, prevP); | 
						|
        cp0[dim] = prevP[dim] + ctrlLen; | 
						|
        v2Copy(cp1, p); | 
						|
        cp1[dim] = p[dim] - ctrlLen; | 
						|
        ctx.bezierCurveTo(cp0[0], cp0[1], cp1[0], cp1[1], p[0], p[1]); | 
						|
      } else { | 
						|
        ctx.lineTo(p[0], p[1]); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    prevIdx = idx; | 
						|
    idx += dir; | 
						|
  } | 
						|
 | 
						|
  return k; | 
						|
} | 
						|
/** | 
						|
 * Draw smoothed line in non-monotone, in may cause undesired curve in extreme | 
						|
 * situations. This should be used when points are non-monotone neither in x or | 
						|
 * y dimension. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function drawNonMono(ctx, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) { | 
						|
  var prevIdx = 0; | 
						|
  var idx = start; | 
						|
 | 
						|
  for (var k = 0; k < segLen; k++) { | 
						|
    var p = points[idx]; | 
						|
 | 
						|
    if (idx >= allLen || idx < 0) { | 
						|
      break; | 
						|
    } | 
						|
 | 
						|
    if (isPointNull(p)) { | 
						|
      if (connectNulls) { | 
						|
        idx += dir; | 
						|
        continue; | 
						|
      } | 
						|
 | 
						|
      break; | 
						|
    } | 
						|
 | 
						|
    if (idx === start) { | 
						|
      ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); | 
						|
      v2Copy(cp0, p); | 
						|
    } else { | 
						|
      if (smooth > 0) { | 
						|
        var nextIdx = idx + dir; | 
						|
        var nextP = points[nextIdx]; | 
						|
 | 
						|
        if (connectNulls) { | 
						|
          // Find next point not null | 
						|
          while (nextP && isPointNull(points[nextIdx])) { | 
						|
            nextIdx += dir; | 
						|
            nextP = points[nextIdx]; | 
						|
          } | 
						|
        } | 
						|
 | 
						|
        var ratioNextSeg = 0.5; | 
						|
        var prevP = points[prevIdx]; | 
						|
        var nextP = points[nextIdx]; // Last point | 
						|
 | 
						|
        if (!nextP || isPointNull(nextP)) { | 
						|
          v2Copy(cp1, p); | 
						|
        } else { | 
						|
          // If next data is null in not connect case | 
						|
          if (isPointNull(nextP) && !connectNulls) { | 
						|
            nextP = p; | 
						|
          } | 
						|
 | 
						|
          vec2.sub(v, nextP, prevP); | 
						|
          var lenPrevSeg; | 
						|
          var lenNextSeg; | 
						|
 | 
						|
          if (smoothMonotone === 'x' || smoothMonotone === 'y') { | 
						|
            var dim = smoothMonotone === 'x' ? 0 : 1; | 
						|
            lenPrevSeg = Math.abs(p[dim] - prevP[dim]); | 
						|
            lenNextSeg = Math.abs(p[dim] - nextP[dim]); | 
						|
          } else { | 
						|
            lenPrevSeg = vec2.dist(p, prevP); | 
						|
            lenNextSeg = vec2.dist(p, nextP); | 
						|
          } // Use ratio of seg length | 
						|
 | 
						|
 | 
						|
          ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); | 
						|
          scaleAndAdd(cp1, p, v, -smooth * (1 - ratioNextSeg)); | 
						|
        } // Smooth constraint | 
						|
 | 
						|
 | 
						|
        vec2Min(cp0, cp0, smoothMax); | 
						|
        vec2Max(cp0, cp0, smoothMin); | 
						|
        vec2Min(cp1, cp1, smoothMax); | 
						|
        vec2Max(cp1, cp1, smoothMin); | 
						|
        ctx.bezierCurveTo(cp0[0], cp0[1], cp1[0], cp1[1], p[0], p[1]); // cp0 of next segment | 
						|
 | 
						|
        scaleAndAdd(cp0, p, v, smooth * ratioNextSeg); | 
						|
      } else { | 
						|
        ctx.lineTo(p[0], p[1]); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    prevIdx = idx; | 
						|
    idx += dir; | 
						|
  } | 
						|
 | 
						|
  return k; | 
						|
} | 
						|
 | 
						|
function getBoundingBox(points, smoothConstraint) { | 
						|
  var ptMin = [Infinity, Infinity]; | 
						|
  var ptMax = [-Infinity, -Infinity]; | 
						|
 | 
						|
  if (smoothConstraint) { | 
						|
    for (var i = 0; i < points.length; i++) { | 
						|
      var pt = points[i]; | 
						|
 | 
						|
      if (pt[0] < ptMin[0]) { | 
						|
        ptMin[0] = pt[0]; | 
						|
      } | 
						|
 | 
						|
      if (pt[1] < ptMin[1]) { | 
						|
        ptMin[1] = pt[1]; | 
						|
      } | 
						|
 | 
						|
      if (pt[0] > ptMax[0]) { | 
						|
        ptMax[0] = pt[0]; | 
						|
      } | 
						|
 | 
						|
      if (pt[1] > ptMax[1]) { | 
						|
        ptMax[1] = pt[1]; | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return { | 
						|
    min: smoothConstraint ? ptMin : ptMax, | 
						|
    max: smoothConstraint ? ptMax : ptMin | 
						|
  }; | 
						|
} | 
						|
 | 
						|
var Polyline = Path.extend({ | 
						|
  type: 'ec-polyline', | 
						|
  shape: { | 
						|
    points: [], | 
						|
    smooth: 0, | 
						|
    smoothConstraint: true, | 
						|
    smoothMonotone: null, | 
						|
    connectNulls: false | 
						|
  }, | 
						|
  style: { | 
						|
    fill: null, | 
						|
    stroke: '#000' | 
						|
  }, | 
						|
  brush: fixClipWithShadow(Path.prototype.brush), | 
						|
  buildPath: function (ctx, shape) { | 
						|
    var points = shape.points; | 
						|
    var i = 0; | 
						|
    var len = points.length; | 
						|
    var result = getBoundingBox(points, shape.smoothConstraint); | 
						|
 | 
						|
    if (shape.connectNulls) { | 
						|
      // Must remove first and last null values avoid draw error in polygon | 
						|
      for (; len > 0; len--) { | 
						|
        if (!isPointNull(points[len - 1])) { | 
						|
          break; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      for (; i < len; i++) { | 
						|
        if (!isPointNull(points[i])) { | 
						|
          break; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    while (i < len) { | 
						|
      i += drawSegment(ctx, points, i, len, len, 1, result.min, result.max, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1; | 
						|
    } | 
						|
  } | 
						|
}); | 
						|
var Polygon = Path.extend({ | 
						|
  type: 'ec-polygon', | 
						|
  shape: { | 
						|
    points: [], | 
						|
    // Offset between stacked base points and points | 
						|
    stackedOnPoints: [], | 
						|
    smooth: 0, | 
						|
    stackedOnSmooth: 0, | 
						|
    smoothConstraint: true, | 
						|
    smoothMonotone: null, | 
						|
    connectNulls: false | 
						|
  }, | 
						|
  brush: fixClipWithShadow(Path.prototype.brush), | 
						|
  buildPath: function (ctx, shape) { | 
						|
    var points = shape.points; | 
						|
    var stackedOnPoints = shape.stackedOnPoints; | 
						|
    var i = 0; | 
						|
    var len = points.length; | 
						|
    var smoothMonotone = shape.smoothMonotone; | 
						|
    var bbox = getBoundingBox(points, shape.smoothConstraint); | 
						|
    var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint); | 
						|
 | 
						|
    if (shape.connectNulls) { | 
						|
      // Must remove first and last null values avoid draw error in polygon | 
						|
      for (; len > 0; len--) { | 
						|
        if (!isPointNull(points[len - 1])) { | 
						|
          break; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      for (; i < len; i++) { | 
						|
        if (!isPointNull(points[i])) { | 
						|
          break; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    while (i < len) { | 
						|
      var k = drawSegment(ctx, points, i, len, len, 1, bbox.min, bbox.max, shape.smooth, smoothMonotone, shape.connectNulls); | 
						|
      drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls); | 
						|
      i += k + 1; | 
						|
      ctx.closePath(); | 
						|
    } | 
						|
  } | 
						|
}); | 
						|
exports.Polyline = Polyline; | 
						|
exports.Polygon = Polygon; |