Skip to content
Snippets Groups Projects
dualplot.cpp 13.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • //**************************************************************************************************
    //*  FRIDA: fast reliable interactive data analysis                         
    //*  (C) Joachim Wuttke 1990-, v2(C++) 2001-                                
    //*  http://apps.jcns.fz-juelich.de/frida                                   
    //**************************************************************************************************
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
    
    //! \file  dualplot.cpp
    
    //! \brief Collection NPlot of plot frames CPlot.
    
    #include <cstdio>
    #include <cmath>
    
    #include <fcntl.h>
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include <errno.h>
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include <iostream>
    
    #include <sys/stat.h>
    
    #include <boost/format.hpp>
    
    #include <../trivia/file_ops.hpp>
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "defs.hpp"
    #include "dualplot.hpp"
    
    using boost::format;
    
    static const int mLin = 80; // max num of chars in PostScript line
    
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    
    
    //! Constructor for plot window: setup for gnuplot and postscript.
    
    
    CPlot::CPlot( int _iPlot, bool _logx, bool _logy ) :
    
        iPlot( _iPlot ), X( "x", _logx ), Y( "y", _logy ),
    
        maxpoints(20000), with_errors(true), equipoints(49), refine(true)
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // == Initialization for Gnuplot ==
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // Create a named pipe (FIFO) that will transmit our commands to Gnuplot.
    
        string fn_fifo = string("/tmp/gnuplot-") + getenv( "LOGNAME" );
    
        triv::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";
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // Start a Gnuplot that will read from our pipe.
    
        triv::system( "gnuplot -title "+S(iPlot)+" -noraise < "+fn_fifo+"&" );
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // 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).
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        if ( ( gp_fifo = open( fn_fifo.c_str(), O_WRONLY ) )==-1 )
    
            throw "SYSTEM ERROR: cannot open fifo "+fn_fifo+" ("+strerror(errno)+")";
    
        fcntl( gp_fifo, F_SETFL, O_NONBLOCK );
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // Now the initialization _within_ Gnuplot.
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        gp_write( string("set terminal x11") );
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // == Initialization for PostScript ==
    
    //! 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( const string& cmd )
    
            X.setLog( !X.logflag );
            printf( "set x %s\n", X.logflag ? "log" : "lin" ); }
    
        else if ( cmd=="gxl+" )
            X.setLog( true );
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        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 );
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        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;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        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;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        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;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        else
            throw "unknown command " + cmd;
    
    //! Plot coordinate frame (axes, ticks, labels).
    
    
    void CPlot::plotFrame( const string& xlabel, const string& ylabel )
    
    Wuttke, Joachim's avatar
    c  
    Wuttke, Joachim committed
        gp_write( "set nologscale" );
        string whichlog="";
    
        if (X.logflag) whichlog += "x";
        if (Y.logflag) whichlog += "y";
    
    Wuttke, Joachim's avatar
    c  
    Wuttke, Joachim committed
        if (whichlog!="")
            gp_write( "set logscale " + whichlog );
    
        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 ntpt;
        double ticklim[2];
        vector<double> Tacks;
    
        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( Tacks, &ntpt, ticklim );
        ps_ticktack( Tacks, ntpt, ticklim, &X);
    
        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( Tacks, &ntpt, ticklim );
        ps_ticktack( Tacks, ntpt, ticklim, &Y);
    
        snprintf( outlin, mLin-4, "   {(%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, bool new_style, int style_no,
    
                         const vector<double>& xp,
                         const vector<double>& yp, const vector<double>& dyp,
                         const vector<double>& z,
    
                         const string& xco,
                         const string& yco,
                         const string& info
    
        static const int mColor = 6;
        static int color[mColor] = { 0x880000, 0x008800, 0x000088,
    
                                     0x006666, 0x660066, 0x666600 };
    
        int 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";
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        if ( as_line )
    
            gp_fnames += str( format( " with lines lt 1 lc rgb \"#%6x\"" )
    
                              % color[ style_no % mColor ] );
    
        else if( with_errors && dyp.size() )
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
            gp_fnames += " with errorbars";
    
        FILE *gp_fd;
    
        if (!(gp_fd = fopen(gp_fnam.c_str(), "w")))
            throw "cannot save gnuplot data to " + gp_fnam;
    
        int nout = 0;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        try {
            for (int i=0; i<np; i++){
                if( std::isinf(xp[i]) || std::isinf(yp[i]) )
                    throw "Data point number " + S(i) + " is invalid: x=" +
                        S(xp[i]) + ", y=" + S(yp[i]);
                if( xp[i]<X.inf || xp[i]>X.sup )
                    throw "CPlot::addSpec: x["+S(i)+"]="+S(xp[i])+" out of range";
                if( yp[i]<Y.inf || yp[i]>Y.sup )
                    throw "CPlot::addSpec: y["+S(i)+"]="+S(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++;
            }
        } catch ( string &s ) {
            fclose(gp_fd);
            throw s;
        } catch ( ... ) {
            fclose(gp_fd);
            throw "BUG: unexpected exception type";
    
        fclose(gp_fd);
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        if( !nout )
            throw "no points in frame: " + info;
    
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // Postscript copy:
    
        if ( new_style ) {
            snprintf( outlin, mLin, "\n%3u [", ++ps_snum );
            ps_accu.push_back( outlin );
    
            for (int 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", style_no );
    
                snprintf( outlin, mLin, "%2d pstyle", style_no );
    
            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 );
    
        } else {
            ps_accu.push_back( "\n" );
    
        for (int 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 );
        }
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    
    
    //! 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 line to postscript output.
    
    void CPlot::docTxLine( const string& line )
    
        ps_Doc.push_back( "  {("+line+")} TxLine" );
    }
    
    
    //! Add documentation line explaining a plot symbol to postscript output.
    
    
    void CPlot::docPtTxLine( const string& line, int num )
    
        ps_Doc.push_back( "  " + S(num) + " {("+line+")} PtTxLine" );
    
    }
    
    
    //! Add documentation line explaining a curve style to postscript output.
    
    
    void CPlot::docCvTxLine( const string& line, int num )
    
        ps_Doc.push_back( "  " + S(num) + " {("+line+")} CvTxLine" );
    
    //! Write buffered plot to postscript file.
    
    
    void CPlot::writePostscript( const string& ps_outdir,
                                 const string& ps_head,
                                 const string& ps_dict )
    
        // construct output file name:
        FILE *pssav;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
            if (ps_fnum>=999)
    
                throw "graph file number overflow";
    
            outf = triv::wordexp_unique( 
    
                ps_outdir + str( format( "l%d" ) % ++ps_fnum ) + "." + ( ps_dict=="" ? "psa" : "ps" ) );
    
            if( !triv::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";
    
        triv::system( cmd );
    
        
        // append specific output to output file:
    
        if ( !(pssav = fopen( outf.c_str(), "a+" )) )
            throw "cannot append contents to file " + outf;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        for( string lin: ps_accu ){
    
            // fprintf does not work here because output line may contain "%"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
            fwrite( lin.c_str(), 1, lin.size(), pssav );
    
        }
    
        // additional output (do not append this to ps_accu to allow
        // further incrementation of ps_accu):
    
        fprintf( pssav, "\n{ black 0 -4 13 1.65 NewList\n" );
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        for ( string lin: ps_Doc ) 
            fprintf( pssav, "%s\n", lin.c_str() );
    
        fprintf( pssav, "} oooinfo 1 eq { exec } { pop } ifelse\n" );
    
        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();
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        ret += "  y: " + Y.info();
    
    //! Send one line to gnuplot fifo.
    
    
    void CPlot::gp_write( const string& in )
    
        string out = in + "\n";
    
        // cout << "monitor gnuplot driver: '" << out << "'\n";
    
        if( write( gp_fifo, out.c_str(), out.size() ) <= 0 )
    
            throw "could not write to gp_fifo";
    
    //! Format ticks and tacks for postscript file.
    
    
    void CPlot::ps_ticktack( const vector<double>& Tacks, int ntpt,
                             const double *ticklim, const CAxis *A )
    
        int i, ntack;
    
        ntack = Tacks.size();
    
        if (ntack > 0 ) {
            ps_accu.push_back( "  [\n" );
            if (A->logflag && ( Tacks[0]<1e-3 || Tacks[ntack-1]>1e3 )) {
                for (i=0; i<ntack; i++) {
                    snprintf( outlin, mLin,
                              "   %9.6f {(10)(%d)sp()} %%{(%g)}\n", 
    
                              A->pc(Tacks[i]), (int)(log10(Tacks[i])), (float) Tacks[i]);
    
                    ps_accu.push_back( outlin );
                }
            } else {
                for (i=0; i<ntack; i++) {
                    snprintf( outlin, mLin, "   %9.6f {(%g)}\n", 
                              A->pc(Tacks[i]), (float) Tacks[i]);
                    ps_accu.push_back( outlin );
                }
    
            ps_accu.push_back( "  ] SetTacVec\n" );
    
        snprintf( outlin, mLin, "  %g %g %d %d SetTicVec%s\n", 
    
    Wuttke, Joachim's avatar
    ?  
    Wuttke, Joachim committed
                  A->pc(ticklim[0]), A->pc(ticklim[1]), ntack+2, ntpt,
    
                  (A->logflag? "Log" : "Lin"));
    
        ps_accu.push_back( outlin );
    
    
    
    namespace NPloWin {
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
        vector<CPlot*> Plots;
    
        int nPlot;
        int iPlot;
    
    
        void initialize();
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    //! Initialize default choice of plot windows.
    
    
    void NPloWin::initialize()
    {
        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