Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3284 vikas 1
/*
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 drawSeries(plot, ctx, series) {
116
        var plotOffset = plot.getPlotOffset();
117
 
118
        if (!series.images || !series.images.show)
119
            return;
120
 
121
        var points = series.datapoints.points,
122
            ps = series.datapoints.pointsize;
123
 
124
        for (var i = 0; i < points.length; i += ps) {
125
            var img = points[i],
126
                x1 = points[i + 1], y1 = points[i + 2],
127
                x2 = points[i + 3], y2 = points[i + 4],
128
                xaxis = series.xaxis, yaxis = series.yaxis,
129
                tmp;
130
 
131
            // actually we should check img.complete, but it
132
            // appears to be a somewhat unreliable indicator in
133
            // IE6 (false even after load event)
134
            if (!img || img.width <= 0 || img.height <= 0)
135
                continue;
136
 
137
            if (x1 > x2) {
138
                tmp = x2;
139
                x2 = x1;
140
                x1 = tmp;
141
            }
142
            if (y1 > y2) {
143
                tmp = y2;
144
                y2 = y1;
145
                y1 = tmp;
146
            }
147
 
148
            // if the anchor is at the center of the pixel, expand the 
149
            // image by 1/2 pixel in each direction
150
            if (series.images.anchor == "center") {
151
                tmp = 0.5 * (x2-x1) / (img.width - 1);
152
                x1 -= tmp;
153
                x2 += tmp;
154
                tmp = 0.5 * (y2-y1) / (img.height - 1);
155
                y1 -= tmp;
156
                y2 += tmp;
157
            }
158
 
159
            // clip
160
            if (x1 == x2 || y1 == y2 ||
161
                x1 >= xaxis.max || x2 <= xaxis.min ||
162
                y1 >= yaxis.max || y2 <= yaxis.min)
163
                continue;
164
 
165
            var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;
166
            if (x1 < xaxis.min) {
167
                sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);
168
                x1 = xaxis.min;
169
            }
170
 
171
            if (x2 > xaxis.max) {
172
                sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);
173
                x2 = xaxis.max;
174
            }
175
 
176
            if (y1 < yaxis.min) {
177
                sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);
178
                y1 = yaxis.min;
179
            }
180
 
181
            if (y2 > yaxis.max) {
182
                sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);
183
                y2 = yaxis.max;
184
            }
185
 
186
            x1 = xaxis.p2c(x1);
187
            x2 = xaxis.p2c(x2);
188
            y1 = yaxis.p2c(y1);
189
            y2 = yaxis.p2c(y2);
190
 
191
            // the transformation may have swapped us
192
            if (x1 > x2) {
193
                tmp = x2;
194
                x2 = x1;
195
                x1 = tmp;
196
            }
197
            if (y1 > y2) {
198
                tmp = y2;
199
                y2 = y1;
200
                y1 = tmp;
201
            }
202
 
203
            tmp = ctx.globalAlpha;
204
            ctx.globalAlpha *= series.images.alpha;
205
            ctx.drawImage(img,
206
                          sx1, sy1, sx2 - sx1, sy2 - sy1,
207
                          x1 + plotOffset.left, y1 + plotOffset.top,
208
                          x2 - x1, y2 - y1);
209
            ctx.globalAlpha = tmp;
210
        }
211
    }
212
 
213
    function processRawData(plot, series, data, datapoints) {
214
        if (!series.images.show)
215
            return;
216
 
217
        // format is Image, x1, y1, x2, y2 (opposite corners)
218
        datapoints.format = [
219
            { required: true },
220
            { x: true, number: true, required: true },
221
            { y: true, number: true, required: true },
222
            { x: true, number: true, required: true },
223
            { y: true, number: true, required: true }
224
        ];
225
    }
226
 
227
    function init(plot) {
228
        plot.hooks.processRawData.push(processRawData);
229
        plot.hooks.drawSeries.push(drawSeries);
230
    }
231
 
232
    $.plot.plugins.push({
233
        init: init,
234
        options: options,
235
        name: 'image',
236
        version: '1.1'
237
    });
238
})(jQuery);