SplineSeries.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /* *
  2. * (c) 2010-2019 Torstein Honsi
  3. *
  4. * License: www.highcharts.com/license
  5. */
  6. 'use strict';
  7. import H from './Globals.js';
  8. import './Utilities.js';
  9. import './Options.js';
  10. import './Series.js';
  11. var pick = H.pick,
  12. seriesType = H.seriesType;
  13. /**
  14. * Spline series type.
  15. *
  16. * @private
  17. * @class
  18. * @name Highcharts.seriesTypes.spline
  19. *
  20. * @augments Highcarts.Series
  21. */
  22. seriesType(
  23. 'spline',
  24. 'line',
  25. /**
  26. * A spline series is a special type of line series, where the segments
  27. * between the data points are smoothed.
  28. *
  29. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  30. * Spline chart
  31. * @sample {highstock} stock/demo/spline/
  32. * Spline chart
  33. *
  34. * @extends plotOptions.series
  35. * @excluding step
  36. * @product highcharts highstock
  37. * @optionparent plotOptions.spline
  38. */
  39. {
  40. },
  41. /** @lends seriesTypes.spline.prototype */ {
  42. /**
  43. * Get the spline segment from a given point's previous neighbour to the
  44. * given point.
  45. *
  46. * @private
  47. * @function Highcharts.seriesTypes.spline#getPointSpline
  48. *
  49. * @param {Array<Highcharts.Point>}
  50. *
  51. * @param {Highcharts.Point} point
  52. *
  53. * @param {number} i
  54. *
  55. * @return {Highcharts.SVGPathArray}
  56. */
  57. getPointSpline: function (points, point, i) {
  58. var
  59. // 1 means control points midway between points, 2 means 1/3
  60. // from the point, 3 is 1/4 etc
  61. smoothing = 1.5,
  62. denom = smoothing + 1,
  63. plotX = point.plotX,
  64. plotY = point.plotY,
  65. lastPoint = points[i - 1],
  66. nextPoint = points[i + 1],
  67. leftContX,
  68. leftContY,
  69. rightContX,
  70. rightContY,
  71. ret;
  72. function doCurve(otherPoint) {
  73. return otherPoint &&
  74. !otherPoint.isNull &&
  75. otherPoint.doCurve !== false &&
  76. !point.isCliff; // #6387, area splines next to null
  77. }
  78. // Find control points
  79. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  80. var lastX = lastPoint.plotX,
  81. lastY = lastPoint.plotY,
  82. nextX = nextPoint.plotX,
  83. nextY = nextPoint.plotY,
  84. correction = 0;
  85. leftContX = (smoothing * plotX + lastX) / denom;
  86. leftContY = (smoothing * plotY + lastY) / denom;
  87. rightContX = (smoothing * plotX + nextX) / denom;
  88. rightContY = (smoothing * plotY + nextY) / denom;
  89. // Have the two control points make a straight line through main
  90. // point
  91. if (rightContX !== leftContX) { // #5016, division by zero
  92. correction = (
  93. ((rightContY - leftContY) * (rightContX - plotX)) /
  94. (rightContX - leftContX) + plotY - rightContY
  95. );
  96. }
  97. leftContY += correction;
  98. rightContY += correction;
  99. // to prevent false extremes, check that control points are
  100. // between neighbouring points' y values
  101. if (leftContY > lastY && leftContY > plotY) {
  102. leftContY = Math.max(lastY, plotY);
  103. // mirror of left control point
  104. rightContY = 2 * plotY - leftContY;
  105. } else if (leftContY < lastY && leftContY < plotY) {
  106. leftContY = Math.min(lastY, plotY);
  107. rightContY = 2 * plotY - leftContY;
  108. }
  109. if (rightContY > nextY && rightContY > plotY) {
  110. rightContY = Math.max(nextY, plotY);
  111. leftContY = 2 * plotY - rightContY;
  112. } else if (rightContY < nextY && rightContY < plotY) {
  113. rightContY = Math.min(nextY, plotY);
  114. leftContY = 2 * plotY - rightContY;
  115. }
  116. // record for drawing in next point
  117. point.rightContX = rightContX;
  118. point.rightContY = rightContY;
  119. }
  120. // Visualize control points for debugging
  121. /*
  122. if (leftContX) {
  123. this.chart.renderer.circle(
  124. leftContX + this.chart.plotLeft,
  125. leftContY + this.chart.plotTop,
  126. 2
  127. )
  128. .attr({
  129. stroke: 'red',
  130. 'stroke-width': 2,
  131. fill: 'none',
  132. zIndex: 9
  133. })
  134. .add();
  135. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  136. leftContY + this.chart.plotTop,
  137. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  138. .attr({
  139. stroke: 'red',
  140. 'stroke-width': 2,
  141. zIndex: 9
  142. })
  143. .add();
  144. }
  145. if (rightContX) {
  146. this.chart.renderer.circle(
  147. rightContX + this.chart.plotLeft,
  148. rightContY + this.chart.plotTop,
  149. 2
  150. )
  151. .attr({
  152. stroke: 'green',
  153. 'stroke-width': 2,
  154. fill: 'none',
  155. zIndex: 9
  156. })
  157. .add();
  158. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  159. rightContY + this.chart.plotTop,
  160. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  161. .attr({
  162. stroke: 'green',
  163. 'stroke-width': 2,
  164. zIndex: 9
  165. })
  166. .add();
  167. }
  168. // */
  169. ret = [
  170. 'C',
  171. pick(lastPoint.rightContX, lastPoint.plotX),
  172. pick(lastPoint.rightContY, lastPoint.plotY),
  173. pick(leftContX, plotX),
  174. pick(leftContY, plotY),
  175. plotX,
  176. plotY
  177. ];
  178. // reset for updating series later
  179. lastPoint.rightContX = lastPoint.rightContY = null;
  180. return ret;
  181. }
  182. }
  183. );
  184. /**
  185. * A `spline` series. If the [type](#series.spline.type) option is
  186. * not specified, it is inherited from [chart.type](#chart.type).
  187. *
  188. * @extends series,plotOptions.spline
  189. * @excluding dataParser, dataURL, step
  190. * @product highcharts highstock
  191. * @apioption series.spline
  192. */
  193. /**
  194. * An array of data points for the series. For the `spline` series type,
  195. * points can be given in the following ways:
  196. *
  197. * 1. An array of numerical values. In this case, the numerical values will be
  198. * interpreted as `y` options. The `x` values will be automatically
  199. * calculated, either starting at 0 and incremented by 1, or from
  200. * `pointStart` and `pointInterval` given in the series options. If the axis
  201. * has categories, these will be used. Example:
  202. * ```js
  203. * data: [0, 5, 3, 5]
  204. * ```
  205. *
  206. * 2. An array of arrays with 2 values. In this case, the values correspond to
  207. * `x,y`. If the first value is a string, it is applied as the name of the
  208. * point, and the `x` value is inferred.
  209. * ```js
  210. * data: [
  211. * [0, 9],
  212. * [1, 2],
  213. * [2, 8]
  214. * ]
  215. * ```
  216. *
  217. * 3. An array of objects with named values. The following snippet shows only a
  218. * few settings, see the complete options set below. If the total number of
  219. * data points exceeds the series'
  220. * [turboThreshold](#series.spline.turboThreshold), this option is not
  221. * available.
  222. * ```js
  223. * data: [{
  224. * x: 1,
  225. * y: 9,
  226. * name: "Point2",
  227. * color: "#00FF00"
  228. * }, {
  229. * x: 1,
  230. * y: 0,
  231. * name: "Point1",
  232. * color: "#FF00FF"
  233. * }]
  234. * ```
  235. *
  236. * @sample {highcharts} highcharts/chart/reflow-true/
  237. * Numerical values
  238. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  239. * Arrays of numeric x and y
  240. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  241. * Arrays of datetime x and y
  242. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  243. * Arrays of point.name and y
  244. * @sample {highcharts} highcharts/series/data-array-of-objects/
  245. * Config objects
  246. *
  247. * @type {Array<number|Array<(number|string),number>|*>}
  248. * @extends series.line.data
  249. * @product highcharts highstock
  250. * @apioption series.spline.data
  251. */