Select Git revision
Forked from
mlz / Frida
1423 commits behind the upstream repository.

Wuttke, Joachim authored
plot.cpp 16.79 KiB
//***************************************************************************//
//* 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();
}