-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathChart.js
202 lines (180 loc) · 7.13 KB
/
Chart.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* Programming contest management system
* Copyright © 2012 Luca Wehrstedt <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var Chart = new function () {
var self = this;
self.draw_chart = function (canvas, y_min, y_max, y_def, h_def, x_int, data, color, marks) {
// canvas is the context
/*
canvas (GWTCanvas): the canvas this chart will be drawn on
y_min (float): the y value corresponding to the bottom of the chart
y_max (float): the y value corresponding to the top of the chart
(note: y_min can be grater than y_max - the chart will be upside-down)
y_def (float): the default y value (the line will start at that value)
h_def (float): the default height of the colored area
x_int (list of tuples of float): the list of x intervals to be drawn,
in the form [begin, end)
data (list of tuples of float): the data to be drawn, in the form (x, y, h)
color (tuple of int): the r, g and b components of the color for the line
marks (list of float): the y values at which horizontal lines will be drawn
*/
// width and height
var wid = canvas.width;
var hei = canvas.height;
// the padding around the chart
var pad_l = 22;
var pad_r = 1;
var pad_t = 6;
var pad_b = 6;
// the intervals of allowed x values
var x_size = 0;
for (var i in x_int) {
x_size += x_int[i][1] - x_int[i][0];
}
// convert values to canvas coordinates
var get_x = function (x) {
return pad_l + x * (wid - pad_l - pad_r) / x_size;
};
var get_y = function (y) {
return pad_t + (y_max - y) * (hei - pad_t - pad_b) / (y_max - y_min == 0? 1: y_max - y_min);
};
// clear the canvas
canvas.width = wid;
// get the context
var context = canvas.getContext("2d");
// draw the axes
context.lineWidth = 2;
context.strokeStyle = "#dddddd";
context.beginPath();
context.moveTo(pad_l, pad_t);
context.lineTo(pad_l, hei - pad_b);
context.lineTo(wid - pad_r, hei - pad_b);
context.lineTo(wid - pad_r, pad_t);
context.stroke();
// draw horizontal markers
context.lineWidth = 1;
context.moveTo(pad_l, pad_t);
context.lineTo(wid - pad_r, pad_t);
context.stroke();
for (var i in marks) {
context.beginPath();
context.moveTo(get_x(0), get_y(marks[i]));
context.lineTo(get_x(x_size), get_y(marks[i]));
context.stroke();
}
// draw labels on the axes
context.fillStyle = "#FFFFFF";
context.textAlign = "right";
context.textBaseline = "middle";
if (y_min != y_max)
context.fillText(y_min.toString(), 18, hei - pad_b);
context.fillText(y_max.toString(), 18, pad_t);
for (var i in marks) {
context.fillText(marks[i].toString(), 18, get_y(marks[i]));
}
var i = 0 // index of current interval
var x_cum = 0 // cumulated x value (sum of the size of the first i-1 intervals)
var x_pos = 0 // current x value
var y_pos = y_def // current y value
var h_pos = h_def // current h value
var x_b = 0 // the 'begin' value of the current interval
var x_e = 0 // the 'end' value of the current interval
var tops = [[x_pos, y_pos]] // points of the line marking the top of the area
var bots = [[x_pos, y_pos + h_pos]] // points of the line marking the bottom
// helper method to open an interval
var open_group = function () {
context.lineWidth = 2;
context.strokeStyle = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
context.beginPath();
x_b = x_int[i][0];
x_e = x_int[i][1];
context.moveTo(get_x(x_pos), get_y(y_pos));
}
// helper method to close an interval
var close_group = function () {
x_cum += x_e - x_b;
x_pos = x_cum;
context.lineTo(get_x(x_pos), get_y(y_pos));
tops.push([x_pos, y_pos]);
bots.push([x_pos, y_pos + h_pos])
context.stroke();
}
// helper method to draw a separator
var draw_separator = function () {
context.lineWidth = 2;
context.strokeStyle = "#dddddd";
context.beginPath();
context.moveTo(get_x(x_pos), get_y(y_min));
context.lineTo(get_x(x_pos), get_y(y_max));
context.stroke();
}
open_group();
for (var idx in data) {
var x = data[idx][0];
var y = data[idx][1];
var h = data[idx][2];
while (i < x_int.length && x_e <= x) {
close_group();
i += 1;
if (i < x_int.length) {
draw_separator();
open_group();
} else {
x_b = 0;
x_e = 0;
}
}
if (x_b <= x && x < x_e) {
x_pos = x_cum + x - x_b;
context.lineTo(get_x(x_pos), get_y(y_pos));
tops.push([x_pos, y_pos]);
bots.push([x_pos, y_pos + h_pos]);
y_pos = y;
h_pos = h;
context.lineTo(get_x(x_pos), get_y(y_pos));
tops.push([x_pos, y_pos]);
bots.push([x_pos, y_pos + h_pos]);
} else {
y_pos = y;
h_pos = h;
context.moveTo(get_x(x_pos), get_y(y_pos));
tops.push([x_pos, y_pos]);
bots.push([x_pos, y_pos + h_pos]);
}
}
if (i < x_int.length) {
close_group();
i += 1;
}
while (i < x_int.length) {
draw_separator();
open_group();
close_group();
i += 1;
}
context.fillStyle = "rgba(" + color[0] + "," + color[1] + "," + color[2] + ",0.3)";
context.beginPath();
context.moveTo(get_x(tops[0][0]), get_y(tops[0][1]));
for (var i = 0; i < tops.length; i += 1) {
context.lineTo(get_x(tops[i][0]), get_y(tops[i][1]));
}
for (var i = bots.length - 1; i >= 0; i -= 1) {
context.lineTo(get_x(bots[i][0]), get_y(bots[i][1]));
}
context.closePath();
context.fill();
};
};