|
@@ -34,12 +34,16 @@ enum {
|
|
|
/* How many horizontal lines to draw. 9 is convenient as that means the
|
|
|
* distance between them is the height of the graph / 8, which is the same
|
|
|
* as height >> 3. */
|
|
|
GRAPH_NUM_LINES_Y = 9,
|
|
|
};
|
|
|
|
|
|
/* Apparently these don't play well with enums. */
|
|
|
static const int64 INVALID_DATAPOINT = LLONG_MAX; // Value used for a datapoint that shouldn't be drawn.
|
|
|
static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn.
|
|
|
|
|
|
typedef struct GraphDrawer {
|
|
|
uint sel; // bitmask of the players *excluded* (e.g. 11111111 means that no players are shown)
|
|
|
byte num_dataset;
|
|
|
byte num_on_x_axis;
|
|
|
bool include_neg;
|
|
|
byte num_vert_lines;
|
|
@@ -58,17 +62,15 @@ typedef struct GraphDrawer {
|
|
|
uint height;
|
|
|
StringID format_str_y_axis;
|
|
|
byte colors[GRAPH_MAX_DATASETS];
|
|
|
int64 cost[GRAPH_MAX_DATASETS][24]; // last 2 years
|
|
|
} GraphDrawer;
|
|
|
|
|
|
static const int64 INVALID_VALUE = 0x80000000;
|
|
|
|
|
|
static void DrawGraph(const GraphDrawer *gw)
|
|
|
{
|
|
|
uint x,y,old_x,old_y;
|
|
|
uint x, y; // Reused whenever x and y coordinates are needed.
|
|
|
int right;
|
|
|
int64 highest_value;
|
|
|
int adj_height;
|
|
|
uint sel;
|
|
|
|
|
|
/* the colors and cost array of GraphDrawer must accomodate
|
|
@@ -130,13 +132,13 @@ static void DrawGraph(const GraphDrawer
|
|
|
sel = gw->sel;
|
|
|
for (int i = 0; i < gw->num_dataset; i++) {
|
|
|
if (!(sel&1)) {
|
|
|
for (int j = 0; j < gw->num_on_x_axis; j++) {
|
|
|
int64 datapoint = gw->cost[i][j];
|
|
|
|
|
|
if (datapoint != INVALID_VALUE) {
|
|
|
if (datapoint != INVALID_DATAPOINT) {
|
|
|
/* For now, if the graph has negative values the scaling is
|
|
|
* symmetrical about the x axis, so take the absolute value
|
|
|
* of each data point. */
|
|
|
highest_value = max(highest_value, myabs(datapoint));
|
|
|
}
|
|
|
}
|
|
@@ -208,32 +210,37 @@ static void DrawGraph(const GraphDrawer
|
|
|
|
|
|
for (int i = 0; i < gw->num_dataset; i++) {
|
|
|
if (!(sel & 1)) {
|
|
|
/* Centre the dot between the grid lines. */
|
|
|
x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2);
|
|
|
|
|
|
byte color = gw->colors[i];
|
|
|
old_y = old_x = INVALID_VALUE;
|
|
|
byte color = gw->colors[i];
|
|
|
uint prev_x = INVALID_DATAPOINT_POS;
|
|
|
uint prev_y = INVALID_DATAPOINT_POS;
|
|
|
|
|
|
for (int j = 0; j < gw->num_on_x_axis; j++) {
|
|
|
int64 datapoint = gw->cost[i][j];
|
|
|
|
|
|
if (datapoint != INVALID_VALUE) {
|
|
|
if (datapoint != INVALID_DATAPOINT) {
|
|
|
/* XXX: This can overflow if adj_height * datapoint is too
|
|
|
* big to fit in an int64. */
|
|
|
y = gw->top + adj_height - (adj_height * datapoint) / highest_value;
|
|
|
|
|
|
/* Draw the point. */
|
|
|
GfxFillRect(x-1, y-1, x+1, y+1, color);
|
|
|
if (old_x != INVALID_VALUE)
|
|
|
GfxDrawLine(old_x, old_y, x, y, color);
|
|
|
|
|
|
/* Draw the line connected to the previous point. */
|
|
|
if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, color);
|
|
|
|
|
|
old_x = x;
|
|
|
old_y = y;
|
|
|
prev_x = x;
|
|
|
prev_y = y;
|
|
|
} else {
|
|
|
old_x = INVALID_VALUE;
|
|
|
prev_x = INVALID_DATAPOINT_POS;
|
|
|
prev_y = INVALID_DATAPOINT_POS;
|
|
|
}
|
|
|
|
|
|
x += GRAPH_X_POSITION_SEPARATION;
|
|
|
}
|
|
|
}
|
|
|
sel >>= 1;
|
|
|
}
|
|
|
}
|
|
@@ -375,13 +382,13 @@ static void OperatingProfitWndProc(Windo
|
|
|
|
|
|
numd = 0;
|
|
|
FOR_ALL_PLAYERS(p) {
|
|
|
if (p->is_active) {
|
|
|
gd.colors[numd] = _colour_gradient[p->player_color][6];
|
|
|
for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : (p->old_economy[j].income + p->old_economy[j].expenses);
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (p->old_economy[j].income + p->old_economy[j].expenses);
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
numd++;
|
|
|
}
|
|
|
|
|
@@ -445,13 +452,13 @@ static void IncomeGraphWndProc(Window *w
|
|
|
|
|
|
numd = 0;
|
|
|
FOR_ALL_PLAYERS(p) {
|
|
|
if (p->is_active) {
|
|
|
gd.colors[numd] = _colour_gradient[p->player_color][6];
|
|
|
for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : p->old_economy[j].income;
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].income;
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
numd++;
|
|
|
}
|
|
|
|
|
@@ -515,13 +522,13 @@ static void DeliveredCargoGraphWndProc(W
|
|
|
|
|
|
numd = 0;
|
|
|
FOR_ALL_PLAYERS(p) {
|
|
|
if (p->is_active) {
|
|
|
gd.colors[numd] = _colour_gradient[p->player_color][6];
|
|
|
for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : p->old_economy[j].delivered_cargo;
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].delivered_cargo;
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
numd++;
|
|
|
}
|
|
|
|
|
@@ -585,13 +592,13 @@ static void PerformanceHistoryWndProc(Wi
|
|
|
|
|
|
numd = 0;
|
|
|
FOR_ALL_PLAYERS(p) {
|
|
|
if (p->is_active) {
|
|
|
gd.colors[numd] = _colour_gradient[p->player_color][6];
|
|
|
for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : p->old_economy[j].performance_history;
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].performance_history;
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
numd++;
|
|
|
}
|
|
|
|
|
@@ -658,13 +665,13 @@ static void CompanyValueGraphWndProc(Win
|
|
|
|
|
|
numd = 0;
|
|
|
FOR_ALL_PLAYERS(p) {
|
|
|
if (p->is_active) {
|
|
|
gd.colors[numd] = _colour_gradient[p->player_color][6];
|
|
|
for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : p->old_economy[j].company_value;
|
|
|
gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].company_value;
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
numd++;
|
|
|
}
|
|
|
|