Commit 54de3eaf authored by Jason Rhinelander's avatar Jason Rhinelander

Add series graph ability to add extra grid lines

parent e8198877
......@@ -27,6 +27,7 @@ void SeriesGraphs::addOptions() {
("resolution,r", above<0>(resolution), "The resolution of the images for PNG output, in pixels per inch.")
("t-min", min<-1>(graph_min_t), "The t value at which to start the graph. If -1, the graph starts at the lowest t value found in the input file(s).")
("t-max", min<-1>(graph_max_t), "The t value at which to end the graph. If -1, the graph ends at the largest t value found in the input file(s).")
("t-extra", value(t_extra), "Draw an extra-wide background grid line at the given `t` value. Can be specified multiple times.")
("y-min", value(graph_min_value), "The data value at which the y-axis should begin. If NaN (the default), the y-axis is sized to fit all of the required values.")
("y-max", value(graph_max_value), "The data value at which the y-axis should end. If NaN (the default), the y-axis is sized to fit all of the required values.")
("t-from", min<-1>(data_min_t), "The t value at which to start including observations. If -1, include all observations. In contrast to --t-min, this controls when data is plotted, not the graph axis limits.")
......
......@@ -100,6 +100,11 @@ class SeriesGraphs : public CmdArgs {
*/
int data_max_t = -1;
/** Draw extra-wide graph t grid marks at these points. Typically the piracy-begins and
* policy-begins periods.
*/
std::vector<int> t_extra;
/** Determines the minimum value to include in the series plot. The default, NaN,
* determines the minimum from the data.
*/
......
......@@ -17,7 +17,12 @@ Series::Series(Target &target, int tmin, int tmax, double ymin, double ymax,
: title{std::move(title)}, x_label{std::move(x_label)},
y_label{std::move(y_label)}, target_{target}
{
setExtents(tmin, tmax, ymin, ymax);
try {
setExtents(tmin, tmax, ymin, ymax);
}
catch (const std::invalid_argument &e) {
throw std::invalid_argument("Unable to initialize series '" + title + "': " + e.what());
}
recalcTicks();
}
......@@ -368,25 +373,35 @@ void Series::finishPage() {
ctx->move_to(graph_left, graph_top);
drawRectangle(ctx, graph_outer_width, graph_outer_height, graph_style, true);
// We're now clipped inside it
std::tuple<LineStyle&, std::set<int>&, std::set<int>&>
tick_grid(tick_grid_style, t_grid, y_grid),
tick_grid_extra(tick_grid_extra_style, t_grid_extra, y_grid_extra);
const double left = graph_left, right = total_width-graph_right, top = graph_top, bottom = total_height-graph_bottom;
// If we have tick grid lines, draw them:
if (tick_grid_style.colour and not (t_grid.empty() and y_grid.empty())) {
double left = graph_left, right = total_width-graph_right, top = graph_top, bottom = total_height-graph_bottom;
for (const auto &t : t_grid) {
double grid_x = t, dontcare = 0;
graph_translation.transform_point(grid_x, dontcare);
ctx->move_to(grid_x, top);
ctx->line_to(grid_x, bottom);
}
for (const auto &y : y_grid) {
double grid_y = y, dontcare = 0;
graph_translation.transform_point(dontcare, grid_y);
ctx->move_to(left, grid_y);
ctx->line_to(right, grid_y);
}
for (const auto &tg : {tick_grid, tick_grid_extra}) {
auto &style = std::get<0>(tg);
auto &ts = std::get<1>(tg), &ys = std::get<2>(tg);
if (style.colour and not (ts.empty() and ys.empty())) {
for (const auto &t : ts) {
double grid_x = t, dontcare = 0;
graph_translation.transform_point(grid_x, dontcare);
ctx->move_to(grid_x, top);
ctx->line_to(grid_x, bottom);
}
for (const auto &y : ys) {
double grid_y = y, dontcare = 0;
graph_translation.transform_point(dontcare, grid_y);
ctx->move_to(left, grid_y);
ctx->line_to(right, grid_y);
}
tick_grid_style.applyTo(ctx);
ctx->stroke();
style.applyTo(ctx);
ctx->stroke();
}
}
// Now draw the graph surface (lines, regions, whatever)
for (const auto &draw : draw_) {
Cairo::SaveGuard saver(ctx);
......
......@@ -196,6 +196,11 @@ class Series {
* lines). */
LineStyle tick_grid_style{Transparent, 0.5};
/** The line style to use for extra grid lines (those set manually into `t_grid_extra` and
* `y_grid_extra`. Defaults to a medium gray line of width 0.5.
*/
LineStyle tick_grid_extra_style{RGBA{0.75}, 0.5};
/** The font colour for tick values. If completely transparent, tick values are not drawn. */
RGBA tick_font_colour{Black};
......@@ -384,6 +389,13 @@ class Series {
*/
std::set<int> t_grid;
/** Extra x-axis grid marks to draw. This can be used to mark special values in addition to
* (and styled differently) than the regular t_grid lines.
*
* The defaults to empty.
*/
std::set<int> t_grid_extra;
/** Y-axis grid mark locations, calculated during construction. Can be recalculated by
* calling recalcTicks(). If cleared, no horizontal grid lines are drawn.
*
......@@ -392,6 +404,9 @@ class Series {
*/
std::set<int> y_grid;
/** Like t_grid_extra, but for y axis values. */
std::set<int> y_grid_extra;
/** Different modes for what to do with the tmin/tmax/ymin/ymax graph end points given
* during construction. */
enum class TickEnds {
......
......@@ -332,6 +332,7 @@ int main(int argc, char *argv[]) {
PDF output(args.output, args.width, args.height);
Series graph(output, tmin, tmax, ymin, ymax);
graph.tick_grid_style.colour = RGBA(0.9);
graph.tick_grid_extra_style.colour = RGBA(0.5);
graph.legend_position = args.legend_position;
graph.legend_rel_x = args.legend_rel_x;
graph.legend_rel_y = args.legend_rel_y;
......@@ -374,6 +375,7 @@ int main(int argc, char *argv[]) {
graph.setExtents(local_tmin, local_tmax, local_ymin, local_ymax, false);
graph.recalcTicks(10, 8, Series::TickEnds::Add);
graph.title = in.title;
graph.t_grid_extra.insert(args.t_extra.cbegin(), args.t_extra.cend());
for (const auto &conf : in.quantiles)
graph.addRegion(conf.second, confband_style);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment