//**************************************************************************// //* FRIDA: flexible rapid interactive data analysis *// //* dualplot.cpp: different mechanisms for screen and paper output *// //* (C) Joachim Wuttke 1990-, v2(C++) 2001- *// //* http://www.messen-und-deuten.de/frida *// //**************************************************************************// #include <stdlib.h> #include <stdio.h> #include <string.h> #include <iostream> #include <math.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <boost/format.hpp> #include "mystd.h" #include "dualplot.h" using boost::format; //! Constructor for plot window: setup for gnuplot and postscript. CPlot::CPlot( uint _iPlot, bool _logx, bool _logy ) : iPlot( _iPlot ), X( _logx ), Y( _logy ), maxpoints(20000), with_errors(true), equipoints(7), refine(true) { // == Initialization for Gnuplot == // Create a named pipe (FIFO) that will transmit our commands to Gnuplot. string fn_fifo = string("/tmp/gnuplot-") + getenv( "LOGNAME" ); mystd::system( "rm -f "+fn_fifo ); if (mkfifo( fn_fifo.c_str(), 0666 )) throw "SYSTEM ERROR cannot make fifo " + fn_fifo + ": will not be able to print"; // Start a Gnuplot that will read from our pipe. mystd::system( "gnuplot -title " + strg(iPlot) + " -noraise" + " < " + fn_fifo + " &" ); // Open our pipe so that we can write commands to it // (we use 'open' instead of 'fopen' or 'ofstream', // because we need non-blocking mode). if ( ! ( gp_fifo = open( fn_fifo.c_str(), O_WRONLY ) ) ) throw "SYSTEM ERROR cannot open fifo " + fn_fifo + ": will not be able to print"; fcntl( gp_fifo, F_SETFL, O_NONBLOCK ); // Now the initialization _within_ Gnuplot. gp_write( string("set terminal x11") ); // == Initialization for PostScript == ps_fnum=0; } //! Clear plot frame and buffer. void CPlot::clearFrame() { // clear gnuplot tmp files gp_fno = 0; gp_fnames = ""; // 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=="gxl" ) { X.setLog( !X.logflag ); printf( "set x %s\n", X.logflag ? "log" : "lin" ); } else if ( cmd=="gxl+" ) X.setLog( true ); else if ( cmd=="gxl-" ) 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 ); else if ( cmd=="gyl-" ) 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; else if ( cmd=="gxf-" ) 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; else if ( cmd=="gyf-" ) Y.force = false; else if ( cmd=="ge" ) with_errors = !with_errors; else if ( cmd=="ge+" ) with_errors = true; else if ( cmd=="ge-" ) with_errors = false; else throw "unknown command " + cmd; } //! Plot coordinate frame (axes, ticks, labels). void CPlot::plotFrame( string xlabel, string ylabel ) { gp_write( "set nologscale" ); string whichlog=""; if (X.logflag) whichlog += "x"; if (Y.logflag) whichlog += "y"; if (whichlog!="") gp_write( "set logscale " + whichlog ); // wups: snprintf( outlin, mLin, "\n%d %g %g xSetCoord\n", X.logflag, X.inf, X.sup ); ps_accu.push_back( outlin ); snprintf( outlin, mLin, "%d %g %g ySetCoord\n", Y.logflag, Y.inf, Y.sup ); ps_accu.push_back( outlin ); snprintf( outlin, mLin, "%% %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 {\n" ); if ( X.logflag && X.inf<= 0 ) throw "BUG: x log incompatible with limits " + X.str(); X.calc_ticks( &ntack, &tack, &ntpt, ticklim ); ps_ticktack(ntack, tack, ntpt, ticklim, &X); free(tack); snprintf( outlin, mLin-4, " {(%s", xlabel.c_str() ); strncat( outlin, ")}\n", mLin ); ps_accu.push_back( outlin ); ps_accu.push_back( " 0 10 0 0 0 90 " "OneAxx Axx Tic Tac xNumL %% low x axis\n" ); ps_accu.push_back( " 0 10 0 10 0 270 " "OneAxx Axx Tic Tac %% top x axis\n" ); ps_accu.push_back( " xCL\n" ); ps_accu.push_back( "} def\n" ); ps_accu.push_back( "\n/yPlotFrame {\n" ); if ( Y.logflag && Y.inf<= 0 ) throw "BUG: y log incompatible with limits " + Y.str(); Y.calc_ticks( &ntack, &tack, &ntpt, ticklim ); ps_ticktack(ntack, tack, ntpt, ticklim, &Y); free(tack); snprintf( outlin, mLin, " {(%s", ylabel.c_str() ); strncat( outlin, ")}\n", mLin ); ps_accu.push_back( outlin ); ps_accu.push_back( " 0 10 0 0 90 0 " "OneAxx Axx Tic Tac yNumL %% left y axis\n" ); ps_accu.push_back( " 0 10 10 0 90 180 " "OneAxx Axx Tic Tac %% right y axis\n" ); ps_accu.push_back( " yCL\n" ); ps_accu.push_back( "} def\n" ); ps_accu.push_back( "\n%% modeDD\nplotbefore\n" ); } //! Plot one spectrum. void CPlot::addSpec( 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 ) { static const int mColor = 6; static int color[mColor] = { 0x880000, 0x008800, 0x000088, 0x006666, 0x660066, 0x666600 }; // Checks: uint np=xp.size(); if ( !np ) throw "invalid call to CPLot::addSpec: no data points"; if ( np!=yp.size() ) throw "invalid call to CPLot::addSpec: x.size<>y.size"; // Prepare for live display, to be shown by showSpecs(): 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 ( as_line ) gp_fnames += str( format( " with lines lt 1 lc rgb \"#%6x\"" ) % color[ style_no % mColor ] ); else if( with_errors && dyp.size() ) gp_fnames += " with errorbars"; FILE *gp_fd; if (!(gp_fd = fopen(gp_fnam.c_str(), "w"))) throw "cannot save gnuplot data to " + gp_fnam; uint nout = 0; for (uint i=0; i<np; i++){ if( isinf(xp[i]) || isinf(yp[i]) ) throw "Data point number " + strg(i) + " is invalid: x=" + strg(xp[i]) + ", y=" + strg(yp[i]); if( xp[i]<X.inf || xp[i]>X.sup ) throw "CPlot::addSpec: x["+strg(i)+"]="+strg(xp[i])+" out of range"; if( yp[i]<Y.inf || yp[i]>Y.sup ) throw "CPlot::addSpec: y["+strg(i)+"]="+strg(yp[i])+" out of range"; if( with_errors && 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]); nout++; } fclose(gp_fd); if( !nout ) throw "no points in frame: " + info; // Postscript copy: snprintf( outlin, mLin, "\n%3u [", ++ps_snum ); ps_accu.push_back( outlin ); for (uint i=0; i<z.size(); i++){ snprintf( outlin, mLin, " %12g", z[i]); ps_accu.push_back( outlin ); } snprintf( outlin, mLin, " ] zValues\n" ); ps_accu.push_back( outlin ); if ( as_line ) snprintf( outlin, mLin, "%2d cstyle", 1 ); else snprintf( outlin, mLin, "%2d pstyle", style_no+1 ); ps_accu.push_back( outlin ); snprintf( outlin, mLin-2, " %% (%s -> %s)", xco.c_str(), yco.c_str() ); strncat( outlin, "\n", mLin ); ps_accu.push_back( outlin ); for (uint i=0; i<np; i++) { snprintf( outlin, mLin, "%6.3f %6.3f %6.3f t%c %% %13.7g wx %13.7g wy\n", X.pc(xp[i]), Y.pc(yp[i]), dyp.size() ? Y.pcerr(yp[i],dyp[i]) : 0, i==0 ? 'i' : i==np-1 ? 'f' : ' ', xp[i], yp[i] ); ps_accu.push_back( outlin ); } } //! Live display as prepared by addSpec. void CPlot::showSpecs() { 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( string ps_outdir, string ps_head, string ps_dict ) { // construct output file name: FILE *pssav; string cmd, outf; while(1) { if (ps_fnum>=999) throw "graph file number overflow"; outf = mystd::wordexp_unique( ps_outdir + str( format( "l%d" ) % ++ps_fnum ) + "." + ( ps_dict=="" ? "psa" : "ps" ) ); if( !mystd::file_exists( outf.c_str() ) ) break; // legal exit } cout << "save plot in " << outf << "\n"; // copy headers to output file: cmd = string("cat ") + ps_dict + " " + // ps_dict may be "" ps_head + " > " + outf + "\n"; mystd::system( cmd ); // append specific output to output file: if ( !(pssav = fopen( outf.c_str(), "a+" )) ) throw "cannot append contents to file " + outf; for( uint i=0; i<ps_accu.size(); ++i ){ // 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 1.65 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" " EndFrame\n", outf.c_str() ); // output completed: fclose(pssav); } //! Info line to characterize this plot window. string CPlot::info() const { string ret; ret = "x: " + X.info(); ret += " y: " + Y.info(); return ret; } //! Send one line to gnuplot fifo. void CPlot::gp_write( string in ) { string out = in + "\n"; // cout << "monitor gnuplot driver: '" << out << "'\n"; 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, CAxis *A ) { uint i; ps_accu.push_back( " [\n" ); if (A->logflag && ( tack[0]<1e-3 || tack[ntack-1]>1e3 )) { for (i=0; i<ntack; i++) { snprintf( outlin, mLin, " %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, mLin, " %9.6f {(%g)}\n", A->pc(tack[i]), (float) tack[i]); ps_accu.push_back( outlin ); } } ps_accu.push_back( " ] SetTacVec\n" ); snprintf( outlin, mLin, " %g %g %d %d SetTicVec%s\n", A->pc(ticklim[0]), A->pc(ticklim[1]), ntack+2, ntpt, (A->logflag? "Log" : "Lin")); ps_accu.push_back( outlin ); } namespace NPloWin { vector<CPlot*> Plots; uint nPlot, iPlot; void initialize(); }; //! Initialize plot windows. void NPloWin::initialize() { using namespace NPloWin; nPlot = 0; // shorthand for Plots.size() Plots.push_back( new class CPlot( nPlot++, false, false ) ); Plots.push_back( new class CPlot( nPlot++, true, false ) ); Plots.push_back( new class CPlot( nPlot++, false, true ) ); Plots.push_back( new class CPlot( nPlot++, true, true ) ); iPlot = 0; // current plot window }