diff --git a/src/frontend/plotting/graf.c b/src/frontend/plotting/graf.c index 648ba104b..4369ae784 100644 --- a/src/frontend/plotting/graf.c +++ b/src/frontend/plotting/graf.c @@ -264,10 +264,169 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */ } +/* Once the line compression code is thorougly tested, checking code can + * be removed. But for now ... + */ +#define LINE_COMPRESSION_CHECKS + +/* Data and functions for line compression: + * try to not keep drawing the same pixels by combining co-linear segments. + */ + +static struct { + enum { EMPTY, LINE, VERTICAL } state; + int x_start, y_start, x_end, y_end; + int lc_min, lc_max; +#define prev_x2 lc_min // Alternate name +#ifdef LINE_COMPRESSION_CHECKS + struct dvec *dv; // Sanity checking. +#endif +} LC; + +/* Flush pending line drawing. */ + +static void LC_flush(void) +{ + switch (LC.state) { + case EMPTY: + return; + case LINE: + DevDrawLine(LC.x_start, LC.y_start, LC.x_end, LC.y_end, FALSE); + break; + case VERTICAL: + DevDrawLine(LC.x_start, LC.lc_min, LC.x_start, LC.lc_max, FALSE); + break; + } + LC.state = EMPTY; +} + +/* This replaces DevDrawLine() - low-level line drawing call. */ + +#ifdef LINE_COMPRESSION_CHECKS +static void drawLine(int x1, int y1, int x2, int y2, struct dvec *dv) +{ + if (LC.dv) { + if (LC.dv != dv) { + fprintf(cp_err, "LC: DV changed!\n"); + LC_flush(); + LC.dv = dv; + } + } else { + LC.dv = dv; + if (LC.state != EMPTY) { + fprintf(cp_err, "LC: State %d but DV NULL.\n", (int)LC.state); + LC_flush(); + } + } +#else +static void drawLine(int x1, int y1, int x2, int y2) +{ +#endif + switch (LC.state) { + refill: + LC_flush(); + // Fall through ... + case EMPTY: + if (x1 == x2) { + /* Vertical */ + + LC.state = VERTICAL; + LC.x_start = x1; + LC.y_end = y2; + if (y1 < y2) { + LC.lc_min = y1; + LC.lc_max = y2; + } else { + LC.lc_min = y2; + LC.lc_max = y1; + } + } else { + LC.state = LINE; + LC.prev_x2 = x2; + + /* Store with LC.x_start < LC.x_end. */ + + if (x1 < x2) { + LC.x_start = x1; + LC.y_start = y1; + LC.x_end = x2; + LC.y_end = y2; + } else { + LC.x_start = x2; + LC.y_start = y2; + LC.x_end = x1; + LC.y_end = y1; + } + } + break; + case LINE: + if ((int64_t)(x2 - x1) * (LC.y_end - LC.y_start) != + (int64_t)(y2 - y1) * (LC.x_end - LC.x_start)) { + /* Not in line. */ + + goto refill; + } + if (x1 != LC.prev_x2) { + /* Not contiguous. */ + + if (x1 > LC.x_end) { + if (x2 > LC.x_end) { + /* Hole. */ + + goto refill; + } + LC.x_end = x1; + LC.y_end = y1; + } else if (x1 < LC.x_start) { + if (x2 < LC.x_start) { + /* Hole. */ + + goto refill; + } + LC.x_start = x1; + LC.y_start = y1; + } + } + + if (x2 > LC.x_end) { + LC.x_end = x2; + LC.y_end = y2; + } else if (x2 < LC.x_start) { + LC.x_start = x2; + LC.y_start = y2; + } + LC.prev_x2 = x2; + break; + case VERTICAL: + if (x1 != LC.x_start || x2 != LC.x_start) + goto refill; + if (y1 != LC.y_end) { + /* Not contiguous, check for hole. */ + + if (y1 < LC.lc_min) { + if (y2 < LC.lc_min) + goto refill; + LC.lc_min = y1; + } else if (y1 > LC.lc_max) { + if (y2 > LC.lc_max) + goto refill; + LC.lc_max = y1; + } + } + + if (y2 < LC.lc_min) + LC.lc_min = y2; + else if (y2 > LC.lc_max) + LC.lc_max = y2; + LC.y_end = y2; + break; + } +} + /* * Add a point to the curve we're currently drawing. * Should be in between a gr_init() and a gr_end() - * expect when iplotting, very bad hack + * except when iplotting, very bad hack * Differences from old gr_point: * We save points here, instead of in lower levels. * Assume we are in right context @@ -327,7 +486,11 @@ void gr_point(struct dvec *dv, /* If it's a linear plot, ignore first point since we don't want to connect with oldx and oldy. */ if (np) - DevDrawLine(fromx, fromy, tox, toy, FALSE); +#ifdef LINE_COMPRESSION_CHECKS + drawLine(fromx, fromy, tox, toy, dv); +#else + drawLine(fromx, fromy, tox, toy); +#endif if ((tics = currentgraph->ticdata) != NULL) { for (; *tics < HUGE; tics++) @@ -348,7 +511,11 @@ void gr_point(struct dvec *dv, DatatoScreen(currentgraph, 0.0, currentgraph->datawindow.ymin, &dummy, &ymin); - DevDrawLine(tox, ymin, tox, toy, FALSE); +#ifdef LINE_COMPRESSION_CHECKS + drawLine(tox, ymin, tox, toy, dv); +#else + drawLine(tox, ymin, tox, toy); +#endif break; case PLOT_POINT: /* Here, gi_linestyle is the character used for the point. */ @@ -497,7 +664,15 @@ void drawlegend(GRAPH *graph, int plotno, struct dvec *dv) /* end one plot of a graph */ void gr_end(struct dvec *dv) { + LC_flush(); +#ifdef LINE_COMPRESSION_CHECKS + if (LC.dv && LC.dv != dv) + fprintf(cp_err, "LC: DV changed in gr_end()!\n"); + else + LC.dv = NULL; +#else NG_IGNORE(dv); +#endif DevUpdate(); } @@ -895,6 +1070,10 @@ static int iplot(struct plot *pl, int id) (isreal(v) ? v->v_realdata[len - 2] : realpart(v->v_compdata[len - 2])), len - 1); + LC_flush(); // Disable line compression here .. +#ifdef LINE_COMPRESSION_CHECKS + LC.dv = NULL; // ... and suppress warnings. +#endif } } }