|
From: Daniel J S. <dan...@ie...> - 2007-06-28 19:56:17
|
I've re-enabled for a discussion on the quick refresh...
> Dan: <<I think it doesn't get us in the direction we want to go, which is
> to eventually store the data, all of it, and then just reuse it in whatever
> way we see fit.>>
>
> Please give a concise example of what you mean. How would it be different
> from what we are already doing? What part of "all of it" are we failing to
> store?
>
> Let's spin off discussion of axis scaling to the mailing list. It's only
> marginally relevant to the issue of refresh vs replot.
>
Let me illustrate the difference by discussing the drawbacks of this macro:
#define STORE_WITH_LOG_AND_UPDATE_RANGE(STORE, VALUE, TYPE, AXIS, \
OUT_ACTION, UNDEF_ACTION) \
do { \
/* HBB 20000726: new check, to avoid crashes with axis index -1 */ \
if (AXIS==-1) \
break; \
/* HBB 20040304: new check to avoid storing infinities and NaNs */ \
if (! (VALUE > -VERYLARGE && VALUE < VERYLARGE)) { \
TYPE = UNDEFINED; \
UNDEF_ACTION; \
break; \
} \
if (axis_array[AXIS].log) { \
if (VALUE<0.0) { \
TYPE = UNDEFINED; \
UNDEF_ACTION; \
break; \
} else if (VALUE == 0.0) { \
STORE = -VERYLARGE; \
TYPE = OUTRANGE; \
OUT_ACTION; \
break; \
} else { \
STORE = AXIS_DO_LOG(AXIS,VALUE); \
} \
} else \
STORE = VALUE; \
if (TYPE != INRANGE) \
break; /* don't set y range if x is outrange, for example */ \
if ((int)AXIS < 0) \
break; /* HBB 20000507: don't check range if not a coordinate */ \
if ( VALUE<axis_array[AXIS].min ) { \
if (axis_array[AXIS].autoscale & AUTOSCALE_MIN) \
axis_array[AXIS].min = VALUE; \
else { \
TYPE = OUTRANGE; \
OUT_ACTION; \
break; \
} \
} \
if ( VALUE>axis_array[AXIS].max ) { \
if (axis_array[AXIS].autoscale & AUTOSCALE_MAX) \
axis_array[AXIS].max = VALUE; \
else { \
TYPE = OUTRANGE; \
OUT_ACTION; \
} \
} \
} while(0)
There is too much going on there. What I'm working on changes this to simply
#define STORE_VALUE(STORE, VALUE, TYPE, AXIS) \
do { \
/* HBB 20000726: new check, to avoid crashes with axis index -1 */ \
if (AXIS==-1) \
break; \
/* HBB 20040304: new check to avoid storing infinities and NaNs */ \
if (! (VALUE > -VERYLARGE && VALUE < VERYLARGE)) { \
TYPE = UNDEFINED; \
break; \
} \
STORE = VALUE; \
} while(0)
Here is the problem with doing the range adjustment and logarithm upfront.
1) The logarithm is especially egregious. Notice that if log scale axis is active, the cases of (VALUE<0.0) and (VALUE == 0.0) do not store the data. The log is monotonic and one-to-one only for positive real numbers. So if one does
unset logscale x
refresh
the refresh will not work. Some of the user's data may have been lost. That's unreliable. So that is why I say we should just store the data without and translation; that way we will always have the original data. The translations can be done when needed, and having the extra UNDEFINEDs in the data doesn't seem to effect the results. In fact, not translating seems to simplify things in other places. There are other potential limitations to hinder development. The Bragg reflection example in mgr.dem might be another.
plot "big_peak.dat" title "Rate" with errorbars, \
"" smooth csplines t "Rate"
Looking at the code, the csplines will change the data in the plot structure. One can't refresh this plot too easily, I don't think.
2) Doing the update range right away isn't so bad. Patch [ gnuplot-Patches-1723715 ] has a compact routine to recompute the ranges. Compact routines are good, but having two methods of updating ranges is a problem. One has to make sure that if either is changed, so is the other. I'd prefer just a single routine than one for plot/replot and one for refresh. With that in mind I've organized the range checking code as follows:
/* Various preparations on data, without actually modifying the data. */
static void
prepare_data(struct curve_points *cp)
{
int i;
/* Go through all the points classifying according to the
* logarithmic scale and sign of data.
*/
for (i = 0; i < cp->p_count; ++i) {
switch (cp->plot_style) {
/* Only x and y are relevant to axis scaling */
default:
int_warn(NO_CARET, "Missing style %d in switch statement", cp->plot_style);
case LINES:
case POINTSTYLE:
case IMPULSES:
case LINESPOINTS:
case DOTS:
case FILLEDCURVES:
#ifdef EAM_DATASTRINGS
case LABELPOINTS:
#endif
axis_update_range(cp->x_axis, cp->points[i].x, &cp->points[i].type);
axis_update_range(cp->y_axis, cp->points[i].y, &cp->points[i].type);
break;
case XERRORBARS:
case BOXES:
case XERRORLINES:
axis_update_range(cp->x_axis, cp->points[i].xlow, &cp->points[i].type);
axis_update_range(cp->x_axis, cp->points[i].xhigh, &cp->points[i].type);
axis_update_range(cp->y_axis, cp->points[i].y, &cp->points[i].type);
break;
case YERRORBARS:
case CANDLESTICKS:
case FINANCEBARS:
case YERRORLINES:
axis_update_range(cp->x_axis, cp->points[i].x, &cp->points[i].type);
axis_update_range(cp->y_axis, cp->points[i].ylow, &cp->points[i].type);
axis_update_range(cp->y_axis, cp->points[i].yhigh, &cp->points[i].type);
break;
case XYERRORBARS:
case BOXXYERROR:
case BOXERROR:
case VECTOR:
case XYERRORLINES:
#ifdef EAM_HISTOGRAMS
case HISTOGRAMS:
#endif
axis_update_range(cp->x_axis, cp->points[i].xlow, &cp->points[i].type);
axis_update_range(cp->x_axis, cp->points[i].xhigh, &cp->points[i].type);
axis_update_range(cp->y_axis, cp->points[i].ylow, &cp->points[i].type);
axis_update_range(cp->y_axis, cp->points[i].yhigh, &cp->points[i].type);
break;
#ifdef WITH_IMAGE
case IMAGE:
case RGBIMAGE:
/* Size of pixels determined by spacing of grid. Do nothing here, but
* after all data collected then update the range. */
break;
#endif
}
#ifdef EAM_HISTOGRAMS
/* Fiddle the auto-scaling data for histograms */
if (cp->plot_style == HISTOGRAMS)
histogram_update_range(cp);
#endif /* EAM_HISTOGRAMS */
#ifdef WITH_IMAGE
if (cp->plot_style == IMAGE || cp->plot_style == RGBIMAGE) {
/* Images are defined by a grid representing centers of pixels.
* Compensate for extent of the image so `set autoscale fix`
* uses outer edges of outer pixels in axes adjustment.
*/
cp->image_properties.type = IC_PALETTE;
plot_image_or_update_axes(cp, TRUE);
}
#endif
}
}
Notice how the above has organized updates for individual points, then groups of points afterward when all data is collected (histogram_update_range was fiddle_histograms).
OK, in brief, here's what I've worked toward (and should post soon), but it doesn't have refresh, just organizes things better, I think.
datafiles
|
| ("plot"/"replot")
|
V
raw_stored_data <--- ("refresh")
|
| (splines perhaps)
|
V
preprocessed_data ----------------> onplot
|
| (prepare_data())
|
V
ranges_for_data
|
| (parameterize routine)
|
V
function_data -------------------> onplot
|
| (prepare_data())
|
V
ranges_for_functions
You may ask Why run the generated function data through preparation? Because I'm wondering if we should have gnuplot not give a message like this
gnuplot> set logscale x
gnuplot> plot x
x range must be greater than 0 for log scale
and simply do the best it can if there is non-positive data. The data from [-10:0] of default [-10:10] could be ignored... or in this case we could choose the default to be [1:10] in the case of logscale.
Dan
|