Add code in graf.c to merge connected lines with the same slope before
plotting. This speeds output and reduces file size when the vector length is much larger than the number of pixels. It should help with Feature Request #58: "Graph plotting under windows redraw very slow and hard to work with", but is not a full fix.
This commit is contained in:
parent
ce9bc9fc64
commit
f4285384e7
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue