|
1 YUI.add('series-line-util', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 * Provides functionality for drawing lines in a series. |
|
5 * |
|
6 * @module charts |
|
7 * @submodule series-line-util |
|
8 */ |
|
9 /** |
|
10 * Utility class used for drawing lines. |
|
11 * |
|
12 * @class Lines |
|
13 * @constructor |
|
14 * @submodule series-line-util |
|
15 */ |
|
16 var Y_Lang = Y.Lang; |
|
17 function Lines(){} |
|
18 |
|
19 Lines.prototype = { |
|
20 /** |
|
21 * @property _lineDefaults |
|
22 * @type Object |
|
23 * @private |
|
24 */ |
|
25 _lineDefaults: null, |
|
26 |
|
27 /** |
|
28 * Creates a graphic in which to draw a series. |
|
29 * |
|
30 * @method _getGraphic |
|
31 * @return Graphic |
|
32 * @private |
|
33 */ |
|
34 _getGraphic: function() |
|
35 { |
|
36 var graphic = this.get("graphic") || this.get("graph").get("graphic"); |
|
37 if(!this._lineGraphic) |
|
38 { |
|
39 this._lineGraphic = graphic.addShape({type: "path"}); |
|
40 } |
|
41 this._lineGraphic.clear(); |
|
42 return this._lineGraphic; |
|
43 }, |
|
44 |
|
45 /** |
|
46 * Toggles visibility |
|
47 * |
|
48 * @method _toggleVisible |
|
49 * @param {Boolean} visible indicates visibilitye |
|
50 * @private |
|
51 */ |
|
52 _toggleVisible: function(visible) |
|
53 { |
|
54 if(this._lineGraphic) |
|
55 { |
|
56 this._lineGraphic.set("visible", visible); |
|
57 } |
|
58 }, |
|
59 |
|
60 /** |
|
61 * Draws lines for the series. |
|
62 * |
|
63 * @method drawLines |
|
64 * @protected |
|
65 */ |
|
66 drawLines: function() |
|
67 { |
|
68 if(this.get("xcoords").length < 1) |
|
69 { |
|
70 return; |
|
71 } |
|
72 var isNumber = Y_Lang.isNumber, |
|
73 xcoords, |
|
74 ycoords, |
|
75 direction = this.get("direction"), |
|
76 len, |
|
77 lastPointValid, |
|
78 pointValid, |
|
79 noPointsRendered = true, |
|
80 lastValidX, |
|
81 lastValidY, |
|
82 nextX, |
|
83 nextY, |
|
84 i, |
|
85 styles = this.get("styles").line, |
|
86 lineType = styles.lineType, |
|
87 lc = styles.color || this._getDefaultColor(this.get("graphOrder"), "line"), |
|
88 lineAlpha = styles.alpha, |
|
89 dashLength = styles.dashLength, |
|
90 gapSpace = styles.gapSpace, |
|
91 connectDiscontinuousPoints = styles.connectDiscontinuousPoints, |
|
92 discontinuousType = styles.discontinuousType, |
|
93 discontinuousDashLength = styles.discontinuousDashLength, |
|
94 discontinuousGapSpace = styles.discontinuousGapSpace, |
|
95 path = this._getGraphic(); |
|
96 if(this._stacked) |
|
97 { |
|
98 xcoords = this.get("stackedXCoords"); |
|
99 ycoords = this.get("stackedYCoords"); |
|
100 } |
|
101 else |
|
102 { |
|
103 xcoords = this.get("xcoords"); |
|
104 ycoords = this.get("ycoords"); |
|
105 } |
|
106 len = direction === "vertical" ? ycoords.length : xcoords.length; |
|
107 path.set("stroke", { |
|
108 weight: styles.weight, |
|
109 color: lc, |
|
110 opacity: lineAlpha |
|
111 }); |
|
112 for(i = 0; i < len; i = ++i) |
|
113 { |
|
114 nextX = xcoords[i]; |
|
115 nextY = ycoords[i]; |
|
116 pointValid = isNumber(nextX) && isNumber(nextY); |
|
117 if(!pointValid) |
|
118 { |
|
119 lastPointValid = pointValid; |
|
120 continue; |
|
121 } |
|
122 if(noPointsRendered) |
|
123 { |
|
124 noPointsRendered = false; |
|
125 path.moveTo(nextX, nextY); |
|
126 } |
|
127 else if(lastPointValid) |
|
128 { |
|
129 if(lineType !== "dashed") |
|
130 { |
|
131 path.lineTo(nextX, nextY); |
|
132 } |
|
133 else |
|
134 { |
|
135 this.drawDashedLine(path, lastValidX, lastValidY, nextX, nextY, |
|
136 dashLength, |
|
137 gapSpace); |
|
138 } |
|
139 } |
|
140 else if(!connectDiscontinuousPoints) |
|
141 { |
|
142 path.moveTo(nextX, nextY); |
|
143 } |
|
144 else |
|
145 { |
|
146 if(discontinuousType !== "solid") |
|
147 { |
|
148 this.drawDashedLine(path, lastValidX, lastValidY, nextX, nextY, |
|
149 discontinuousDashLength, |
|
150 discontinuousGapSpace); |
|
151 } |
|
152 else |
|
153 { |
|
154 path.lineTo(nextX, nextY); |
|
155 } |
|
156 } |
|
157 lastValidX = nextX; |
|
158 lastValidY = nextY; |
|
159 lastPointValid = true; |
|
160 } |
|
161 path.end(); |
|
162 }, |
|
163 |
|
164 /** |
|
165 * Connects data points with a consistent curve for a series. |
|
166 * |
|
167 * @method drawSpline |
|
168 * @protected |
|
169 */ |
|
170 drawSpline: function() |
|
171 { |
|
172 if(this.get("xcoords").length < 1) |
|
173 { |
|
174 return; |
|
175 } |
|
176 var xcoords = this.get("xcoords"), |
|
177 ycoords = this.get("ycoords"), |
|
178 curvecoords = this.getCurveControlPoints(xcoords, ycoords), |
|
179 len = curvecoords.length, |
|
180 cx1, |
|
181 cx2, |
|
182 cy1, |
|
183 cy2, |
|
184 x, |
|
185 y, |
|
186 i = 0, |
|
187 styles = this.get("styles").line, |
|
188 path = this._getGraphic(), |
|
189 lineAlpha = styles.alpha, |
|
190 color = styles.color || this._getDefaultColor(this.get("graphOrder"), "line"); |
|
191 path.set("stroke", { |
|
192 weight: styles.weight, |
|
193 color: color, |
|
194 opacity: lineAlpha |
|
195 }); |
|
196 path.moveTo(xcoords[0], ycoords[0]); |
|
197 for(; i < len; i = ++i) |
|
198 { |
|
199 x = curvecoords[i].endx; |
|
200 y = curvecoords[i].endy; |
|
201 cx1 = curvecoords[i].ctrlx1; |
|
202 cx2 = curvecoords[i].ctrlx2; |
|
203 cy1 = curvecoords[i].ctrly1; |
|
204 cy2 = curvecoords[i].ctrly2; |
|
205 path.curveTo(cx1, cy1, cx2, cy2, x, y); |
|
206 } |
|
207 path.end(); |
|
208 }, |
|
209 |
|
210 /** |
|
211 * Draws a dashed line between two points. |
|
212 * |
|
213 * @method drawDashedLine |
|
214 * @param {Number} xStart The x position of the start of the line |
|
215 * @param {Number} yStart The y position of the start of the line |
|
216 * @param {Number} xEnd The x position of the end of the line |
|
217 * @param {Number} yEnd The y position of the end of the line |
|
218 * @param {Number} dashSize the size of dashes, in pixels |
|
219 * @param {Number} gapSize the size of gaps between dashes, in pixels |
|
220 * @private |
|
221 */ |
|
222 drawDashedLine: function(path, xStart, yStart, xEnd, yEnd, dashSize, gapSize) |
|
223 { |
|
224 dashSize = dashSize || 10; |
|
225 gapSize = gapSize || 10; |
|
226 var segmentLength = dashSize + gapSize, |
|
227 xDelta = xEnd - xStart, |
|
228 yDelta = yEnd - yStart, |
|
229 delta = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2)), |
|
230 segmentCount = Math.floor(Math.abs(delta / segmentLength)), |
|
231 radians = Math.atan2(yDelta, xDelta), |
|
232 xCurrent = xStart, |
|
233 yCurrent = yStart, |
|
234 i; |
|
235 xDelta = Math.cos(radians) * segmentLength; |
|
236 yDelta = Math.sin(radians) * segmentLength; |
|
237 |
|
238 for(i = 0; i < segmentCount; ++i) |
|
239 { |
|
240 path.moveTo(xCurrent, yCurrent); |
|
241 path.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize); |
|
242 xCurrent += xDelta; |
|
243 yCurrent += yDelta; |
|
244 } |
|
245 |
|
246 path.moveTo(xCurrent, yCurrent); |
|
247 delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent)); |
|
248 |
|
249 if(delta > dashSize) |
|
250 { |
|
251 path.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize); |
|
252 } |
|
253 else if(delta > 0) |
|
254 { |
|
255 path.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta); |
|
256 } |
|
257 |
|
258 path.moveTo(xEnd, yEnd); |
|
259 }, |
|
260 |
|
261 /** |
|
262 * Default values for `styles` attribute. |
|
263 * |
|
264 * @method _getLineDefaults |
|
265 * @return Object |
|
266 * @protected |
|
267 */ |
|
268 _getLineDefaults: function() |
|
269 { |
|
270 return { |
|
271 alpha: 1, |
|
272 weight: 6, |
|
273 lineType:"solid", |
|
274 dashLength:10, |
|
275 gapSpace:10, |
|
276 connectDiscontinuousPoints:true, |
|
277 discontinuousType:"solid", |
|
278 discontinuousDashLength:10, |
|
279 discontinuousGapSpace:10 |
|
280 }; |
|
281 } |
|
282 }; |
|
283 Y.augment(Lines, Y.Attribute); |
|
284 Y.Lines = Lines; |
|
285 |
|
286 |
|
287 }, '@VERSION@'); |