6 Dygraph
= window
.Dygraph
;
7 } else if (typeof(module
) !== 'undefined') {
8 Dygraph
= require('../dygraph');
12 * Given three sequential points, p0, p1 and p2, find the left and right
13 * control points for p1.
15 * The three points are expected to have x and y properties.
17 * The alpha parameter controls the amount of smoothing.
18 * If α=0, then both control points will be the same as p1 (i.e. no smoothing).
20 * Returns [l1x, l1y, r1x, r1y]
22 * It's guaranteed that the line from (l1x, l1y)-(r1x, r1y) passes through p1.
23 * Unless allowFalseExtrema is set, then it's also guaranteed that:
27 * The basic algorithm is:
28 * 1. Put the control points l1 and r1 α of the way down (p0, p1) and (p1, p2).
29 * 2. Shift l1 and r2 so that the line l1–r1 passes through p1
30 * 3. Adjust to prevent false extrema while keeping p1 on the l1–r1 line.
32 * This is loosely based on the HighCharts algorithm.
34 function getControlPoints(p0
, p1
, p2
, opt_alpha
, opt_allowFalseExtrema
) {
35 var alpha
= (opt_alpha
!== undefined
) ? opt_alpha
: 1/3; // 0=no smoothing
, 1=crazy smoothing
36 var allowFalseExtrema
= opt_allowFalseExtrema
|| false;
39 return [p1
.x
, p1
.y
, null, null];
42 // Step 1: Position the control points along each line segment.
43 var l1x
= (1 - alpha
) * p1
.x
+ alpha
* p0
.x
,
44 l1y
= (1 - alpha
) * p1
.y
+ alpha
* p0
.y
,
45 r1x
= (1 - alpha
) * p1
.x
+ alpha
* p2
.x
,
46 r1y
= (1 - alpha
) * p1
.y
+ alpha
* p2
.y
;
48 // Step 2: shift the points up so that p1 is on the l1–r1 line.
50 // This can be derived w/ some basic algebra
.
51 var deltaY
= p1
.y
- r1y
- (p1
.x
- r1x
) * (l1y
- r1y
) / (l1x
- r1x
);
56 // Step 3: correct to avoid false extrema.
57 if (!allowFalseExtrema
) {
58 if (l1y
> p0
.y
&& l1y
> p1
.y
) {
59 l1y
= Math
.max(p0
.y
, p1
.y
);
61 } else if (l1y
< p0
.y
&& l1y
< p1
.y
) {
62 l1y
= Math
.min(p0
.y
, p1
.y
);
66 if (r1y
> p1
.y
&& r1y
> p2
.y
) {
67 r1y
= Math
.max(p1
.y
, p2
.y
);
69 } else if (r1y
< p1
.y
&& r1y
< p2
.y
) {
70 r1y
= Math
.min(p1
.y
, p2
.y
);
75 return [l1x
, l1y
, r1x
, r1y
];
78 // i.e. is none of (null, undefined, NaN)
80 return !!x
&& !isNaN(x
);
83 // A plotter which uses splines to create a smooth curve.
84 // See tests/plotters.html
for a demo
.
85 // Can be controlled via smoothPlotter.smoothing
86 function smoothPlotter(e
) {
87 var ctx
= e
.drawingContext
,
91 ctx
.moveTo(points
[0].canvasx
, points
[0].canvasy
);
93 // right control point for previous point
94 var lastRightX
= points
[0].canvasx
, lastRightY
= points
[0].canvasy
;
96 for (var i
= 1; i
< points
.length
; i
++) {
97 var p0
= points
[i
- 1],
100 p0
= p0
&& isOK(p0
.canvasy
) ? p0
: null;
101 p1
= p1
&& isOK(p1
.canvasy
) ? p1
: null;
102 p2
= p2
&& isOK(p2
.canvasy
) ? p2
: null;
104 var controls
= getControlPoints({x
: p0
.canvasx
, y
: p0
.canvasy
},
105 {x
: p1
.canvasx
, y
: p1
.canvasy
},
106 p2
&& {x
: p2
.canvasx
, y
: p2
.canvasy
},
107 smoothPlotter
.smoothing
);
108 // Uncomment to show the control points:
109 // ctx.lineTo(lastRightX, lastRightY);
110 // ctx.lineTo(controls[0], controls[1]);
111 // ctx.lineTo(p1.canvasx, p1.canvasy);
112 lastRightX
= (lastRightX
!== null) ? lastRightX
: p0
.canvasx
;
113 lastRightY
= (lastRightY
!== null) ? lastRightY
: p0
.canvasy
;
114 ctx
.bezierCurveTo(lastRightX
, lastRightY
,
115 controls
[0], controls
[1],
116 p1
.canvasx
, p1
.canvasy
);
117 lastRightX
= controls
[2];
118 lastRightY
= controls
[3];
120 // We're starting again after a missing point.
121 ctx
.moveTo(p1
.canvasx
, p1
.canvasy
);
122 lastRightX
= p1
.canvasx
;
123 lastRightY
= p1
.canvasy
;
125 lastRightX
= lastRightY
= null;
131 smoothPlotter
.smoothing
= 1/3;
132 smoothPlotter
._getControlPoints
= getControlPoints
; // for testing
134 // older versions exported a global.
135 // This will be removed in the future.
136 // The preferred way to access smoothPlotter is via Dygraph.smoothPlotter.
137 window
.smoothPlotter
= smoothPlotter
;
138 Dygraph
.smoothPlotter
= smoothPlotter
;