2
* spectrumPlot.cpp - Compute histograms of values for valued 3D point data
3
* Copyright (C) 2013, D Haley
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "spectrumPlot.h"
22
#include "filterCommon.h"
28
SPECTRUM_BAD_BINCOUNT,
34
KEY_SPECTRUM_BINWIDTH,
35
KEY_SPECTRUM_AUTOEXTREMA,
38
KEY_SPECTRUM_LOGARITHMIC,
39
KEY_SPECTRUM_PLOTTYPE,
43
//Limit user to one :million: bins
44
const unsigned int SPECTRUM_MAX_BINS=1000000;
46
const unsigned int SPECTRUM_AUTO_MAX_BINS=25000;
48
SpectrumPlotFilter::SpectrumPlotFilter()
57
//Default to blue plot
62
Filter *SpectrumPlotFilter::cloneUncached() const
64
SpectrumPlotFilter *p = new SpectrumPlotFilter();
69
p->autoExtrema=autoExtrema;
74
p->plotStyle=plotStyle;
75
p->logarithmic = logarithmic;
78
//We are copying wether to cache or not,
79
//not the cache itself
82
p->userString=userString;
86
//!Get approx number of bytes for caching output
87
size_t SpectrumPlotFilter::numBytesForCache(size_t nObjects) const
89
//Check that we have good plot limits, and bin width. if not, we cannot estimate cache size
90
if(minPlot ==std::numeric_limits<float>::max() ||
91
maxPlot==-std::numeric_limits<float>::max() ||
92
binWidth < sqrt(std::numeric_limits<float>::epsilon()))
97
return (size_t)((float)(maxPlot- minPlot)/(binWidth))*2*sizeof(float);
100
unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
101
std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
106
//Only report the spectrum plot
107
for(unsigned int ui=0;ui<filterOutputs.size(); ui++)
108
getOut.push_back(filterOutputs[ui]);
114
size_t totalSize=numElements(dataIn,STREAM_TYPE_IONS);
116
unsigned int nBins=2;
120
//Determine min and max of input
125
progress.stepName=TRANS("Extrema");
128
minPlot = std::numeric_limits<float>::max();
129
maxPlot =-std::numeric_limits<float>::max();
130
//Loop through each type of data
132
unsigned int curProg=NUM_CALLBACK;
133
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
135
//Only process stream_type_ions. Do not propagate anything,
136
//except for the spectrum
137
if(dataIn[ui]->getStreamType() == STREAM_TYPE_IONS)
140
ions = (IonStreamData *)dataIn[ui];
141
for(unsigned int uj=0;uj<ions->data.size(); uj++)
143
minPlot = std::min(minPlot,
144
ions->data[uj].getMassToCharge());
145
maxPlot = std::max(maxPlot,
146
ions->data[uj].getMassToCharge());
152
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
153
curProg=NUM_CALLBACK;
154
if(!(*callback)(false))
155
return SPECTRUM_ABORT_FAIL;
163
//Check that the plot values have been set (ie not same as initial values)
164
if(minPlot !=std::numeric_limits<float>::max() &&
165
maxPlot!=-std::numeric_limits<float>::max() )
167
//push them out a bit to make the edges visible
172
//Time to move to phase 2
174
progress.stepName=TRANS("count");
179
double delta = ((double)maxPlot - (double)(minPlot))/(double)binWidth;
182
//Check that we have good plot limits.
183
if(minPlot ==std::numeric_limits<float>::max() ||
184
minPlot ==-std::numeric_limits<float>::max() ||
185
fabs(delta) > std::numeric_limits<float>::max() || // Check for out-of-range
186
binWidth < sqrt(std::numeric_limits<float>::epsilon()) )
188
//If not, then simply set it to "1".
189
minPlot=0; maxPlot=1.0; binWidth=0.1;
194
//Estimate number of bins in floating point, and check for potential overflow .
195
float tmpNBins = (float)((maxPlot-minPlot)/binWidth);
197
//If using autoextrema, use a lower limit for max bins,
198
//as we may just hit a nasty data point
200
tmpNBins = std::min(SPECTRUM_AUTO_MAX_BINS,(unsigned int)tmpNBins);
202
tmpNBins = std::min(SPECTRUM_MAX_BINS,(unsigned int)tmpNBins);
204
nBins = (unsigned int)tmpNBins;
209
binWidth = (maxPlot-minPlot)/nBins;
215
d=new PlotStreamData;
218
d->xyData.resize(nBins);
220
catch(std::bad_alloc)
224
return SPECTRUM_BAD_ALLOC;
233
d->logarithmic=logarithmic;
234
d->plotStyle = plotStyle;
235
d->plotMode=PLOT_MODE_1D;
239
d->dataLabel = getUserString();
240
d->yLabel= TRANS("Count");
242
//Check all the incoming ion data's type name
243
//and if it is all the same, use it for the plot X-axis
244
std::string valueName;
245
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
247
switch(dataIn[ui]->getStreamType())
249
case STREAM_TYPE_IONS:
251
const IonStreamData *ionD;
252
ionD=(const IonStreamData *)dataIn[ui];
253
if(!valueName.size())
254
valueName=ionD->valueType;
257
if(ionD->valueType != valueName)
259
valueName=TRANS("Mixed data");
270
//Look for any ranges in input stream, and add them to the plot
271
//while we are doing that, count the number of ions too
272
for(unsigned int ui=0;ui<dataIn.size();ui++)
274
switch(dataIn[ui]->getStreamType())
276
case STREAM_TYPE_RANGE:
278
const RangeStreamData *rangeD;
279
rangeD=(const RangeStreamData *)dataIn[ui];
280
for(unsigned int uj=0;uj<rangeD->rangeFile->getNumRanges();uj++)
283
ionId=rangeD->rangeFile->getIonID(uj);
284
//Only append the region if both the range
285
//and the ion are enabled
286
if((rangeD->enabledRanges)[uj] &&
287
(rangeD->enabledIons)[ionId])
289
//save the range as a "region"
290
d->regions.push_back(rangeD->rangeFile->getRange(uj));
291
d->regionID.push_back(uj);
293
//FIXME: Const correctness
294
d->regionParent=(Filter*)rangeD->parent;
297
//Use the ionID to set the range colouring
298
colour=rangeD->rangeFile->getColour(ionId);
300
//push back the range colour
301
d->regionR.push_back(colour.red);
302
d->regionG.push_back(colour.green);
303
d->regionB.push_back(colour.blue);
313
#pragma omp parallel for
314
for(unsigned int ui=0;ui<nBins;ui++)
316
d->xyData[ui].first = minPlot + ui*binWidth;
317
d->xyData[ui].second=0;
319
//Compute the plot bounds
320
d->autoSetHardBounds();
321
//Limit them to 1.0 or greater (due to log)
322
d->hardMinY=std::min(1.0f,d->hardMaxY);
325
//Number of ions currently processed
327
unsigned int curProg=NUM_CALLBACK;
328
//Loop through each type of data
329
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
331
switch(dataIn[ui]->getStreamType())
333
case STREAM_TYPE_IONS:
335
const IonStreamData *ions;
336
ions = (const IonStreamData *)dataIn[ui];
340
//Sum the data bins as needed
341
for(unsigned int uj=0;uj<ions->data.size(); uj++)
344
bin = (unsigned int)((ions->data[uj].getMassToCharge()-minPlot)/binWidth);
345
//Dependant upon the bounds,
346
//actual data could be anywhere.
347
if( bin < d->xyData.size())
348
d->xyData[bin].second++;
350
//update progress every CALLBACK ions
354
progress.filterProgress= (unsigned int)(((float)(n)/((float)totalSize))*100.0f);
355
curProg=NUM_CALLBACK;
356
if(!(*callback)(false))
359
return SPECTRUM_ABORT_FAIL;
367
//Don't propagate any type.
375
d->cached=1; //IMPORTANT: cached must be set PRIOR to push back
376
filterOutputs.push_back(d);
387
void SpectrumPlotFilter::getProperties(FilterPropGroup &propertyList) const
394
stream_cast(str,binWidth);
395
p.name=TRANS("Bin width");
397
p.key=KEY_SPECTRUM_BINWIDTH;
398
p.type=PROPERTY_TYPE_REAL;
399
p.helpText=TRANS("Step size for spectrum");
400
propertyList.addProperty(p,curGroup);
408
p.name=TRANS("Auto Min/max");
410
p.key=KEY_SPECTRUM_AUTOEXTREMA;
411
p.type=PROPERTY_TYPE_BOOL;
412
p.helpText=TRANS("Automatically compute spectrum upper and lower bound");
413
propertyList.addProperty(p,curGroup);
415
stream_cast(str,minPlot);
418
p.key=KEY_SPECTRUM_MIN;
419
p.type=PROPERTY_TYPE_REAL;
420
p.helpText=TRANS("Starting position for spectrum");
421
propertyList.addProperty(p,curGroup);
423
stream_cast(str,maxPlot);
424
p.key=KEY_SPECTRUM_MAX;
427
p.type=PROPERTY_TYPE_REAL;
428
p.helpText=TRANS("Ending position for spectrum");
429
propertyList.addProperty(p,curGroup);
435
p.key=KEY_SPECTRUM_LOGARITHMIC;
436
p.name=TRANS("Logarithmic");
438
p.type=PROPERTY_TYPE_BOOL;
439
p.helpText=TRANS("Convert the plot to logarithmic mode");
440
propertyList.addProperty(p,curGroup);
442
//Let the user know what the valid values for plot type are
443
vector<pair<unsigned int,string> > choices;
447
tmpStr=plotString(PLOT_TRACE_LINES);
448
choices.push_back(make_pair((unsigned int) PLOT_TRACE_LINES,tmpStr));
449
tmpStr=plotString(PLOT_TRACE_BARS);
450
choices.push_back(make_pair((unsigned int)PLOT_TRACE_BARS,tmpStr));
451
tmpStr=plotString(PLOT_TRACE_STEPS);
452
choices.push_back(make_pair((unsigned int)PLOT_TRACE_STEPS,tmpStr));
453
tmpStr=plotString(PLOT_TRACE_STEM);
454
choices.push_back(make_pair((unsigned int)PLOT_TRACE_STEM,tmpStr));
457
tmpStr= choiceString(choices,plotStyle);
458
p.name=TRANS("Plot Type");
460
p.type=PROPERTY_TYPE_CHOICE;
461
p.helpText=TRANS("Visual style of plot");
462
p.key=KEY_SPECTRUM_PLOTTYPE;
463
propertyList.addProperty(p,curGroup);
467
//Convert the colour to a hex string
468
genColString((unsigned char)(r*255.0),(unsigned char)(g*255.0),
469
(unsigned char)(b*255.0),(unsigned char)(a*255.0),thisCol);
471
p.name=TRANS("Colour");
473
p.type=PROPERTY_TYPE_COLOUR;
474
p.helpText=TRANS("Colour of plotted spectrum");
475
p.key=KEY_SPECTRUM_COLOUR;
476
propertyList.addProperty(p,curGroup);
479
bool SpectrumPlotFilter::setProperty( unsigned int key,
480
const std::string &value, bool &needUpdate)
486
case KEY_SPECTRUM_BINWIDTH:
489
if(stream_cast(newWidth,value))
492
if(newWidth < std::numeric_limits<float>::epsilon())
495
//Prevent overflow on next line
496
if(maxPlot == std::numeric_limits<float>::max() ||
497
minPlot == std::numeric_limits<float>::min())
500
if(newWidth < 0.0f || newWidth > (maxPlot - minPlot))
512
case KEY_SPECTRUM_AUTOEXTREMA:
514
//Only allow valid values
515
unsigned int valueInt;
516
if(stream_cast(valueInt,value))
519
//Only update as needed
520
if(valueInt ==0 || valueInt == 1)
522
if((int)autoExtrema != valueInt)
525
autoExtrema=valueInt;
540
case KEY_SPECTRUM_MIN:
546
if(stream_cast(newMin,value))
549
if(newMin >= maxPlot)
559
case KEY_SPECTRUM_MAX:
564
if(stream_cast(newMax,value))
567
if(newMax <= minPlot)
576
case KEY_SPECTRUM_LOGARITHMIC:
578
//Only allow valid values
579
unsigned int valueInt;
580
if(stream_cast(valueInt,value))
583
//Only update as needed
584
if(valueInt ==0 || valueInt == 1)
586
if((int)logarithmic != valueInt)
589
logarithmic=valueInt;
600
//Change the output of the plot streams that
601
//we cached, in order to avoid recomputation
602
for(size_t ui=0;ui<filterOutputs.size();ui++)
604
if(filterOutputs[ui]->getStreamType() == STREAM_TYPE_PLOT)
607
p =(PlotStreamData*)filterOutputs[ui];
609
p->logarithmic=logarithmic;
619
case KEY_SPECTRUM_PLOTTYPE:
621
unsigned int tmpPlotType;
623
tmpPlotType=plotID(value);
625
if(tmpPlotType >= PLOT_TRACE_ENDOFENUM)
628
plotStyle = tmpPlotType;
632
//Perform introspection on
636
for(size_t ui=0;ui<filterOutputs.size();ui++)
638
if(filterOutputs[ui]->getStreamType() == STREAM_TYPE_PLOT)
641
p =(PlotStreamData*)filterOutputs[ui];
643
p->plotStyle=plotStyle;
651
case KEY_SPECTRUM_COLOUR:
653
unsigned char newR,newG,newB,newA;
655
parseColString(value,newR,newG,newB,newA);
657
if(newB != b || newR != r ||
658
newG !=g || newA != a)
666
for(size_t ui=0;ui<filterOutputs.size();ui++)
668
if(filterOutputs[ui]->getStreamType() == STREAM_TYPE_PLOT)
671
p =(PlotStreamData*)filterOutputs[ui];
692
void SpectrumPlotFilter::setUserString(const std::string &s)
703
std::string SpectrumPlotFilter::getErrString(unsigned int code) const
707
case SPECTRUM_BAD_ALLOC:
708
return string(TRANS("Insufficient memory for spectrum filter."));
709
case SPECTRUM_BAD_BINCOUNT:
710
return string(TRANS("Bad bincount value in spectrum filter."));
712
return std::string("BUG: (SpectrumPlotFilter::getErrString) Shouldn't see this!");
715
void SpectrumPlotFilter::setPropFromBinding(const SelectionBinding &b)
720
bool SpectrumPlotFilter::writeState(std::ostream &f,unsigned int format, unsigned int depth) const
725
case STATE_FORMAT_XML:
727
f << tabs(depth) << "<" << trueName() << ">" << endl;
728
f << tabs(depth+1) << "<userstring value=\""<< escapeXML(userString) << "\"/>" << endl;
730
f << tabs(depth+1) << "<extrema min=\"" << minPlot << "\" max=\"" <<
731
maxPlot << "\" auto=\"" << autoExtrema << "\"/>" << endl;
732
f << tabs(depth+1) << "<binwidth value=\"" << binWidth<< "\"/>" << endl;
734
f << tabs(depth+1) << "<colour r=\"" << r<< "\" g=\"" << g << "\" b=\"" <<b
735
<< "\" a=\"" << a << "\"/>" <<endl;
737
f << tabs(depth+1) << "<logarithmic value=\"" << logarithmic<< "\"/>" << endl;
739
f << tabs(depth+1) << "<plottype value=\"" << plotStyle<< "\"/>" << endl;
741
f << tabs(depth) << "</" << trueName() << ">" << endl;
752
bool SpectrumPlotFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileDir)
757
//Retrieve user string
759
if(XMLHelpFwdToElem(nodePtr,"userstring"))
762
xmlChar *xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
765
userString=(char *)xmlString;
772
if(XMLHelpFwdToElem(nodePtr,"extrema"))
775
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"min");
778
tmpStr=(char *)xmlString;
781
//convert from string to digit
782
if(stream_cast(tmpMin,tmpStr))
785
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"max");
788
tmpStr=(char *)xmlString;
791
//convert from string to digit
792
if(stream_cast(tmpMax,tmpStr))
802
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"auto");
806
tmpStr=(char *)xmlString;
809
else if(tmpStr== "0")
823
if(!XMLGetNextElemAttrib(nodePtr,binWidth,"binwidth","value"))
828
if(!autoExtrema && binWidth > maxPlot - minPlot)
833
if(XMLHelpFwdToElem(nodePtr,"colour"))
835
if(!parseXMLColour(nodePtr,r,g,b,a))
839
//Retrieve logarithmic mode
841
if(!XMLGetNextElemAttrib(nodePtr,tmpStr,"logarithmic","value"))
845
else if(tmpStr == "1")
853
if(!XMLGetNextElemAttrib(nodePtr,plotStyle,"plottype","value"))
855
if(plotStyle >= PLOT_TRACE_ENDOFENUM)
862
unsigned int SpectrumPlotFilter::getRefreshBlockMask() const
864
//Absolutely nothing can go through this filter.
865
return STREAMTYPE_MASK_ALL;
868
unsigned int SpectrumPlotFilter::getRefreshEmitMask() const
870
return STREAM_TYPE_PLOT;
873
unsigned int SpectrumPlotFilter::getRefreshUseMask() const
875
return STREAM_TYPE_IONS;
881
IonStreamData *synDataPoints(const unsigned int span[], unsigned int numPts)
883
IonStreamData *d = new IonStreamData;
885
for(unsigned int ui=0;ui<numPts;ui++)
888
h.setPos(Point3D(ui%span[0],
889
ui%span[1],ui%span[2]));
890
h.setMassToCharge(ui);
891
d->data.push_back(h);
899
auto_ptr<IonStreamData> d;
900
const unsigned int VOL[]={
903
const unsigned int NUMPTS=100;
904
d.reset(synDataPoints(VOL,NUMPTS));
906
SpectrumPlotFilter *f;
907
f = new SpectrumPlotFilter;
912
TEST(f->setProperty(KEY_SPECTRUM_LOGARITHMIC,"0",needUp),"Set prop");
914
genColString(255,0,0,s);
915
TEST(f->setProperty(KEY_SPECTRUM_COLOUR,s,needUp),"Set prop");
917
vector<const FilterStreamData*> streamIn,streamOut;
919
streamIn.push_back(d.get());
921
//OK, so now do the rotation
924
f->setCaching(false);
925
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
928
TEST(streamOut.size() == 1,"stream count");
929
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_PLOT,"stream type");
931
PlotStreamData *plot;
932
plot = (PlotStreamData*)streamOut[0];
935
//Check the plot colour is what we want.
936
TEST(fabs(plot->r -1.0f) < sqrt(std::numeric_limits<float>::epsilon()),"colour (r)");
938
sqrt(std::numeric_limits<float>::epsilon()),"colour (g)");
939
TEST(plot->b < sqrt(std::numeric_limits<float>::epsilon()),"colour (b)");
941
//Count the number of ions in the plot, and check that it is equal to the number of ions we put in.
944
for(unsigned int ui=0;ui<plot->xyData.size();ui++)
945
sumV+=plot->xyData[ui].second;
947
TEST(fabs(sumV - (float)NUMPTS) <
948
std::numeric_limits<float>::epsilon(),"ion count");
955
bool SpectrumPlotFilter::runUnitTests()