Commit | Line | Data |
---|---|---|
6a1aa64f DV |
1 | /* |
2 | PlotKit Sweet Canvas Renderer | |
3 | ============================= | |
4 | Canvas Renderer for PlotKit which looks pretty! | |
5 | ||
6 | Copyright | |
7 | --------- | |
8 | Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net> | |
9 | For use under the BSD license. <http://www.liquidx.net/plotkit> | |
10 | */ | |
11 | ||
12 | // ------------------------------------------------------------------------- | |
13 | // Check required components | |
14 | // ------------------------------------------------------------------------- | |
15 | ||
16 | try { | |
17 | if (typeof(PlotKit.CanvasRenderer) == 'undefined') | |
18 | { | |
19 | throw ""; | |
20 | } | |
21 | } | |
22 | catch (e) { | |
23 | throw "SweetCanvas depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Layout, Canvas}" | |
24 | } | |
25 | ||
26 | ||
27 | if (typeof(PlotKit.SweetCanvasRenderer) == 'undefined') { | |
28 | PlotKit.SweetCanvasRenderer = {}; | |
29 | } | |
30 | ||
31 | PlotKit.SweetCanvasRenderer = function(element, layout, options) { | |
32 | if (arguments.length > 0) { | |
33 | this.__init__(element, layout, options); | |
34 | } | |
35 | }; | |
36 | ||
37 | PlotKit.SweetCanvasRenderer.NAME = "PlotKit.SweetCanvasRenderer"; | |
38 | PlotKit.SweetCanvasRenderer.VERSION = PlotKit.VERSION; | |
39 | ||
40 | PlotKit.SweetCanvasRenderer.__repr__ = function() { | |
41 | return "[" + this.NAME + " " + this.VERSION + "]"; | |
42 | }; | |
43 | ||
44 | PlotKit.SweetCanvasRenderer.toString = function() { | |
45 | return this.__repr__(); | |
46 | }; | |
47 | ||
48 | // --------------------------------------------------------------------- | |
49 | // Subclassing Magic | |
50 | // --------------------------------------------------------------------- | |
51 | ||
52 | PlotKit.SweetCanvasRenderer.prototype = new PlotKit.CanvasRenderer(); | |
53 | PlotKit.SweetCanvasRenderer.prototype.constructor = PlotKit.SweetCanvasRenderer; | |
54 | PlotKit.SweetCanvasRenderer.__super__ = PlotKit.CanvasRenderer.prototype; | |
55 | ||
56 | // --------------------------------------------------------------------- | |
57 | // Constructor | |
58 | // --------------------------------------------------------------------- | |
59 | ||
60 | PlotKit.SweetCanvasRenderer.prototype.__init__ = function(el, layout, opts) { | |
61 | var moreOpts = PlotKit.Base.officeBlue(); | |
62 | MochiKit.Base.update(moreOpts, opts); | |
63 | PlotKit.SweetCanvasRenderer.__super__.__init__.call(this, el, layout, moreOpts); | |
64 | }; | |
65 | ||
66 | // --------------------------------------------------------------------- | |
67 | // Extended Plotting Functions | |
68 | // --------------------------------------------------------------------- | |
69 | ||
70 | PlotKit.SweetCanvasRenderer.prototype._renderBarChart = function() { | |
71 | var bind = MochiKit.Base.bind; | |
72 | var shadowColor = Color.blackColor().colorWithAlpha(0.1).toRGBString(); | |
73 | ||
74 | var prepareFakeShadow = function(context, x, y, w, h) { | |
75 | context.fillStyle = shadowColor; | |
76 | context.fillRect(x-2, y-2, w+4, h+2); | |
77 | context.fillStyle = shadowColor; | |
78 | context.fillRect(x-1, y-1, w+2, h+1); | |
79 | }; | |
80 | ||
81 | var colorCount = this.options.colorScheme.length; | |
82 | var colorScheme = this.options.colorScheme; | |
83 | var setNames = PlotKit.Base.keys(this.layout.datasets); | |
84 | var setCount = setNames.length; | |
85 | ||
86 | var chooseColor = function(name) { | |
87 | for (var i = 0; i < setCount; i++) { | |
88 | if (name == setNames[i]) | |
89 | return colorScheme[i%colorCount]; | |
90 | } | |
91 | return colorScheme[0]; | |
92 | }; | |
93 | ||
94 | var drawRect = function(context, bar) { | |
95 | var x = this.area.w * bar.x + this.area.x; | |
96 | var y = this.area.h * bar.y + this.area.y; | |
97 | var w = this.area.w * bar.w; | |
98 | var h = this.area.h * bar.h; | |
99 | ||
100 | if ((w < 1) || (h < 1)) | |
101 | return; | |
102 | ||
103 | context.save(); | |
104 | ||
105 | context.shadowBlur = 5.0; | |
106 | context.shadowColor = Color.fromHexString("#888888").toRGBString(); | |
107 | ||
108 | if (this.isIE) { | |
109 | context.save(); | |
110 | context.fillStyle = "#cccccc"; | |
111 | context.fillRect(x-2, y-2, w+4, h+2); | |
112 | context.restore(); | |
113 | } | |
114 | else { | |
115 | prepareFakeShadow(context, x, y, w, h); | |
116 | } | |
117 | ||
118 | if (this.options.shouldFill) { | |
119 | context.fillStyle = chooseColor(bar.name).toRGBString(); | |
120 | context.fillRect(x, y, w, h); | |
121 | } | |
122 | ||
123 | context.shadowBlur = 0; | |
124 | context.strokeStyle = Color.whiteColor().toRGBString(); | |
125 | context.lineWidth = 2.0; | |
126 | ||
127 | if (this.options.shouldStroke) { | |
128 | context.strokeRect(x, y, w, h); | |
129 | } | |
130 | ||
131 | context.restore(); | |
132 | ||
133 | }; | |
134 | this._renderBarChartWrap(this.layout.bars, bind(drawRect, this)); | |
135 | }; | |
136 | ||
137 | PlotKit.SweetCanvasRenderer.prototype._renderLineChart = function() { | |
138 | var context = this.element.getContext("2d"); | |
139 | var colorCount = this.options.colorScheme.length; | |
140 | var colorScheme = this.options.colorScheme; | |
141 | var setNames = PlotKit.Base.keys(this.layout.datasets); | |
142 | var setCount = setNames.length; | |
143 | var bind = MochiKit.Base.bind; | |
144 | ||
145 | ||
146 | for (var i = 0; i < setCount; i++) { | |
147 | var setName = setNames[i]; | |
148 | var color = colorScheme[i%colorCount]; | |
149 | var strokeX = this.options.strokeColorTransform; | |
150 | ||
151 | // setup graphics context | |
152 | context.save(); | |
153 | ||
154 | // create paths | |
155 | var makePath = function(ctx) { | |
156 | ctx.beginPath(); | |
157 | ctx.moveTo(this.area.x, this.area.y + this.area.h); | |
158 | var addPoint = function(ctx_, point) { | |
159 | if (point.name == setName) | |
160 | ctx_.lineTo(this.area.w * point.x + this.area.x, | |
161 | this.area.h * point.y + this.area.y); | |
162 | }; | |
163 | MochiKit.Iter.forEach(this.layout.points, partial(addPoint, ctx), this); | |
164 | ctx.lineTo(this.area.w + this.area.x, | |
165 | this.area.h + this.area.y); | |
166 | ctx.lineTo(this.area.x, this.area.y + this.area.h); | |
167 | ctx.closePath(); | |
168 | }; | |
169 | ||
170 | // faux shadow for firefox | |
171 | if (this.options.shouldFill) { | |
172 | context.save(); | |
173 | if (this.isIE) { | |
174 | context.fillStyle = "#cccccc"; | |
175 | } | |
176 | else { | |
177 | context.fillStyle = Color.blackColor().colorWithAlpha(0.2).toRGBString(); | |
178 | } | |
179 | context.translate(-1, -2); | |
180 | bind(makePath, this)(context); | |
181 | if (this.options.shouldFill) { | |
182 | context.fill(); | |
183 | } | |
184 | context.restore(); | |
185 | } | |
186 | ||
187 | context.shadowBlur = 5.0; | |
188 | context.shadowColor = Color.fromHexString("#888888").toRGBString(); | |
189 | context.fillStyle = color.toRGBString(); | |
190 | context.lineWidth = 2.0; | |
191 | context.strokeStyle = Color.whiteColor().toRGBString(); | |
192 | ||
193 | if (this.options.shouldFill) { | |
194 | bind(makePath, this)(context); | |
195 | context.fill(); | |
196 | } | |
197 | if (this.options.shouldStroke) { | |
198 | bind(makePath, this)(context); | |
199 | context.stroke(); | |
200 | } | |
201 | context.restore(); | |
202 | } | |
203 | }; | |
204 | ||
205 | PlotKit.SweetCanvasRenderer.prototype._renderPieChart = function() { | |
206 | var context = this.element.getContext("2d"); | |
207 | ||
208 | var colorCount = this.options.colorScheme.length; | |
209 | var slices = this.layout.slices; | |
210 | ||
211 | var centerx = this.area.x + this.area.w * 0.5; | |
212 | var centery = this.area.y + this.area.h * 0.5; | |
213 | var radius = Math.min(this.area.w * this.options.pieRadius, | |
214 | this.area.h * this.options.pieRadius); | |
215 | ||
216 | if (this.isIE) { | |
217 | centerx = parseInt(centerx); | |
218 | centery = parseInt(centery); | |
219 | radius = parseInt(radius); | |
220 | } | |
221 | ||
222 | // NOTE NOTE!! Canvas Tag draws the circle clockwise from the y = 0, x = 1 | |
223 | // so we have to subtract 90 degrees to make it start at y = 1, x = 0 | |
224 | ||
225 | if (!this.isIE) { | |
226 | context.save(); | |
227 | var shadowColor = Color.blackColor().colorWithAlpha(0.2); | |
228 | context.fillStyle = shadowColor.toRGBString(); | |
229 | context.shadowBlur = 5.0; | |
230 | context.shadowColor = Color.fromHexString("#888888").toRGBString(); | |
231 | context.translate(1, 1); | |
232 | context.beginPath(); | |
233 | context.moveTo(centerx, centery); | |
234 | context.arc(centerx, centery, radius + 2, 0, Math.PI*2, false); | |
235 | context.closePath(); | |
236 | context.fill(); | |
237 | context.restore(); | |
238 | } | |
239 | ||
240 | context.save(); | |
241 | context.strokeStyle = Color.whiteColor().toRGBString(); | |
242 | context.lineWidth = 2.0; | |
243 | for (var i = 0; i < slices.length; i++) { | |
244 | var color = this.options.colorScheme[i%colorCount]; | |
245 | context.fillStyle = color.toRGBString(); | |
246 | ||
247 | var makePath = function() { | |
248 | context.beginPath(); | |
249 | context.moveTo(centerx, centery); | |
250 | context.arc(centerx, centery, radius, | |
251 | slices[i].startAngle - Math.PI/2, | |
252 | slices[i].endAngle - Math.PI/2, | |
253 | false); | |
254 | context.lineTo(centerx, centery); | |
255 | context.closePath(); | |
256 | }; | |
257 | ||
258 | if (Math.abs(slices[i].startAngle - slices[i].endAngle) > 0.0001) { | |
259 | if (this.options.shouldFill) { | |
260 | makePath(); | |
261 | context.fill(); | |
262 | } | |
263 | if (this.options.shouldStroke) { | |
264 | makePath(); | |
265 | context.stroke(); | |
266 | } | |
267 | } | |
268 | } | |
269 | context.restore(); | |
270 | }; | |
271 | ||
272 | PlotKit.SweetCanvasRenderer.prototype._renderBackground = function() { | |
273 | var context = this.element.getContext("2d"); | |
274 | ||
275 | if (this.layout.style == "bar" || this.layout.style == "line") { | |
276 | context.save(); | |
277 | context.fillStyle = this.options.backgroundColor.toRGBString(); | |
278 | context.fillRect(this.area.x, this.area.y, this.area.w, this.area.h); | |
279 | context.strokeStyle = this.options.axisLineColor.toRGBString(); | |
280 | context.lineWidth = 1.0; | |
281 | ||
282 | var ticks = this.layout.yticks; | |
283 | var horiz = false; | |
284 | if (this.layout.style == "bar" && | |
285 | this.layout.options.barOrientation == "horizontal") { | |
286 | ticks = this.layout.xticks; | |
287 | horiz = true; | |
288 | } | |
289 | ||
290 | for (var i = 0; i < ticks.length; i++) { | |
291 | var x1 = 0; | |
292 | var y1 = 0; | |
293 | var x2 = 0; | |
294 | var y2 = 0; | |
295 | ||
296 | if (horiz) { | |
297 | x1 = ticks[i][0] * this.area.w + this.area.x; | |
298 | y1 = this.area.y; | |
299 | x2 = x1; | |
300 | y2 = y1 + this.area.h; | |
301 | } | |
302 | else { | |
303 | x1 = this.area.x; | |
304 | y1 = ticks[i][0] * this.area.h + this.area.y; | |
305 | x2 = x1 + this.area.w; | |
306 | y2 = y1; | |
307 | } | |
308 | ||
309 | context.beginPath(); | |
310 | context.moveTo(x1, y1); | |
311 | context.lineTo(x2, y2); | |
312 | context.closePath(); | |
313 | context.stroke(); | |
314 | } | |
315 | context.restore(); | |
316 | } | |
317 | else { | |
318 | PlotKit.SweetCanvasRenderer.__super__._renderBackground.call(this); | |
319 | } | |
320 | }; | |
321 | ||
322 | // Namespace Iniitialisation | |
323 | ||
324 | PlotKit.SweetCanvas = {} | |
325 | PlotKit.SweetCanvas.SweetCanvasRenderer = PlotKit.SweetCanvasRenderer; | |
326 | ||
327 | PlotKit.SweetCanvas.EXPORT = [ | |
328 | "SweetCanvasRenderer" | |
329 | ]; | |
330 | ||
331 | PlotKit.SweetCanvas.EXPORT_OK = [ | |
332 | "SweetCanvasRenderer" | |
333 | ]; | |
334 | ||
335 | PlotKit.SweetCanvas.__new__ = function() { | |
336 | var m = MochiKit.Base; | |
337 | ||
338 | m.nameFunctions(this); | |
339 | ||
340 | this.EXPORT_TAGS = { | |
341 | ":common": this.EXPORT, | |
342 | ":all": m.concat(this.EXPORT, this.EXPORT_OK) | |
343 | }; | |
344 | }; | |
345 | ||
346 | PlotKit.SweetCanvas.__new__(); | |
347 | MochiKit.Base._exportSymbols(this, PlotKit.SweetCanvas); | |
348 |