Newer
Older
//**************************************************************************//
//* FRIDA: fast reliable interactive data analysis *//
//* dualplot.cpp: different mechanisms for screen and paper output *//
//* (C) Joachim Wuttke 1990-, v2(C++) 2001- *//
//* http://frida.sourceforge.net *//
//**************************************************************************//
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "mystd.h"
#include "plotaux.h"
#include "coord.h"
#include <ask_simple_value.h>
#include <ask_and_callback.h>
#include "dualplot.h"
//**************************************************************************//
//**************************************************************************//
//! Set invalid limits.
// This means: next plot call will automatically recalculate them.
inf = -INFINITY;
sup = +INFINITY;
void CAxis::setLimits( string axnam, double _inf, double _sup )
if( _inf>=_sup )
throw "Cannot set " + axnam + " plot range " +
strg(_inf) + " .. " + strg(_sup);
if( logflag && _inf<=0 )
throw "Cannot set log " + axnam + " plot range " +
strg(_inf) + " .. " + strg(_sup);
inf = _inf;
sup = _sup;
}
//! Set lin or log, reset ranges to auto.
{
logflag = _log;
setAuto();
}
if ( in=="*" ) {
return;
string s1, s2;
mystd::string_extract_word( in, &s1, &s2 );
if ( !mystd::any2dbl(s1,&inf) )
throw string( "invalid lower bound, expecting real number" );
if ( !mystd::any2dbl(s2,&sup) )
throw string( "invalid upper bound, expecting real number" );
//! Replace limits by rounded values.
void CAxis::setRoundedLimits( string axnam, double _inf, double _sup )
static double relmargin = 0.05;
static double digits = 2;
throw "unexpected inf limits when rounding " + axnam + " plot range";
if( _inf>=_sup )
throw "Cannot round " + axnam + " plot range " +
strg(_inf) + " .. " + strg(_sup);
if( logflag && _inf<=0 )
throw "Cannot round log " + axnam + " plot range " +
strg(_inf) + " .. " + strg(_sup);
inf = _inf;
sup = _sup;
if ( inf==sup ) {
if( logflag ){
inf /= 10;
sup *= 10;
} else {
inf -= 1;
sup += 1;
}
double inf2, sup2;
double mydigits = digits;
if ( !logflag ) { // lin limits
double margin = relmargin * (sup - inf);
do {
if (mydigits>=8.5) return; // do not round
sup2 = mystd::round_decimal( sup+margin, mydigits );
inf2 = mystd::round_decimal( inf-margin, mydigits );
if(sup<0 && sup2>0) sup2 = 0;
if(inf2<0 && inf>0) inf2 = 0;
mydigits += 0.5;
} while (!((inf2<inf) && (sup<sup2)));
} else { // log limits
double ratio = sup / inf;
double margin = exp( relmargin*log(ratio) );
do {
if (mydigits>=8.5) return; // do not round
sup2 = mystd::round_decimal( sup*margin, mydigits );
inf2 = mystd::round_decimal( inf/margin, mydigits );
mydigits += 0.5;
} while ( !((inf2<inf) && (sup<sup2)) );
//! Have finite limits been set ?
return inf!=-INFINITY && sup!=+INFINITY;
//! Is value val contained in this range?
bool CAxis::contains(double val) const
//! Map application scale (inf..sup) to linear plot scale (0..1).
double CAxis::value2plotcoord( double v ) const
throw string( "undefined plot range" );
throw string( "negative value in log range" );
//! Map linear plot scale (0..1) to application scale (inf..sup).
double CAxis::plotcoord2value( double c ) const
{
if ( !finite() )
throw string( "undefined plot range" );
return inf * exp( c*log(sup/inf) );
} else {
return inf + c*(sup-inf);
}
}
if( inf==-INFINITY || sup== +INFINITY )
return "*";
in = sask( quest, def );
if (in==def)
return;
if (in!="")
if (logflag && inf<=0 && inf!=-INFINITY) {
setAuto();
throw string( "log scale requires range above 0" );
}
if (!logflag && ( inf!=-INFINITY && inf<-3e38 ||
sup!=+INFINITY && sup>3e38 ) ) {
setAuto();
throw string( "lin scale exceeds typical PS implementation range "
"(abs<3e38)" );
//! Describe range, for use in table of plot frames.
ret = logflag ? "log" : "lin";
ret += " " + strg(inf);
ret += " " + strg(sup);
return ret;
}
//! Convert value -> plot_coordinate.
return CPLOT_PSMAX * value2plotcoord( v );
//**************************************************************************//
//**************************************************************************//
//! Constructor for plot window: setup for gnuplot and postscript.
CPlot::CPlot( uint _iPlot, bool _logx, bool _logy ) :
iPlot( _iPlot ), maxpoints(12000), X( _logx ), Y( _logy )
// Start gnuplot. Use input redirection so that gnuplot receives
// commands from a gp_fifo which is created here.
string fn_fifo = string("/tmp/gnuplot-") + getenv( "LOGNAME" );
if (mkfifo( fn_fifo.c_str(), 0666 )) {
printf("SEVERE SYSTEM ERROR cannot make fifo %s"
" - will not be able to print\n", fn_fifo.c_str() );
return;
}
mystd::system( "gnuplot -title " + strg(iPlot) + " -noraise" +
" < " + fn_fifo + " &" );
// we use open instead of fopen or ofstream,
// because we need non-blocking mode.
if (!(gp_fifo = open(fn_fifo.c_str(), O_WRONLY))) {
printf("SEVERE SYSTEM ERROR cannot open gp_fifo"
" - will not be able to print\n");
return;
}
fcntl(gp_fifo,F_SETFL,O_NONBLOCK);
//! Clear plot frame and buffer.
void CPlot::clearFrame()
// reset buffers for postscript output
string cmd;
ps_accu.clear();
ps_accu.push_back( "\n%% output created by frida2\n");
ps_snum = 0;
ps_pnum = 0;
ps_cnum = 0;
ps_Doc.clear();
//! Change one setup parameter per command.
void CPlot::setAux( string cmd )
if ( cmd=="gnp" )
maxpoints = iask("Max # points in plot", maxpoints);
else if ( cmd=="gxl" ) {
X.setLog( !X.logflag );
printf( "set x %s\n", X.logflag ? "log" : "lin" ); }
else if ( cmd=="gxl+" )
X.setLog( true );
X.setLog( false );
else if ( cmd=="gyl" ) {
Y.setLog( !Y.logflag );
printf( "set y %s\n", Y.logflag ? "log" : "lin" ); }
else if ( cmd=="gyl+" )
Y.setLog( true );
Y.setLog( false );
else if ( cmd=="gxf" ) {
X.force = !X.force;
printf( "force x %s\n", X.force ? "on" : "off" ); }
else if ( cmd=="gxf+" )
X.force = true;
X.force = false;
else if ( cmd=="gyf" ) {
Y.force = !Y.force;
printf( "force y %s\n", Y.force ? "on" : "off" ); }
else if ( cmd=="gyf+" )
Y.force = true;
//! Plot coordinate frame (axes, ticks, labels).
void CPlot::plotFrame()
if (X.logflag) whichlog += "x";
if (Y.logflag) whichlog += "y";
snprintf( outlin, CPLOT_LINSIZ, "\n%d %g %g xSetCoord\n",
snprintf( outlin, CPLOT_LINSIZ, "%d %g %g ySetCoord\n",
snprintf( outlin, CPLOT_LINSIZ, "%% %d %g %g %d zSetCoord\n\n",
0, 0., 0., 0 );
ps_accu.push_back( outlin );
int ntack, ntpt;
double *tack, ticklim[2];
ps_accu.push_back( "\n/xPlotFrame {" );
if ( X.logflag && X.inf<= 0 )
throw "BUG: x log incompatible with limits " + X.str();
plotaux::calc_ticks( X.logflag, X.inf, X.sup,
&ntack, &tack, &ntpt, ticklim );
ps_ticktack(ntack, tack, ntpt, ticklim, &X);
free(tack);
snprintf( outlin, CPLOT_LINSIZ-4, " {(%s", X.C.ps_str().c_str() );
strncat( outlin, ")}\n", CPLOT_LINSIZ );
ps_accu.push_back( outlin );
ps_accu.push_back( " 0 10 0 0 0 90 "
"OneAxx Axx Tic xTacL xNumL %% low x axis\n" );
ps_accu.push_back( " 0 10 0 10 0 270 "
"OneAxx Axx Tic xTacH %% top x axis\n" );
ps_accu.push_back( " xCL\n" );
ps_accu.push_back( "} def\n" );
ps_accu.push_back( "\n/yPlotFrame {" );
if ( Y.logflag && Y.inf<= 0 )
throw "BUG: y log incompatible with limits " + Y.str();
plotaux::calc_ticks( Y.logflag, Y.inf, Y.sup,
&ntack, &tack, &ntpt, ticklim );
ps_ticktack(ntack, tack, ntpt, ticklim, &Y);
free(tack);
snprintf( outlin, CPLOT_LINSIZ, " {(%s", Y.C.ps_str().c_str() );
strncat( outlin, ")}\n", CPLOT_LINSIZ );
ps_accu.push_back( outlin );
ps_accu.push_back( " 0 10 0 0 90 0 "
"OneAxx Axx Tic yTacL yNumL %% left y axis\n" );
ps_accu.push_back( " 0 10 10 0 90 180 "
"OneAxx Axx Tic yTacH % yNumH %% right yaxis\n" );
ps_accu.push_back( " yCL\n" );
ps_accu.push_back( "} def\n" );
ps_accu.push_back( "\n%% modeDD\nplotbefore\n" );
void CPlot::addScan( bool as_line, int style_no,
const vector<double>& xp,
const vector<double>& yp, const vector<double>& dyp,
const vector<double>& z,
string xco, string yco, string info
// Checks:
uint np=xp.size();
if ( np<0 )
throw string( "PROG ERR NPLot::Line x.size=0" );
if ( np!=yp.size() )
throw string( "PROG ERR NPLot::Line x.size<>y.size" );
string gp_fnam = str( format( "/tmp/%s-%d-%03d.gnu" )
% getenv("LOGNAME") % iPlot % gp_fno++ );
if (gp_fnames!="") gp_fnames += ", ";
gp_fnames += string("\"") + gp_fnam + "\" notitle";
if (!(gp_fd = fopen(gp_fnam.c_str(), "w")))
throw "cannot save gnuplot data to " + gp_fnam;
for (uint i=0; i<np; i++){
throw "Plot::Line: x["+strg(i)+"]="+strg(xp[i])+" out of range";
throw "Plot::Line: y["+strg(i)+"]="+strg(yp[i])+" out of range";
if( dyp.size() )
fprintf(gp_fd, "%16.8g %16.8g %16.8g\n", xp[i], yp[i], dyp[i] );
else
fprintf(gp_fd, "%16.8g %16.8g\n", xp[i], yp[i]);
if( !nout ){
cout << "no points in frame: " << info << "\n";
return;
}
snprintf( outlin, CPLOT_LINSIZ, "\n%3u [", ++ps_snum );
for (uint i=0; i<z.size(); i++){
snprintf( outlin, CPLOT_LINSIZ, " %12g", z[i]);
ps_accu.push_back( outlin );
}
snprintf( outlin, CPLOT_LINSIZ, " ] zValues\n" );
snprintf( outlin, CPLOT_LINSIZ, "%2d cstyle", 1 );
snprintf( outlin, CPLOT_LINSIZ, "%2d pstyle", style_no+1 );
snprintf( outlin, CPLOT_LINSIZ-2, " %% (%s -> %s)",
xco.c_str(), yco.c_str() );
strncat( outlin, "\n", CPLOT_LINSIZ );
ps_accu.push_back( outlin );
for (uint i=0; i<np; i++) {
snprintf( outlin, CPLOT_LINSIZ,
"%7.3f%7.3f%7.3f t%c %% %14.7g wx %14.7g wy\n",
X.pc(xp[i]), Y.pc(yp[i]),
dyp.size() ? Y.pc(dyp[i]) : 0,
i==0 ? 'i' : i==np-1 ? 'f' : ' ',
xp[i], yp[i] );
ps_accu.push_back( outlin );
}
//! Live display as prepared by addScan.
void CPlot::showScans()
{
if ( gp_fnames!="" )
gp_write( "plot " +
str( format( "[%12.8g:%12.8g] [%12.8g:%12.8g] " )
% X.inf % X.sup % Y.inf % Y.sup ) +
gp_fnames );
//! Add documentation lines to postscript output.
void CPlot::plotDoclines( const vector<string>& lDoc )
for (uint i=0; i<lDoc.size(); i++)
ps_Doc.push_back(lDoc[i]);
//! Write buffered plot to postscript file.
void CPlot::writePostscript( bool full_outfile )
string ps_outdir, ps_head, ps_dict;
// read configuration parameters:
ps_outdir = NRead::wofmac( "\\psdir" );
ps_head = NRead::wofmac( "\\pshead" );
// construct output file name:
FILE *pssav;
if (ps_fnum>=999)
throw string( "graph file number overflow" );
outf = ps_outdir + str( format( "l%d." ) % ++ps_fnum ) +
( full_outfile ? "ps" : "psa" );
if (!(pssav = mystd::glob_fopen(outf.c_str(), "", "", "r")))
break; // legal exit
fclose(pssav);
}
// copy headers to output file:
cmd = string("cat ") + ( full_outfile ? ps_dict : "" ) + " " +
ps_head + " > " + outf + "\n";
// a redundant check of existence of the outfile:
if (!(pssav = mystd::glob_fopen(outf.c_str(), "", "", "r", &flong))) {
throw "have not created file " + outf;
return;
}
fclose(pssav);
// append specific output to output file:
if (!(pssav = fopen(flong.c_str(), "a+"))) {
printf ("cannot write (append) to file %s\n", flong.c_str());
return;
}
for( uint i=0; i<ps_accu.size(); ++i ){

Wuttke, Joachim
committed
// fprintf does not work here because output line may contain "%"
fwrite( ps_accu[i].c_str(), 1, ps_accu[i].size(), pssav );
}
// additional output (do not append this to ps_accu to allow
// further incrementation of ps_accu):
fprintf(pssav, "\nblack 0 -4 13 newlist\n");
for (uint i=0; i<ps_Doc.size(); i++)
fprintf(pssav, "{(%s)} infline\n", ps_Doc[i].c_str());
fprintf(pssav,
"\n{(%s)} /filename exch def 10 -2.8 18 showfilename\n\n"
// output completed:
fclose(pssav);
//! Endless loop: query for gnuplot commands.
void CPlot::feedGnuplot()
string cmd;
cout << "entering mygnuplot - to leave, type q\n";
while(1) {
cmd = sask("mygnuplot> ");
if (cmd.substr(0,1)=="q") return;
gp_write(cmd);
}
//! Info line to characterize this plot window.
{
string ret;
ret = "x: " + X.info();
return ret;
}
//! Send one line to gnuplot fifo.
void CPlot::gp_write( string in )
if( write( gp_fifo, out.c_str(), out.size() ) <= 0 )
throw string( "could not write to gp_fifo" );
//! Format ticks and tacks for postscript file.
void CPlot::ps_ticktack(uint ntack, double *tack, int ntpt, double *ticklim,
uint i;
ps_accu.push_back( "[\n" );
if (A->logflag && ( tack[0]<1e-3 || tack[ntack-1]>1e3 )) {
snprintf( outlin, CPLOT_LINSIZ,
" %9.6f {(10)(%d)sp()} %%{(%g)}\n",
A->pc(tack[i]), (int)(log10(tack[i])),
(float) tack[i]);
ps_accu.push_back( outlin );
}
} else {
for (i=0; i<ntack; i++) {
snprintf( outlin, CPLOT_LINSIZ, " %9.6f {(%g)}\n",
ps_accu.push_back( outlin );
}
}
ps_accu.push_back( " ] SetTacVec\n" );
snprintf( outlin, CPLOT_LINSIZ, " %g %g %d %d SetTicVec%s\n",