63 |
63 |
64 int left, top; ///< Where to start drawing the graph, in pixels. |
64 int left, top; ///< Where to start drawing the graph, in pixels. |
65 uint height; ///< The height of the graph in pixels. |
65 uint height; ///< The height of the graph in pixels. |
66 StringID format_str_y_axis; |
66 StringID format_str_y_axis; |
67 byte colors[GRAPH_MAX_DATASETS]; |
67 byte colors[GRAPH_MAX_DATASETS]; |
68 Money cost[GRAPH_MAX_DATASETS][24]; ///< last 2 years |
68 OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][24]; ///< last 2 years |
69 }; |
69 }; |
70 |
70 |
71 static void DrawGraph(const GraphDrawer *gw) |
71 static void DrawGraph(const GraphDrawer *gw) |
72 { |
72 { |
73 uint x, y; ///< Reused whenever x and y coordinates are needed. |
73 uint x, y; ///< Reused whenever x and y coordinates are needed. |
131 highest_value = x_axis_offset * 2; |
131 highest_value = x_axis_offset * 2; |
132 |
132 |
133 for (int i = 0; i < gw->num_dataset; i++) { |
133 for (int i = 0; i < gw->num_dataset; i++) { |
134 if (!HasBit(gw->excluded_data, i)) { |
134 if (!HasBit(gw->excluded_data, i)) { |
135 for (int j = 0; j < gw->num_on_x_axis; j++) { |
135 for (int j = 0; j < gw->num_on_x_axis; j++) { |
136 Money datapoint = gw->cost[i][j]; |
136 OverflowSafeInt64 datapoint = gw->cost[i][j]; |
137 |
137 |
138 if (datapoint != INVALID_DATAPOINT) { |
138 if (datapoint != INVALID_DATAPOINT) { |
139 /* For now, if the graph has negative values the scaling is |
139 /* For now, if the graph has negative values the scaling is |
140 * symmetrical about the x axis, so take the absolute value |
140 * symmetrical about the x axis, so take the absolute value |
141 * of each data point. */ |
141 * of each data point. */ |
213 byte color = gw->colors[i]; |
213 byte color = gw->colors[i]; |
214 uint prev_x = INVALID_DATAPOINT_POS; |
214 uint prev_x = INVALID_DATAPOINT_POS; |
215 uint prev_y = INVALID_DATAPOINT_POS; |
215 uint prev_y = INVALID_DATAPOINT_POS; |
216 |
216 |
217 for (int j = 0; j < gw->num_on_x_axis; j++) { |
217 for (int j = 0; j < gw->num_on_x_axis; j++) { |
218 Money datapoint = gw->cost[i][j]; |
218 OverflowSafeInt64 datapoint = gw->cost[i][j]; |
219 |
219 |
220 if (datapoint != INVALID_DATAPOINT) { |
220 if (datapoint != INVALID_DATAPOINT) { |
221 /* XXX: This can overflow if x_axis_offset * datapoint is |
221 /* |
222 * too big to fit in an int64. */ |
222 * Check whether we need to reduce the 'accuracy' of the |
223 y = gw->top + x_axis_offset - (x_axis_offset * datapoint) / highest_value; |
223 * datapoint value and the highest value to splut overflows. |
|
224 * And when 'drawing' 'one million' or 'one million and one' |
|
225 * there is no significant difference, so the least |
|
226 * significant bits can just be removed. |
|
227 * |
|
228 * If there are more bits needed than would fit in a 32 bits |
|
229 * integer, so at about 31 bits because of the sign bit, the |
|
230 * least significant bits are removed. |
|
231 */ |
|
232 int mult_range = FindLastBit(x_axis_offset) + FindLastBit(abs(datapoint)); |
|
233 int reduce_range = max(mult_range - 31, 0); |
|
234 |
|
235 /* Handle negative values differently (don't shift sign) */ |
|
236 if (datapoint < 0) { |
|
237 datapoint = -(abs(datapoint) >> reduce_range); |
|
238 } else { |
|
239 datapoint >>= reduce_range; |
|
240 } |
|
241 |
|
242 y = gw->top + x_axis_offset - (x_axis_offset * datapoint) / (highest_value >> reduce_range); |
224 |
243 |
225 /* Draw the point. */ |
244 /* Draw the point. */ |
226 GfxFillRect(x - 1, y - 1, x + 1, y + 1, color); |
245 GfxFillRect(x - 1, y - 1, x + 1, y + 1, color); |
227 |
246 |
228 /* Draw the line connected to the previous point. */ |
247 /* Draw the line connected to the previous point. */ |