2
* ionColour.cpp - Filter to create coloured batches of ions based upon value
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 "ionColour.h"
20
#include "filterCommon.h"
22
#include "common/colourmap.h"
26
const unsigned int MAX_NUM_COLOURS=256;
29
KEY_IONCOLOURFILTER_COLOURMAP,
30
KEY_IONCOLOURFILTER_MAPSTART,
31
KEY_IONCOLOURFILTER_MAPEND,
32
KEY_IONCOLOURFILTER_NCOLOURS,
33
KEY_IONCOLOURFILTER_REVERSE,
34
KEY_IONCOLOURFILTER_SHOWBAR,
42
IonColourFilter::IonColourFilter() : colourMap(0),reverseMap(false),
43
nColours(MAX_NUM_COLOURS),showColourBar(true)
46
mapBounds[1] = 100.0f;
49
cache=true; //By default, we should cache, but decision is made higher up
53
Filter *IonColourFilter::cloneUncached() const
55
IonColourFilter *p=new IonColourFilter();
56
p->colourMap = colourMap;
57
p->mapBounds[0]=mapBounds[0];
58
p->mapBounds[1]=mapBounds[1];
59
p->nColours =nColours;
60
p->showColourBar =showColourBar;
61
p->reverseMap=reverseMap;
63
//We are copying wether to cache or not,
64
//not the cache itself
67
p->userString=userString;
71
size_t IonColourFilter::numBytesForCache(size_t nObjects) const
73
return (size_t)((float)(nObjects*IONDATA_SIZE));
78
unsigned int IonColourFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
79
std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
81
//use the cached copy if we have it.
84
ASSERT(filterOutputs.size());
85
propagateStreams(dataIn,getOut,STREAM_TYPE_IONS,false);
87
propagateCache(getOut);
89
if(filterOutputs.size() && showColourBar)
91
DrawStreamData *d = new DrawStreamData;
93
d->drawables.push_back(makeColourBar(mapBounds[0],
94
mapBounds[1],nColours,colourMap));
102
ASSERT(nColours >0 && nColours<=MAX_NUM_COLOURS);
103
IonStreamData *d[nColours];
104
unsigned char rgb[3]; //RGB array
105
//Build the colourmap values, each as a unique filter output
106
for(unsigned int ui=0;ui<nColours; ui++)
108
d[ui]=new IonStreamData;
111
value = (float)ui*(mapBounds[1]-mapBounds[0])/(float)nColours + mapBounds[0];
112
//Pick the desired colour map
113
colourMapWrap(colourMap,rgb,value,mapBounds[0],mapBounds[1],reverseMap);
115
d[ui]->r=rgb[0]/255.0f;
116
d[ui]->g=rgb[1]/255.0f;
117
d[ui]->b=rgb[2]/255.0f;
123
//Try to maintain ion size if possible
124
bool haveIonSize,sameSize; // have we set the ionSize?
129
//Did we find any ions in this pass?
130
bool foundIons=false;
131
unsigned int totalSize=numElements(dataIn);
132
unsigned int curProg=NUM_CALLBACK;
134
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
136
switch(dataIn[ui]->getStreamType())
138
case STREAM_TYPE_IONS:
142
//Check for ion size consistency
145
sameSize &= (fabs(ionSize-((const IonStreamData *)dataIn[ui])->ionSize)
146
< std::numeric_limits<float>::epsilon());
150
ionSize=((const IonStreamData *)dataIn[ui])->ionSize;
153
for(vector<IonHit>::const_iterator it=((const IonStreamData *)dataIn[ui])->data.begin();
154
it!=((const IonStreamData *)dataIn[ui])->data.end(); ++it)
159
tmp= (it->getMassToCharge()-mapBounds[0])/(mapBounds[1]-mapBounds[0]);
160
tmp = std::max(0.0f,tmp);
161
tmp = std::min(tmp,1.0f);
163
colour=(unsigned int)(tmp*(float)(nColours-1));
164
d[colour]->data.push_back(*it);
166
//update progress every CALLBACK ions
170
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
171
curProg=NUM_CALLBACK;
172
if(!(*callback)(false))
174
for(unsigned int ui=0;ui<nColours;ui++)
176
return IONCOLOUR_ABORT_ERR;
185
getOut.push_back(dataIn[ui]);
190
//create the colour bar as needed
191
if(foundIons && showColourBar)
193
DrawStreamData *d = new DrawStreamData;
194
d->drawables.push_back(makeColourBar(mapBounds[0],mapBounds[1],nColours,colourMap,reverseMap));
201
//If all the ions are the same size, then propagate
202
if(haveIonSize && sameSize)
204
for(unsigned int ui=0;ui<nColours;ui++)
205
d[ui]->ionSize=ionSize;
207
//merge the results as needed
210
for(unsigned int ui=0;ui<nColours;ui++)
212
if(d[ui]->data.size())
216
if(d[ui]->data.size())
217
filterOutputs.push_back(d[ui]);
219
cacheOK=filterOutputs.size();
223
for(unsigned int ui=0;ui<nColours;ui++)
225
//NOTE: MUST set cached BEFORE push_back!
231
//push the colours onto the output. cached or not (their status is set above).
232
for(unsigned int ui=0;ui<nColours;ui++)
234
if(d[ui]->data.size())
235
getOut.push_back(d[ui]);
244
void IonColourFilter::getProperties(FilterPropGroup &propertyList) const
249
vector<pair<unsigned int, string> > choices;
253
for(unsigned int ui=0;ui<NUM_COLOURMAPS; ui++)
254
choices.push_back(make_pair(ui,getColourMapName(ui)));
256
tmpStr=choiceString(choices,colourMap);
258
p.name=TRANS("Colour Map");
260
p.key=KEY_IONCOLOURFILTER_COLOURMAP;
261
p.type=PROPERTY_TYPE_CHOICE;
262
p.helpText=TRANS("Colour scheme used to assign points colours by value");
263
propertyList.addProperty(p,curGroup);
266
p.name=TRANS("Reverse map");
267
p.helpText=TRANS("Reverse the colour scale");
268
p.data= boolStrEnc(reverseMap);
269
p.key=KEY_IONCOLOURFILTER_REVERSE;
270
p.type=PROPERTY_TYPE_BOOL;
271
propertyList.addProperty(p,curGroup);
274
p.name=TRANS("Show Bar");
275
p.key=KEY_IONCOLOURFILTER_SHOWBAR;
276
p.data=boolStrEnc(showColourBar);
277
p.type=PROPERTY_TYPE_BOOL;
278
propertyList.addProperty(p,curGroup);
280
stream_cast(tmpStr,nColours);
281
p.name=TRANS("Num Colours");
283
p.helpText=TRANS("Number of unique colours to use in colour map");
284
p.key=KEY_IONCOLOURFILTER_NCOLOURS;
285
p.type=PROPERTY_TYPE_INTEGER;
286
propertyList.addProperty(p,curGroup);
288
stream_cast(tmpStr,mapBounds[0]);
289
p.name=TRANS("Map start");
290
p.helpText=TRANS("Assign points with this value to the first colour in map");
292
p.key=KEY_IONCOLOURFILTER_MAPSTART;
293
p.type=PROPERTY_TYPE_REAL;
294
propertyList.addProperty(p,curGroup);
296
stream_cast(tmpStr,mapBounds[1]);
297
p.name=TRANS("Map end");
298
p.helpText=TRANS("Assign points with this value to the last colour in map");
300
p.key=KEY_IONCOLOURFILTER_MAPEND;
301
p.type=PROPERTY_TYPE_REAL;
302
propertyList.addProperty(p,curGroup);
307
bool IonColourFilter::setProperty( unsigned int key,
308
const std::string &value, bool &needUpdate)
314
case KEY_IONCOLOURFILTER_COLOURMAP:
317
tmpMap=(unsigned int)-1;
318
for(unsigned int ui=0;ui<NUM_COLOURMAPS;ui++)
320
if(value== getColourMapName(ui))
327
if(tmpMap >=NUM_COLOURMAPS || tmpMap ==colourMap)
335
case KEY_IONCOLOURFILTER_REVERSE:
338
if(!boolStrDec(value,newVal))
341
//Only need update if changed
342
if(newVal!=reverseMap)
351
case KEY_IONCOLOURFILTER_MAPSTART:
354
stream_cast(tmpBound,value);
355
if(tmpBound >=mapBounds[1])
360
mapBounds[0]=tmpBound;
363
case KEY_IONCOLOURFILTER_MAPEND:
366
stream_cast(tmpBound,value);
367
if(tmpBound <=mapBounds[0])
372
mapBounds[1]=tmpBound;
375
case KEY_IONCOLOURFILTER_NCOLOURS:
377
unsigned int numColours;
378
if(stream_cast(numColours,value))
383
//enforce 1->MAX_NUM_COLOURS range
384
nColours=std::min(numColours,MAX_NUM_COLOURS);
389
case KEY_IONCOLOURFILTER_SHOWBAR:
392
if(!boolStrDec(value,newVal))
394
//Only need update if changed
395
if(newVal!=showColourBar)
400
showColourBar=newVal;
411
std::string IonColourFilter::getErrString(unsigned int code) const
413
//Currently the only error is aborting
414
return std::string(TRANS("Aborted"));
417
void IonColourFilter::setPropFromBinding(const SelectionBinding &b)
422
bool IonColourFilter::writeState(std::ostream &f,unsigned int format, unsigned int depth) const
427
case STATE_FORMAT_XML:
429
f << tabs(depth) << "<" << trueName() << ">" << endl;
430
f << tabs(depth+1) << "<userstring value=\""<< escapeXML(userString) << "\"/>" << endl;
432
f << tabs(depth+1) << "<colourmap value=\"" << colourMap << "\"/>" << endl;
433
f << tabs(depth+1) << "<extrema min=\"" << mapBounds[0] << "\" max=\""
434
<< mapBounds[1] << "\"/>" << endl;
435
f << tabs(depth+1) << "<ncolours value=\"" << nColours << "\"/>" << endl;
437
f << tabs(depth+1) << "<showcolourbar value=\"" << boolStrEnc(showColourBar)<< "\"/>" << endl;
438
f << tabs(depth+1) << "<reversemap value=\"" << boolStrEnc(reverseMap)<< "\"/>" << endl;
440
f << tabs(depth) << "</" << trueName() << ">" << endl;
451
bool IonColourFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileDir)
453
//Retrieve user string
455
if(XMLHelpFwdToElem(nodePtr,"userstring"))
458
xmlChar *xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
461
userString=(char *)xmlString;
468
if(XMLHelpFwdToElem(nodePtr,"colourmap"))
471
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
474
tmpStr=(char *)xmlString;
476
//convert from string to digit
477
if(stream_cast(colourMap,tmpStr))
480
if(colourMap>= NUM_COLOURMAPS)
488
if(XMLHelpFwdToElem(nodePtr,"extrema"))
491
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"min");
494
tmpStr=(char *)xmlString;
496
//convert from string to digit
497
if(stream_cast(tmpMin,tmpStr))
500
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"max");
503
tmpStr=(char *)xmlString;
505
//convert from string to digit
506
if(stream_cast(tmpMax,tmpStr))
519
//Retrieve num colours
521
if(XMLHelpFwdToElem(nodePtr,"ncolours"))
524
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
527
tmpStr=(char *)xmlString;
529
//convert from string to digit
530
if(stream_cast(nColours,tmpStr))
536
//Retrieve num colours
538
if(XMLHelpFwdToElem(nodePtr,"showcolourbar"))
541
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
544
tmpStr=(char *)xmlString;
546
//convert from string to digit
547
if(stream_cast(showColourBar,tmpStr))
553
//Check for colour map reversal
555
if(XMLHelpFwdToElem(nodePtr,"reversemap"))
557
//Didn't exist prior to 0.0.15, assume off
562
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
565
tmpStr=(char *)xmlString;
567
//convert from string to bool
568
if(!boolStrDec(tmpStr,reverseMap))
577
unsigned int IonColourFilter::getRefreshBlockMask() const
579
//Anything but ions can go through this filter.
580
return STREAM_TYPE_IONS;
583
unsigned int IonColourFilter::getRefreshEmitMask() const
585
return STREAM_TYPE_DRAW | STREAM_TYPE_IONS;
588
unsigned int IonColourFilter::getRefreshUseMask() const
590
return STREAM_TYPE_IONS;
594
IonStreamData *sythIonCountData(unsigned int numPts, float mStart, float mEnd)
596
IonStreamData *d = new IonStreamData;
597
d->data.resize(numPts);
598
for(unsigned int ui=0; ui<numPts;ui++)
602
h.setPos(Point3D(ui,ui,ui));
603
h.setMassToCharge( (mEnd-mStart)*(float)ui/(float)numPts + mStart);
613
const int NUM_PTS=1000;
614
vector<const FilterStreamData*> streamIn,streamOut;
615
IonStreamData *d=sythIonCountData(NUM_PTS,0,100);
616
streamIn.push_back(d);
619
IonColourFilter *f = new IonColourFilter;
620
f->setCaching(false);
623
TEST(f->setProperty(KEY_IONCOLOURFILTER_NCOLOURS,"100",needUpdate),"Set prop");
624
TEST(f->setProperty(KEY_IONCOLOURFILTER_MAPSTART,"0",needUpdate),"Set prop");
625
TEST(f->setProperty(KEY_IONCOLOURFILTER_MAPEND,"100",needUpdate),"Set prop");
626
TEST(f->setProperty(KEY_IONCOLOURFILTER_SHOWBAR,"0",needUpdate),"Set prop");
629
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
633
TEST(streamOut.size() == 99,"stream count");
635
for(unsigned int ui=0;ui<streamOut.size();ui++)
637
TEST(streamOut[ui]->getStreamType() == STREAM_TYPE_IONS,"stream type");
640
for(unsigned int ui=0;ui<streamOut.size();ui++)
641
delete streamOut[ui];
647
bool IonColourFilter::runUnitTests()