root / hci / trunk / eneraptor-web-app / web-app / js / flot / jquery.flot.image.js @ 70
History | View | Annotate | Download (7.46 KB)
1 | 11 | alexbesir | /*
|
---|---|---|---|
2 | Flot plugin for plotting images, e.g. useful for putting ticks on a
|
||
3 | prerendered complex visualization.
|
||
4 | |||
5 | The data syntax is [[image, x1, y1, x2, y2], ...] where (x1, y1) and
|
||
6 | (x2, y2) are where you intend the two opposite corners of the image to
|
||
7 | end up in the plot. Image must be a fully loaded Javascript image (you
|
||
8 | can make one with new Image()). If the image is not complete, it's
|
||
9 | skipped when plotting.
|
||
10 | |||
11 | There are two helpers included for retrieving images. The easiest work
|
||
12 | the way that you put in URLs instead of images in the data (like
|
||
13 | ["myimage.png", 0, 0, 10, 10]), then call $.plot.image.loadData(data,
|
||
14 | options, callback) where data and options are the same as you pass in
|
||
15 | to $.plot. This loads the images, replaces the URLs in the data with
|
||
16 | the corresponding images and calls "callback" when all images are
|
||
17 | loaded (or failed loading). In the callback, you can then call $.plot
|
||
18 | with the data set. See the included example.
|
||
19 | |||
20 | A more low-level helper, $.plot.image.load(urls, callback) is also
|
||
21 | included. Given a list of URLs, it calls callback with an object
|
||
22 | mapping from URL to Image object when all images are loaded or have
|
||
23 | failed loading.
|
||
24 | |||
25 | Options for the plugin are
|
||
26 | |||
27 | series: {
|
||
28 | images: {
|
||
29 | show: boolean
|
||
30 | anchor: "corner" or "center"
|
||
31 | alpha: [0,1]
|
||
32 | }
|
||
33 | }
|
||
34 | |||
35 | which can be specified for a specific series
|
||
36 | |||
37 | $.plot($("#placeholder"), [{ data: [ ... ], images: { ... } ])
|
||
38 | |||
39 | Note that because the data format is different from usual data points,
|
||
40 | you can't use images with anything else in a specific data series.
|
||
41 | |||
42 | Setting "anchor" to "center" causes the pixels in the image to be
|
||
43 | anchored at the corner pixel centers inside of at the pixel corners,
|
||
44 | effectively letting half a pixel stick out to each side in the plot.
|
||
45 | |||
46 | |||
47 | A possible future direction could be support for tiling for large
|
||
48 | images (like Google Maps).
|
||
49 | |||
50 | */
|
||
51 | |||
52 | (function ($) { |
||
53 | var options = {
|
||
54 | series: {
|
||
55 | images: {
|
||
56 | show: false, |
||
57 | alpha: 1, |
||
58 | anchor: "corner" // or "center" |
||
59 | } |
||
60 | } |
||
61 | }; |
||
62 | |||
63 | $.plot.image = {};
|
||
64 | |||
65 | $.plot.image.loadDataImages = function (series, options, callback) { |
||
66 | var urls = [], points = [];
|
||
67 | |||
68 | var defaultShow = options.series.images.show;
|
||
69 | |||
70 | $.each(series, function (i, s) { |
||
71 | if (!(defaultShow || s.images.show))
|
||
72 | return;
|
||
73 | |||
74 | if (s.data)
|
||
75 | s = s.data; |
||
76 | |||
77 | $.each(s, function (i, p) { |
||
78 | if (typeof p[0] == "string") { |
||
79 | urls.push(p[0]);
|
||
80 | points.push(p); |
||
81 | } |
||
82 | }); |
||
83 | }); |
||
84 | |||
85 | $.plot.image.load(urls, function (loadedImages) { |
||
86 | $.each(points, function (i, p) { |
||
87 | var url = p[0]; |
||
88 | if (loadedImages[url])
|
||
89 | p[0] = loadedImages[url];
|
||
90 | }); |
||
91 | |||
92 | callback(); |
||
93 | }); |
||
94 | } |
||
95 | |||
96 | $.plot.image.load = function (urls, callback) { |
||
97 | var missing = urls.length, loaded = {};
|
||
98 | if (missing == 0) |
||
99 | callback({}); |
||
100 | |||
101 | $.each(urls, function (i, url) { |
||
102 | var handler = function () { |
||
103 | --missing; |
||
104 | |||
105 | loaded[url] = this;
|
||
106 | |||
107 | if (missing == 0) |
||
108 | callback(loaded); |
||
109 | }; |
||
110 | |||
111 | $('<img />').load(handler).error(handler).attr('src', url); |
||
112 | }); |
||
113 | } |
||
114 | |||
115 | function draw(plot, ctx) { |
||
116 | var plotOffset = plot.getPlotOffset();
|
||
117 | |||
118 | $.each(plot.getData(), function (i, series) { |
||
119 | var points = series.datapoints.points,
|
||
120 | ps = series.datapoints.pointsize; |
||
121 | |||
122 | for (var i = 0; i < points.length; i += ps) { |
||
123 | var img = points[i],
|
||
124 | x1 = points[i + 1], y1 = points[i + 2], |
||
125 | x2 = points[i + 3], y2 = points[i + 4], |
||
126 | xaxis = series.xaxis, yaxis = series.yaxis, |
||
127 | tmp; |
||
128 | |||
129 | // actually we should check img.complete, but it
|
||
130 | // appears to be a somewhat unreliable indicator in
|
||
131 | // IE6 (false even after load event)
|
||
132 | if (!img || img.width <= 0 || img.height <= 0) |
||
133 | continue;
|
||
134 | |||
135 | if (x1 > x2) {
|
||
136 | tmp = x2; |
||
137 | x2 = x1; |
||
138 | x1 = tmp; |
||
139 | } |
||
140 | if (y1 > y2) {
|
||
141 | tmp = y2; |
||
142 | y2 = y1; |
||
143 | y1 = tmp; |
||
144 | } |
||
145 | |||
146 | // if the anchor is at the center of the pixel, expand the
|
||
147 | // image by 1/2 pixel in each direction
|
||
148 | if (series.images.anchor == "center") { |
||
149 | tmp = 0.5 * (x2-x1) / (img.width - 1); |
||
150 | x1 -= tmp; |
||
151 | x2 += tmp; |
||
152 | tmp = 0.5 * (y2-y1) / (img.height - 1); |
||
153 | y1 -= tmp; |
||
154 | y2 += tmp; |
||
155 | } |
||
156 | |||
157 | // clip
|
||
158 | if (x1 == x2 || y1 == y2 ||
|
||
159 | x1 >= xaxis.max || x2 <= xaxis.min || |
||
160 | y1 >= yaxis.max || y2 <= yaxis.min) |
||
161 | continue;
|
||
162 | |||
163 | var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; |
||
164 | if (x1 < xaxis.min) {
|
||
165 | sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); |
||
166 | x1 = xaxis.min; |
||
167 | } |
||
168 | |||
169 | if (x2 > xaxis.max) {
|
||
170 | sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); |
||
171 | x2 = xaxis.max; |
||
172 | } |
||
173 | |||
174 | if (y1 < yaxis.min) {
|
||
175 | sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); |
||
176 | y1 = yaxis.min; |
||
177 | } |
||
178 | |||
179 | if (y2 > yaxis.max) {
|
||
180 | sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); |
||
181 | y2 = yaxis.max; |
||
182 | } |
||
183 | |||
184 | x1 = xaxis.p2c(x1); |
||
185 | x2 = xaxis.p2c(x2); |
||
186 | y1 = yaxis.p2c(y1); |
||
187 | y2 = yaxis.p2c(y2); |
||
188 | |||
189 | // the transformation may have swapped us
|
||
190 | if (x1 > x2) {
|
||
191 | tmp = x2; |
||
192 | x2 = x1; |
||
193 | x1 = tmp; |
||
194 | } |
||
195 | if (y1 > y2) {
|
||
196 | tmp = y2; |
||
197 | y2 = y1; |
||
198 | y1 = tmp; |
||
199 | } |
||
200 | |||
201 | tmp = ctx.globalAlpha; |
||
202 | ctx.globalAlpha *= series.images.alpha; |
||
203 | ctx.drawImage(img, |
||
204 | sx1, sy1, sx2 - sx1, sy2 - sy1, |
||
205 | x1 + plotOffset.left, y1 + plotOffset.top, |
||
206 | x2 - x1, y2 - y1); |
||
207 | ctx.globalAlpha = tmp; |
||
208 | } |
||
209 | }); |
||
210 | } |
||
211 | |||
212 | function processRawData(plot, series, data, datapoints) { |
||
213 | if (!series.images.show)
|
||
214 | return;
|
||
215 | |||
216 | // format is Image, x1, y1, x2, y2 (opposite corners)
|
||
217 | datapoints.format = [ |
||
218 | { required: true }, |
||
219 | { x: true, number: true, required: true }, |
||
220 | { y: true, number: true, required: true }, |
||
221 | { x: true, number: true, required: true }, |
||
222 | { y: true, number: true, required: true } |
||
223 | ]; |
||
224 | } |
||
225 | |||
226 | function init(plot) { |
||
227 | plot.hooks.processRawData.push(processRawData); |
||
228 | plot.hooks.draw.push(draw); |
||
229 | } |
||
230 | |||
231 | $.plot.plugins.push({
|
||
232 | init: init,
|
||
233 | options: options,
|
||
234 | name: 'image', |
||
235 | version: '1.1' |
||
236 | }); |
||
237 | })(jQuery); |