On Sunday 31 May 2009, James R. Van Zandt wrote:
>
> A year ago, Ethan wrote:
> >> I figured to leave the existing log-scale code in place, as you
> >> suggested above, while adding a new more general mechanism that worked
> >> on the original stored coordinates. Once the general mechanism was
> >> in place and working, the old log-scale code could be removed without
> >> ever having to modify it to work with "last-minute" scaling.
>
> Here's my reply. Do you think this makes sense?
>
> > I think I see how that would work:
> >
> > Currently:
> > Upon "set log x", all stored x values are transformed and on "unset
> > log x" they are inverse transformed.
> >
> > Phase 1:
> > Add a scaling "newlog".
> >
> > Add to AXIS a pointer to a function that transforms the data. The
> > function takes two parameters: the datum to be transformed, and a
> > double. For scaling "linear" or "log" the function is a no-op. For
> > "newlog", the extra parameter is the base of the logs. Otherwise the
> > extra parameter is ignored.
I have been thinking a bit about this. I think that the core requirement
is simply to store two function pointers for each axis: the transform and
its inverse. The forward transform is used [only?] by the routines that
map plot coordinates to terminal coordinates. It is conceptually easiest
to show for the routine map_position_double():
%%%%%%%%%%%%%%%%%% current code %%%%%%%%%%%%%%%%%%%%%
/*{{{ map_position_double */
static void
map_position_double(
struct position *pos,
double *x, double *y,
const char *what)
{
switch (pos->scalex) {
case first_axes:
{
double xx = axis_log_value_checked(FIRST_X_AXIS, pos->x, what);
*x = AXIS_MAP(FIRST_X_AXIS, xx);
break;
}
...
%%%%%%%%%%%%%%% proposed code %%%%%%%%%%%%%%%%%%%%%%%%%
/*{{{ map_position_double */
static void
map_position_double(
struct position *pos,
double *x, double *y,
const char *what)
{
switch (pos->scalex) {
case first_axes:
{
double xx = pos->x;
if (FIRST_X_AXIS.transform)
xx = (FIRST_X_AXIS.transform)xx;
*x = AXIS_MAP(FIRST_X_AXIS, xx);
break;
}
...
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The transform is obviously not applied in the case statements handling
screen, character, or graph coordinates. It only applies to the axial
coordinates.
Note: this edit by itself doesn't actually do much, because the data points
are usually plotted via a direct calls to the macros map_x() and map_y().
I made a very quick hack to test specifically a log transform on axis y2,
and found that for simple plots it was sufficient to modify the macros
AXIS_MAP(axis, variable) and AXIS_SETSCALE(axis, out_low, out_high)
in axis.h. I'm sure there will turn out to be other places that need
tweaking as well, but this is looking quite do-able.
The inverse transform is obviously needed for mouse feedback. But it
would also be available for guiding the choice of sampling intervals
and tic placement if needed.
I am not following you about needing a second parameter for log scale.
The appearance of the plot is independent of the base of the log.
Choosing a different base would introduce a constant scale multiplier,
which is re-scaled out of existence by conversion to terminal coordinates.
> > Always do "last-minute" scaling: When plotting a value, call the
> > function via the supplied pointer to transform it.
> > On "set log x" or "unset prob x" etc., update the function pointer.
> > On "(un)set log x", continue to (un)transform the stored data.
> >
> > Phase 2 (after initial checkout):
> > Rename scaling "log" to "oldlog", and rename "newlog" to "log".
I suggest to introduce a new command
{un}set transform {x|y|x2|y2|...} func(dummy)
Phase 1 is to start using this command.
Phase 2 is to trap the older command "set log y" and treat it as
"set transform y log(y)".
> > Phase 3 (after a major release):
> > Delete the "oldlog" commands and the code to (un)transform the stored
> > data.
> >
> > The above only implements standard scalings (linear, log, probability,
> > and maybe weibull). To accommodate a user-defined scaling function,
> > the scaling step needs to be able to point to an action table. We
> > could use that mechanism to implement the standard scalings too -
> > building the action tables from static strings like "_linear(x,b)=x"
> > or "_log(x,b)=log(x)/log(b)". However, I'm worried that would be too
> > slow. The faster method would be to give the scaling function three
> > parameters (datum, extra, and action table), and let the standard
> > scaling functions ignore the third parameter.
While I understand your concern about speed in evaluating the function,
I point out that we have extensive benchmarks on how much it costs.
Every time you put parentheses in a 'using' statement you incur the cost
of action table evaluation.
plot 'foo' using ($1):($2)
is slower than
plot 'foo' using 1:2
And yes, there have been a small number of complaints about the speed hit,
but it is only noticeable for very large datasets. A while back I showed
that unexpectedly, at least under linux the largest part of this hit comes
from from enabling/disabling FPE trapping around each point.
But that's a digression.
I suggest that if we implement this, and it works, then we can worry later
about further optimizing the speed. For some common cases, e.g. log(), we
could store a pointer to the C library function rather than to an action
table.
Summary:
- I could make simple plots work using a last-minute log transform by modifying
only 2 macros in axis.h. This is looking quite do-able, although I would
prefer to turn these macros back into real subroutines for readability if
nothing else.
- Unresolved issues:
+ how to deal with singularities in the transform? We won't hit them until
we are in the middle of plotting.
+ how to pass the transform to an external helper (gnuplot_x11) or wrap it
in the driver output (canvas terminal)?
+ tic placement (although I can report that my quick hack "just worked" for
placing logscale tics on simple plots with no change to the tic code at all
- Ethan
------------------------------------------------------------------------------
Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT
is a gathering of tech-side developers & brand creativity professionals. Meet
the minds behind Google Creative Lab, Visual Complexity, Processing, &
iPhoneDevCamp as they present alongside digital heavyweights like Barbarian
Group, R/GA, & Big Spaceship.
http://p.sf.net/sfu/creativitycat-com
_______________________________________________
gnuplot-beta mailing list
gnuplot-beta@...
https://lists.sourceforge.net/lists/listinfo/gnuplot-beta