//***************************************************************************// //* FRIDA: fast reliable interactive data analysis *// //* (C) Joachim Wuttke 1990-, v2(C++) 2001- *// //* http://apps.jcns.fz-juelich.de/frida *// //***************************************************************************// //! \file plot.cpp //! \brief NPlot: plot data and curves #include <vector> #include <string> #include <memory> #include <math.h> #include "defs.hpp" #include "list.hpp" #include "olf.hpp" #include "mem.hpp" #include "jsel.hpp" #include "zentry.hpp" #include "dualplot.hpp" #include "plot.hpp" #include "curve.hpp" //**************************************************************************// //* Subroutines: determine data ranges *// //**************************************************************************// //! Loop over selected data files to determine x range. void determine_Xrange( CPlot* plot, CList* JSel ) { double inf, sup; NOlm::IterateD fiter; const vector<double> *vx; bool first = true; POld fd; while((fd=fiter())) { JSel->evaluate( 0, fd->nJ()-1 ); for ( int iv=0; iv<JSel->size(); ++iv ) { vx = &( fd->VS( JSel->V[iv] )->x ); for ( int i=0; i<vx->size(); ++i ){ if ( plot->X.logflag && (*vx)[i]<=0 ) continue; if ( first ){ inf = (*vx)[i]; sup = (*vx)[i]; first = false; } else { if ( (*vx)[i]<inf ) inf = (*vx)[i]; if ( (*vx)[i]>sup ) sup = (*vx)[i]; } } } } if ( first ) throw "x range is empty"; plot->X.setRoundedLimits( "x", inf, sup ); } //! Loop over selected files to determine y range for current x range. void determine_Yrange( CPlot* plot, CList* JSel ) { double inf=INFINITY, sup=-INFINITY; NOlm::IterateO iter; POlo f; while( (f=iter()) ){ int k = iter.k(); POlc fc = P2C( f ); if ( fc && fc->kconv!=-1 ) continue; POld fd = P2D( f ); JSel->evaluate( 0, f->nJ()-1 ); for ( int iv=0; iv<JSel->size(); ++iv ){ int j = JSel->V[iv]; PSpec s; if ( fd ){ s = fd->VS(j); } else { s = PSpec( new CSpec ); plot->X.set_xgrid( s->x, 97 ); fc->curve_val_vec( &(s->y), s->x, k, j ); } for ( int i=0; i<s->size(); i++ ) { if ( !plot->X.contains( s->x[i] ) ) continue; if ( plot->Y.logflag && s->y[i]<=0 ) continue; if (s->y[i]<inf) inf=s->y[i]; if (s->y[i]>sup) sup=s->y[i]; } } } if ( inf==INFINITY || sup==-INFINITY ) throw "y range is empty"; plot->Y.setRoundedLimits( "y", inf, sup ); } //**************************************************************************// //* Subroutines: plot one scan *// //**************************************************************************// //! Plot scan j of data file fd; return number of points plotted. int plot_data( CPlot* plot, POld fd, int k, int j, int pstyle ) { const PSpec s = fd->VS(j); int n=s->x.size(); if ( n!=s->y.size() ) throw "BUG: plot: x.size<>y.size"; // Filter: x,y -> xp,yp if inside frame int np=0, nxl=0, nxh=0, nyl=0, nyh=0; vector<double> xp, yp, dyp; for ( int i=0; i<n; i++ ) { double x = s->x[i]; if ( !plot->X.contains(x) ) { if ( x<=plot->X.inf ){ x = plot->X.inf; nxl++; } if ( x>=plot->X.sup ){ x = plot->X.sup; nxh++; } if ( !plot->X.force ) continue; } double y = s->y[i]; if ( !plot->Y.contains(y) ) { if ( y<=plot->Y.inf ){ y = plot->Y.inf; nyl++; } if ( y>=plot->Y.sup ){ y = plot->Y.sup; nyh++; } if ( !plot->Y.force ) continue; } if ( np==plot->maxpoints && np<n ) { cout << "reached maxpoints at " << s->x[i] << "\n"; } xp.push_back( x ); yp.push_back( y ); if ( s->dy.size() ) dyp.push_back( s->dy[i] ); np = xp.size(); if ( np > plot->maxpoints ) // continue in large steps i += plot->maxpoints; } if ( np==0 ) { cout << "file " << k << " spec " << j << " all " << n << " points outside plot range:\n"; if ( nxl ) cout << " " << nxl << " points < xmin\n"; if ( nxh ) cout << " " << nxh << " points > xmax\n"; if ( nyl ) cout << " " << nyl << " points < ymin\n"; if ( nyh ) cout << " " << nyh << " points > ymax\n"; } else { plot->addSpec( false, true, pstyle, xp, yp, dyp, fd->V[j]->z, fd->xco.str_std(), fd->yco.str_std(), "data file " + S(k) + " spectrum "+S(j) ); } return np; } //! Plot scan j of convolved curve file fc; return number of points plotted. int plot_curve_convolved( CPlot* plot, POlc fc, int k, int j, int cstyle ) { vector<double> novec; int kconv, jconv; PSpec sconv; NCurveFile::getConv( &sconv, &kconv, &jconv, k, j ); vector<double> xp, yp; for ( int i=0; i<sconv->size(); ++i ){ double x = sconv->x[i]; if ( plot->X.contains( x ) ) xp.push_back( x ); } fc->curve_val_vec( &yp, xp, k, j ); vector<double> xo, yo; for ( int i=0; i<xp.size(); ++i ){ if ( !( plot->X.contains( xp[i] ) && plot->Y.contains( yp[i] ) ) ) continue; xo.push_back( xp[i] ); yo.push_back( yp[i] ); } if ( xo.size()==0 ){ cout << "curve k="<<S(k)<<", j="<<S(j)<< " has no points in plot window\n"; } plot->addSpec( true, true, cstyle, xo, yo, novec, fc->V[j]->z, fc->xco.str_std(), fc->yco.str_std(), "curve file "+S(k)+" spectrum "+S(j) ); return xo.size(); } //! Plot scan j of curve file fc, using grid from file kd. int plot_curve_to_grid( CPlot* plot, POlc fc, int k, int j, int cstyle ) { if( fc->kd==-1 ) throw "data reference not set, incompatible with cg+"; POld fd = NOlm::mem_get_D(fc->kd); if( !fd ) throw "data reference not found"; if( j>=fd->nJ() ) throw "number of spectra does not match"; vector<double> *xc = &(fd->VS(j)->x); vector<double> novec, yc, xp, yp; fc->curve_val_vec( &yc, *xc, k, j ); for ( int i=0; i<xc->size(); ++i ) { if ( plot->X.contains((*xc)[i]) && plot->Y.contains(yc[i]) ) { xp.push_back( (*xc)[i] ); yp.push_back( yc[i] ); } } plot->addSpec( true, true, cstyle, xp, yp, novec, fc->V[j]->z, fc->xco.str_std(), fc->yco.str_std(), "curve file "+S(k)+" spectrum "+S(j) ); return xp.size(); } //! Plot scan j of non-convolved curve file fc on equidistant grid; //! return number of points plotted. int plot_curve_equidist( CPlot* plot, POlc fc, int k, int j, int cstyle ) { vector<double> novec; // equidistant grid int npts = plot->equipoints; vector<double> xc, yc, xp, yp; plot->X.set_xgrid( xc, npts ); fc->curve_val_vec( &yc, xc, k, j ); for ( int i=0; i<npts; ++i ) { if ( plot->X.contains(xc[i]) && plot->Y.contains(yc[i]) ) { xp.push_back( xc[i] ); yp.push_back( yc[i] ); } } plot->addSpec( true, true, cstyle, xp, yp, novec, fc->V[j]->z, fc->xco.str_std(), fc->yco.str_std(), "curve file "+S(k)+" spectrum "+S(j) ); return xp.size(); } //! Plot scan j of non-convolved curve file fc, refining the grid where //! necessary; return number of points plotted. int plot_curve_refine( CPlot* plot, POlc fc, int k, int j, int cstyle ) { // refinement cares to: // - stop/start line when leaving/reentering/crossing(TODO) the frame // - TODO interpolate when crossing the horizontal border // - interpolate at discontinuities vector<double> xc, yc, xn, yn; vector<double> novec; // start with equidistant grid: int npts = plot->equipoints; plot->X.set_xgrid( xc, npts ); // refinement loop: for ( int iref=0; ; ++iref ) { if ( !xc.size() ){ if ( !iref ) throw "BUG: plot_curve_refine called with xc.size()=0"; else break; // regular exit } // cout << "DEB refinement #" <<iref<< ", xc.size=" <<xc.size()<< "\n"; // evaluate curve for grid xc: fc->curve_val_vec( &yc, xc, k, j ); // for ( int i=0; i<xc.size(); ++i ) // printf ("DEB xc yc %12.6g %12.6g\n", xc[i], yc[i] ); // merge xc,yc and xn,yn: if ( iref==0 ) { xn = xc; yn = yc; } else { vector<double> xa, ya; int ic=0, in=0; for ( ; ic<xc.size() && in<xn.size(); ) { if ( plot->X.close_enough( xc[ic], xn[ic], 1e-7 ) ){ ++in; // skip new point } else if ( xc[ic]<xn[in] ) { xa.push_back( xc[ic] ); ya.push_back( yc[ic] ); ++ic; } else { xa.push_back( xn[in] ); ya.push_back( yn[in] ); ++in; } } for ( ; ic<xc.size(); ++ic ) { xa.push_back( xc[ic] ); ya.push_back( yc[ic] ); } for ( ; in<xn.size(); ++in ) { xa.push_back( xn[in] ); ya.push_back( yn[in] ); } xn = xa; yn = ya; } // cout << "DEB merger -> " << xn.size() << "\n"; // for ( int i=0; i<xn.size(); ++i ) // printf ("DEB xn yn %12.6g %12.6g\n", xn[i], yn[i] ); // end of loop if ( iref>=10 || xn.size()>=plot->maxpoints ) break; // alternate exit: too many refinements or too many points // eliminate points outside y range (except border points): vector<double> xa, ya; xa.push_back( xn[0] ); ya.push_back( yn[0] ); for ( int i=1; i<xn.size()-1; ++i) { new_segment: xa.push_back( xn[i] ); ya.push_back( yn[i] ); if ( !plot->Y.contains(yn[i]) ) { for ( int ii=i+1; ii<xn.size()-1; ++ii ) { if ( plot->Y.contains(yn[ii]) ) { if ( ii-1>i ) { xa.push_back( xn[ii-1] ); ya.push_back( yn[ii-1] ); } i = ii; goto new_segment; } } } } xa.push_back( xn.back() ); ya.push_back( yn.back() ); xn = xa; yn = ya; // cout << "DEB within frame -> " << xn.size() << "\n"; // for ( int i=0; i<xn.size(); ++i ) // printf ("DEB xy %12.6g %12.6g\n", xn[i], yn[i] ); // set additional base points: bool insert_next = false; xc.clear(); for ( int i=1; i<xn.size()-1; ++i ) { if ( i<xn.size()-2 ) { double yi = ( (xn[i+1]-xn[i])*yn[i-1] + (xn[i]-xn[i-1])*yn[i+1] ) / (xn[i+1]-xn[i-1]); if ( !plot->Y.close_enough( yn[i], yi, 0.005 ) ) { xc.push_back( (xn[i-1] + xn[i])/2 ); // cout << "DEB disc " << xc.back() << "\n"; insert_next = true; continue; } } if ( insert_next ) { xc.push_back( (xn[i-1] + xn[i])/2 ); // cout << "DEB next " << xc.back() << "\n"; insert_next = false; continue; } if ( ( plot->Y.contains(yn[i]) && !plot->Y.contains(yn[i-1]) ) || ( !plot->Y.contains(yn[i]) && plot->Y.contains(yn[i-1]) ) ) { // cout << "DEB edge " << xc.back() << "\n"; xc.push_back( (xn[i-1] + xn[i])/2 ); continue; } } // temporary check if( xc.size() ) for ( int i=0; i<xc.size()-1; ++i ) if ( xc[i+1]<=xc[i] ) throw "BUG: new base points not sorted at " + S(i) + ": " + S(xc[i]) + " " + S(xc[i+1]); } // divide into segments and plot: vector<double> xa, ya; bool first_seg = true; npts = 0; for ( int i=0; i<xn.size(); ++i ) { if ( plot->Y.contains(yn[i]) ) { xa.push_back( xn[i] ); ya.push_back( yn[i] ); } if ( !plot->Y.contains(yn[i]) || i==xn.size()-1 ) { npts += xa.size(); if ( xa.size() ){ // cout << "DEB segment -> " << xa.size() << "\n"; plot->addSpec( true, first_seg, cstyle, xa, ya, novec, fc->V[j]->z, fc->xco.str_std(), fc->yco.str_std(), "curve file "+S(k)+ " spectrum "+S(j) ); xa.clear(); ya.clear(); first_seg = false; } } } return npts; } //**************************************************************************// //* Plot routine is accessible from the main menu *// //**************************************************************************// //! Plot set of spectra, or add to open plot. void NPlot::Plot( class CPlot *plot, bool add, const string& mode ) { NOlm::IterateO fiter; static CList JSel; static int pstyle=1, cstyle=1; static string jSel = ""; if ( mode=="ask" ) { JSelAsk( "Plot which spectra", &jSel, &JSel ); } else if ( mode == "next" ) { JSel.increment( +1, 0, NOlm::nJ_max()-1 ); cout << "plotting spectra " << JSel << "\n"; jSel = JSel.str(); } else if ( mode == "prev" ) { JSel.increment( -1, 0, NOlm::nJ_max()-1 ); cout << "plotting spectra " << JSel << "\n"; jSel = JSel.str(); } if ( !add ) { // prepare frame (ranges, labels) pstyle = 1; cstyle = 1; if (!plot->X.finite()) determine_Xrange( plot, &JSel ); if (!plot->Y.finite()) determine_Yrange( plot, &JSel ); // build label: (zur Zeit nur vom ersten File; definiere +=) CCoord xCo = NOlm::sel_first()->xco; CCoord yCo = NOlm::sel_first()->yco; // draw new frame: plot->clearFrame(); plot->plotFrame( xCo.str_ps(), yCo.str_ps() ); } // plot: while( POlo f = fiter() ){ int k = fiter.k(); POld fd = P2D( f ); POlc fc = P2C( f ); plot->docTxLine( f->name ); for (int i=0; i<f->lDoc.size(); i++) plot->docTxLine( " " + f->lDoc[i] ); if ( fc ) { vector<string> vs = fc->infoFile(); for (int i=0; i<vs.size(); i++) plot->docTxLine( " " + vs[i] ); plot->docTxLine( " " + fc->infoScanHeader() ); } JSel.evaluate( 0, f->nJ()-1 ); for ( int iv=0; iv<JSel.size(); ++iv){ int j = JSel.V[iv]; if ( fd ) { if ( !plot_data( plot, fd, k, j, pstyle ) ) continue; } else if ( fc ) { if ( fc->kconv!=-1 ) { if ( !plot_curve_convolved( plot, fc, k, j, cstyle ) ) continue; } else if ( fc->plot_to_grid ) { if ( !plot_curve_to_grid( plot, fc, k, j, cstyle ) ) continue; } else if ( !plot->refine ) { if ( !plot_curve_equidist( plot, fc, k, j, cstyle ) ) continue; } else { if ( !plot_curve_refine( plot, fc, k, j, cstyle ) ) continue; } } else { throw "PROGRAM ERROR plot: unexpected file type"; } if ( fd ) { plot->docPtTxLine( " " + f->infoLine( j ), pstyle ); ++pstyle; } else if ( fc ) { plot->docCvTxLine( " " + f->infoLine( j ), cstyle ); ++cstyle; } } } plot->showSpecs(); }