2
* transform.cpp - Perform geometrical transform operations on point clouds
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 "transform.h"
20
#include "filterCommon.h"
29
KEY_SCALEFACTOR_ANISOTROPIC,
31
KEY_TRANSFORM_SHOWORIGIN,
40
//Possible transform modes (scaling, rotation etc)
45
MODE_SCALE_ANISOTROPIC,
53
//!Possible mode for selection of origin in transform filter
57
ORIGINMODE_CENTREBOUND,
58
ORIGINMODE_MASSCENTRE,
59
ORIGINMODE_END, // Not actually origin mode, just end of enum
62
//!Possible noise modes
77
const char *TRANSFORM_MODE_STRING[] = { NTRANS("Translate"),
78
NTRANS("Scale (isotropic)"),
79
NTRANS("Scale (anisotropic)"),
81
NTRANS("Value Shuffle"),
82
NTRANS("Spatial Noise"),
83
NTRANS("Translate Value")
86
const char *TRANSFORM_ORIGIN_STRING[]={
88
NTRANS("Boundbox Centre"),
94
//=== Transform filter ===
95
TransformFilter::TransformFilter()
97
COMPILE_ASSERT(THREEDEP_ARRAYSIZE(TRANSFORM_MODE_STRING) == MODE_ENUM_END);
98
COMPILE_ASSERT(THREEDEP_ARRAYSIZE(TRANSFORM_ORIGIN_STRING) == ORIGINMODE_END);
101
transformMode=MODE_TRANSLATE;
102
originMode=ORIGINMODE_SELECT;
103
noiseType=NOISETYPE_WHITE;
104
//Set up default value
105
vectorParams.resize(1);
106
vectorParams[0] = Point3D(0,0,0);
115
Filter *TransformFilter::cloneUncached() const
117
TransformFilter *p=new TransformFilter();
120
p->vectorParams.resize(vectorParams.size());
121
p->scalarParams.resize(scalarParams.size());
123
std::copy(vectorParams.begin(),vectorParams.end(),p->vectorParams.begin());
124
std::copy(scalarParams.begin(),scalarParams.end(),p->scalarParams.begin());
126
p->showPrimitive=showPrimitive;
127
p->originMode=originMode;
128
p->transformMode=transformMode;
129
p->showOrigin=showOrigin;
130
p->noiseType=noiseType;
131
//We are copying wether to cache or not,
132
//not the cache itself
135
p->userString=userString;
139
size_t TransformFilter::numBytesForCache(size_t nObjects) const
141
return nObjects*sizeof(IonHit);
144
DrawStreamData* TransformFilter::makeMarkerSphere(SelectionDevice* &s) const
146
//construct a new primitive, do not cache
147
DrawStreamData *drawData=new DrawStreamData;
148
drawData->parent=this;
149
//Add drawable components
150
DrawSphere *dS = new DrawSphere;
151
dS->setOrigin(vectorParams[0]);
153
//FIXME: Alpha blending is all screwed up. May require more
154
//advanced drawing in scene. (front-back drawing).
155
//I have set alpha=1 for now.
156
dS->setColour(0.2,0.2,0.8,1.0);
157
dS->setLatSegments(40);
158
dS->setLongSegments(40);
160
drawData->drawables.push_back(dS);
163
//Set up selection "device" for user interaction
164
//Note the order of s->addBinding is critical,
165
//as bindings are selected by first match.
167
//The object is selectable
168
if (originMode == ORIGINMODE_SELECT )
172
s=new SelectionDevice(this);
175
b.setBinding(SELECT_BUTTON_LEFT,0,DRAW_SPHERE_BIND_ORIGIN,
176
BINDING_SPHERE_ORIGIN,dS->getOrigin(),dS);
177
b.setInteractionMode(BIND_MODE_POINT3D_TRANSLATE);
186
unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
187
std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
189
//use the cached copy if we have it.
192
propagateStreams(dataIn,getOut, STREAM_TYPE_IONS,false);
193
propagateCache(getOut);
198
//The user is allowed to choose the mode by which the origin is computed
199
//so set the origin variable depending upon this
202
case ORIGINMODE_CENTREBOUND:
205
masterB.setInverseLimits();
206
#pragma omp parallel for
207
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
211
if(dataIn[ui]->getStreamType() == STREAM_TYPE_IONS)
213
const IonStreamData* ions;
214
ions = (const IonStreamData*)dataIn[ui];
215
if(ions->data.size())
217
IonHit::getBoundCube(ions->data,thisB);
219
masterB.expand(thisB);
224
if(!masterB.isValid())
225
vectorParams[0]=Point3D(0,0,0);
227
vectorParams[0]=masterB.getCentroid();
230
case ORIGINMODE_MASSCENTRE:
232
Point3D massCentre(0,0,0);
234
#pragma omp parallel for
235
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
238
if(dataIn[ui]->getStreamType() == STREAM_TYPE_IONS)
240
const IonStreamData* ions;
241
ions = (const IonStreamData*)dataIn[ui];
243
if(ions->data.size())
246
thisCentre=Point3D(0,0,0);
247
for(unsigned int uj=0;uj<ions->data.size();uj++)
248
thisCentre+=ions->data[uj].getPosRef();
249
massContrib=thisCentre*1.0/(float)ions->data.size();
251
massCentre+=massContrib;
256
vectorParams[0]=massCentre*1.0/(float)numCentres;
260
case ORIGINMODE_SELECT:
266
//If the user is using a transform mode that requires origin selection
267
if(showOrigin && (transformMode == MODE_ROTATE ||
268
transformMode == MODE_SCALE_ANISOTROPIC ||
269
transformMode == MODE_SCALE_ISOTROPIC) )
272
DrawStreamData *d=makeMarkerSphere(s);
274
devices.push_back(s);
278
filterOutputs.push_back(d);
286
//Apply the transformations to the incoming
287
//ion streams, generating new outgoing ion streams with
288
//the modified positions
289
size_t totalSize=numElements(dataIn);
291
//If there are no ions, nothing to do.
292
// just copy non-ion input to output
295
for(unsigned int ui=0;ui<dataIn.size();ui++)
297
if(dataIn[ui]->getStreamType() == STREAM_TYPE_IONS)
300
getOut.push_back(dataIn[ui]);
305
if( transformMode != MODE_VALUE_SHUFFLE)
307
//Dont cross the streams. Why? It would be bad.
308
// - Im fuzzy on the whole good-bad thing, what do you mean bad?"
309
// - Every ion in the data body can be operated on independently.
311
// OK, important safety tip.
313
for(unsigned int ui=0;ui<dataIn.size() ;ui++)
315
switch(transformMode)
317
case MODE_SCALE_ISOTROPIC:
319
//We are going to scale the incoming point data
320
//around the specified origin.
321
ASSERT(vectorParams.size() == 1);
322
ASSERT(scalarParams.size() == 1);
323
float scaleFactor=scalarParams[0];
324
Point3D origin=vectorParams[0];
326
switch(dataIn[ui]->getStreamType())
328
case STREAM_TYPE_IONS:
330
//Set up scaling output ion stream
331
IonStreamData *d=new IonStreamData;
333
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
337
d->data.resize(src->data.size());
339
catch(std::bad_alloc)
348
d->ionSize = src->ionSize;
349
d->valueType=src->valueType;
351
ASSERT(src->data.size() <= totalSize);
352
unsigned int curProg=NUM_CALLBACK;
356
#pragma omp parallel for shared(spin)
357
for(unsigned int ui=0;ui<src->data.size();ui++)
359
unsigned int thisT=omp_get_thread_num();
368
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
374
if(!(*callback)(false))
380
//set the position for the given ion
381
d->data[ui].setPos((src->data[ui].getPosRef() - origin)*scaleFactor+origin);
382
d->data[ui].setMassToCharge(src->data[ui].getMassToCharge());
387
return ERR_CALLBACK_FAIL;
391
//Single threaded version
393
//Copy across the ions into the target
394
for(vector<IonHit>::const_iterator it=src->data.begin();
395
it!=src->data.end(); ++it)
397
//set the position for the given ion
398
d->data[pos].setPos((it->getPosRef() - origin)*scaleFactor+origin);
399
d->data[pos].setMassToCharge(it->getMassToCharge());
400
//update progress every CALLBACK ions
404
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
405
if(!(*callback)(false))
408
return ERR_CALLBACK_FAIL;
410
curProg=NUM_CALLBACK;
415
ASSERT(pos == d->data.size());
417
ASSERT(d->data.size() == src->data.size());
422
filterOutputs.push_back(d);
432
//Just copy across the ptr, if we are unfamiliar with this type
433
getOut.push_back(dataIn[ui]);
438
case MODE_SCALE_ANISOTROPIC:
440
//We are going to scale the incoming point data
441
//around the specified origin.
442
ASSERT(vectorParams.size() == 2);
445
Point3D origin=vectorParams[0];
446
Point3D transformVec=vectorParams[1];
447
switch(dataIn[ui]->getStreamType())
449
case STREAM_TYPE_IONS:
451
//Set up scaling output ion stream
452
IonStreamData *d=new IonStreamData;
454
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
458
d->data.resize(src->data.size());
460
catch(std::bad_alloc)
469
d->ionSize = src->ionSize;
470
d->valueType=src->valueType;
472
ASSERT(src->data.size() <= totalSize);
473
unsigned int curProg=NUM_CALLBACK;
477
#pragma omp parallel for shared(spin)
478
for(unsigned int ui=0;ui<src->data.size();ui++)
480
unsigned int thisT=omp_get_thread_num();
489
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
495
if(!(*callback)(false))
501
//set the position for the given ion
502
d->data[ui].setPos((src->data[ui].getPosRef() - origin)*transformVec+origin);
503
d->data[ui].setMassToCharge(src->data[ui].getMassToCharge());
508
return ERR_CALLBACK_FAIL;
512
//Single threaded version
514
//Copy across the ions into the target
515
for(vector<IonHit>::const_iterator it=src->data.begin();
516
it!=src->data.end(); ++it)
518
//set the position for the given ion
519
d->data[pos].setPos((it->getPosRef() - origin)*transformVec+origin);
520
d->data[pos].setMassToCharge(it->getMassToCharge());
521
//update progress every CALLBACK ions
525
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
526
if(!(*callback)(false))
529
return ERR_CALLBACK_FAIL;
531
curProg=NUM_CALLBACK;
536
ASSERT(pos == d->data.size());
538
ASSERT(d->data.size() == src->data.size());
543
filterOutputs.push_back(d);
553
//Just copy across the ptr, if we are unfamiliar with this type
554
getOut.push_back(dataIn[ui]);
565
//We are going to scale the incoming point data
566
//around the specified origin.
567
ASSERT(vectorParams.size() == 1);
568
ASSERT(scalarParams.size() == 0);
569
Point3D origin =vectorParams[0];
570
switch(dataIn[ui]->getStreamType())
572
case STREAM_TYPE_IONS:
574
//Set up scaling output ion stream
575
IonStreamData *d=new IonStreamData;
578
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
581
d->data.resize(src->data.size());
583
catch(std::bad_alloc)
592
d->ionSize = src->ionSize;
593
d->valueType=src->valueType;
595
ASSERT(src->data.size() <= totalSize);
596
unsigned int curProg=NUM_CALLBACK;
600
#pragma omp parallel for shared(spin)
601
for(unsigned int ui=0;ui<src->data.size();ui++)
603
unsigned int thisT=omp_get_thread_num();
612
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
618
if(!(*callback)(false))
624
//set the position for the given ion
625
d->data[ui].setPos((src->data[ui].getPosRef() - origin));
626
d->data[ui].setMassToCharge(src->data[ui].getMassToCharge());
631
return ERR_CALLBACK_FAIL;
635
//Single threaded version
637
//Copy across the ions into the target
638
for(vector<IonHit>::const_iterator it=src->data.begin();
639
it!=src->data.end(); ++it)
641
//set the position for the given ion
642
d->data[pos].setPos((it->getPosRef() - origin));
643
d->data[pos].setMassToCharge(it->getMassToCharge());
644
//update progress every CALLBACK ions
648
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
649
if(!(*callback)(false))
652
return ERR_CALLBACK_FAIL;
654
curProg=NUM_CALLBACK;
658
ASSERT(pos == d->data.size());
660
ASSERT(d->data.size() == src->data.size());
664
filterOutputs.push_back(d);
674
//Just copy across the ptr, if we are unfamiliar with this type
675
getOut.push_back(dataIn[ui]);
680
case MODE_TRANSLATE_VALUE:
682
//We are going to scale the incoming point data
683
//around the specified origin.
684
ASSERT(vectorParams.size() == 0);
685
ASSERT(scalarParams.size() == 1);
686
float origin =scalarParams[0];
687
switch(dataIn[ui]->getStreamType())
689
case STREAM_TYPE_IONS:
691
//Set up scaling output ion stream
692
IonStreamData *d=new IonStreamData;
695
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
698
d->data.resize(src->data.size());
700
catch(std::bad_alloc)
709
d->ionSize = src->ionSize;
710
d->valueType=src->valueType;
712
ASSERT(src->data.size() <= totalSize);
713
unsigned int curProg=NUM_CALLBACK;
717
#pragma omp parallel for shared(spin)
718
for(unsigned int ui=0;ui<src->data.size();ui++)
720
unsigned int thisT=omp_get_thread_num();
729
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
735
if(!(*callback)(false))
741
//set the position for the given ion
742
d->data[ui].setPos((src->data[ui].getPosRef()));
743
d->data[ui].setMassToCharge(src->data[ui].getMassToCharge()+origin);
748
return ERR_CALLBACK_FAIL;
752
//Single threaded version
754
//Copy across the ions into the target
755
for(vector<IonHit>::const_iterator it=src->data.begin();
756
it!=src->data.end(); ++it)
758
//set the position for the given ion
759
d->data[pos].setPos((it->getPosRef()));
760
d->data[pos].setMassToCharge(it->getMassToCharge() + origin);
761
//update progress every CALLBACK ions
765
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
766
if(!(*callback)(false))
769
return ERR_CALLBACK_FAIL;
771
curProg=NUM_CALLBACK;
775
ASSERT(pos == d->data.size());
777
ASSERT(d->data.size() == src->data.size());
781
filterOutputs.push_back(d);
791
//Just copy across the ptr, if we are unfamiliar with this type
792
getOut.push_back(dataIn[ui]);
799
Point3D origin=vectorParams[0];
800
switch(dataIn[ui]->getStreamType())
802
case STREAM_TYPE_IONS:
805
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
806
//Set up output ion stream
807
IonStreamData *d=new IonStreamData;
811
d->data.resize(src->data.size());
813
catch(std::bad_alloc)
822
d->ionSize = src->ionSize;
823
d->valueType=src->valueType;
825
//We are going to rotate the incoming point data
826
//around the specified origin.
827
ASSERT(vectorParams.size() == 2);
828
ASSERT(scalarParams.size() == 1);
829
Point3D axis =vectorParams[1];
831
float angle=scalarParams[0]*M_PI/180.0f;
833
unsigned int curProg=NUM_CALLBACK;
842
//Generate the rotating quaternion
843
quat_get_rot_quat(&rotVec,-angle,&q1);
844
ASSERT(src->data.size() <= totalSize);
849
//TODO: Parallelise rotation
850
//Copy across the ions into the target
851
for(vector<IonHit>::const_iterator it=src->data.begin();
852
it!=src->data.end(); ++it)
854
p.fx=it->getPosRef()[0]-origin[0];
855
p.fy=it->getPosRef()[1]-origin[1];
856
p.fz=it->getPosRef()[2]-origin[2];
857
quat_rot_apply_quat(&p,&q1);
858
//set the position for the given ion
859
d->data[pos].setPos(p.fx+origin[0],
860
p.fy+origin[1],p.fz+origin[2]);
861
d->data[pos].setMassToCharge(it->getMassToCharge());
862
//update progress every CALLBACK ions
866
progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
867
if(!(*callback)(false))
870
return ERR_CALLBACK_FAIL;
872
curProg=NUM_CALLBACK;
877
ASSERT(d->data.size() == src->data.size());
881
filterOutputs.push_back(d);
891
getOut.push_back(dataIn[ui]);
897
case MODE_SPATIAL_NOISE:
899
ASSERT(scalarParams.size() ==1 &&
900
vectorParams.size()==0);
901
switch(dataIn[ui]->getStreamType())
903
case STREAM_TYPE_IONS:
905
//Set up scaling output ion stream
906
IonStreamData *d=new IonStreamData;
909
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
912
d->data.resize(src->data.size());
914
catch(std::bad_alloc)
923
d->ionSize = src->ionSize;
924
d->valueType=src->valueType;
926
float scaleFactor=scalarParams[0];
927
ASSERT(src->data.size() <= totalSize);
928
unsigned int curProg=NUM_CALLBACK;
930
//NOTE: This *cannot* be parallelised without parallelising the random
931
// number generator safely. If using multiple random number generators,
932
// one would need to ensure sufficient entropy in EACH generator. This
933
// is not trivial to prove, and so has not been done here. Bootstrapping
934
// each random number generator using non-random seeds could be problematic
935
// same as feeding back a random number into other rng instances
937
// One solution is to use the unix /dev/urandom interface or the windows
938
// cryptographic API, alternatively use the TR1 header's Mersenne twister with
940
// http://theo.phys.sci.hiroshima-u.ac.jp/~ishikawa/PRNG/mt_stream_en.html
943
case NOISETYPE_WHITE:
945
for(size_t ui=0;ui<src->data.size();ui++)
949
pt.setValue(0,randGen.genUniformDev()-0.5f);
950
pt.setValue(1,randGen.genUniformDev()-0.5f);
951
pt.setValue(2,randGen.genUniformDev()-0.5f);
955
//set the position for the given ion
956
d->data[ui].setPos(src->data[ui].getPosRef() + pt);
957
d->data[ui].setMassToCharge(src->data[ui].getMassToCharge());
962
curProg=NUM_CALLBACK;
963
progress.filterProgress= (unsigned int)((float)(ui)/((float)totalSize)*100.0f);
964
if(!(*callback)(false))
967
return ERR_CALLBACK_FAIL;
973
case NOISETYPE_GAUSSIAN:
975
for(size_t ui=0;ui<src->data.size();ui++)
979
pt.setValue(0,randGen.genGaussDev());
980
pt.setValue(1,randGen.genGaussDev());
981
pt.setValue(2,randGen.genGaussDev());
985
//set the position for the given ion
986
d->data[ui].setPos(src->data[ui].getPosRef() + pt);
987
d->data[ui].setMassToCharge(src->data[ui].getMassToCharge());
992
curProg=NUM_CALLBACK;
993
progress.filterProgress= (unsigned int)((float)(ui)/((float)totalSize)*100.0f);
994
if(!(*callback)(false))
997
return ERR_CALLBACK_FAIL;
1006
ASSERT(d->data.size() == src->data.size());
1010
filterOutputs.push_back(d);
1016
getOut.push_back(d);
1020
getOut.push_back(dataIn[ui]);
1031
progress.filterProgress=0;
1032
progress.stepName=TRANS("Collate");
1034
if(!(*callback)(true))
1035
return ERR_CALLBACK_FAIL;
1036
//we have to cross the streams (I thought that was bad?)
1037
// - Each dataset is no longer independent, and needs to
1038
// be mixed with the other datasets. Bugger; sounds mem. expensive.
1040
//Set up output ion stream
1041
IonStreamData *d=new IonStreamData;
1044
//TODO: Better output colouring/size
1045
//Set up ion metadata
1051
d->valueType=TRANS("Mass-to-Charge (amu/e)");
1055
vector<float> massData;
1057
//TODO: Ouch. Memory intensive -- could do a better job
1061
massData.resize(totalSize);
1062
d->data.resize(totalSize);
1064
catch(std::bad_alloc)
1069
//merge the datasets
1070
for(size_t ui=0;ui<dataIn.size() ;ui++)
1072
switch(dataIn[ui]->getStreamType())
1074
case STREAM_TYPE_IONS:
1077
const IonStreamData *src = (const IonStreamData *)dataIn[ui];
1079
//Loop through the ions in this stream, and copy its data value
1080
#pragma omp parallel for shared(massData,d,curPos,src)
1081
for(size_t uj=0;uj<src->data.size();uj++)
1083
massData[uj+curPos] = src->data[uj].getMassToCharge();
1084
d->data[uj+curPos].setPos(src->data[uj].getPos());
1087
if(!(*callback)(true))
1090
return ERR_CALLBACK_FAIL;
1093
curPos+=src->data.size();
1097
getOut.push_back(dataIn[ui]);
1104
progress.filterProgress=0;
1105
progress.stepName=TRANS("Shuffle");
1106
if(!(*callback)(true))
1109
return ERR_CALLBACK_FAIL;
1111
//Shuffle the value data.TODO: callback functor
1112
std::random_shuffle(massData.begin(),massData.end());
1113
if(!(*callback)(true))
1116
return ERR_CALLBACK_FAIL;
1120
progress.filterProgress=0;
1121
progress.stepName=TRANS("Splice");
1122
if(!(*callback)(true))
1125
return ERR_CALLBACK_FAIL;
1130
//Set the output data by splicing together the
1131
//shuffled values and the original position info
1132
#pragma omp parallel for shared(d,massData)
1133
for(size_t uj=0;uj<totalSize;uj++)
1134
d->data[uj].setMassToCharge(massData[uj]);
1136
if(!(*callback)(true))
1139
return ERR_CALLBACK_FAIL;
1147
filterOutputs.push_back(d);
1153
getOut.push_back(d);
1160
void TransformFilter::getProperties(FilterPropGroup &propertyList) const
1166
vector<pair<unsigned int,string> > choices;
1167
for(unsigned int ui=0;ui<MODE_ENUM_END; ui++)
1168
choices.push_back(make_pair(ui,TRANS(TRANSFORM_MODE_STRING[ui])));
1170
tmpStr=choiceString(choices,transformMode);
1173
p.name=TRANS("Mode");
1175
p.type=PROPERTY_TYPE_CHOICE;
1176
p.helpText=TRANS("Algorithm to use to transform point data");
1178
propertyList.addProperty(p,curGroup);
1182
//non-translation transforms require a user to select an origin
1183
if( (transformMode == MODE_SCALE_ISOTROPIC || transformMode == MODE_SCALE_ANISOTROPIC
1184
|| transformMode == MODE_ROTATE))
1186
vector<pair<unsigned int,string> > choices;
1187
for(unsigned int ui=0;ui<ORIGINMODE_END;ui++)
1188
choices.push_back(make_pair(ui,getOriginTypeString(ui)));
1190
tmpStr= choiceString(choices,originMode);
1192
p.name=TRANS("Origin mode");
1194
p.type=PROPERTY_TYPE_CHOICE;
1195
p.helpText=TRANS("Select how transform origin is computed");
1196
p.key=KEY_ORIGINMODE;
1197
propertyList.addProperty(p,curGroup);
1199
stream_cast(tmpStr,showOrigin);
1200
p.name=TRANS("Show marker");
1202
p.type=PROPERTY_TYPE_BOOL;
1203
if( originMode == ORIGINMODE_SELECT)
1204
p.helpText=TRANS("Display an interactive object to set transform origin");
1206
p.helpText=TRANS("Display a small marker to denote transform origin");
1207
p.key=KEY_TRANSFORM_SHOWORIGIN;
1208
propertyList.addProperty(p,curGroup);
1213
bool haveProps=true;
1214
switch(transformMode)
1216
case MODE_TRANSLATE:
1218
ASSERT(vectorParams.size() == 1);
1219
ASSERT(scalarParams.size() == 0);
1221
stream_cast(tmpStr,vectorParams[0]);
1222
p.name=TRANS("Translation");
1224
p.type=PROPERTY_TYPE_POINT3D;
1225
p.helpText=TRANS("Translation vector for transform");
1227
propertyList.addProperty(p,curGroup);
1230
case MODE_TRANSLATE_VALUE:
1232
ASSERT(vectorParams.size() == 0);
1233
ASSERT(scalarParams.size() == 1);
1236
stream_cast(tmpStr,scalarParams[0]);
1237
p.name=TRANS("Offset");
1239
p.key=KEY_ORIGIN_VALUE;
1240
p.type=PROPERTY_TYPE_REAL;
1241
p.helpText=TRANS("Scalar to use to offset each point's associated value");
1242
propertyList.addProperty(p,curGroup);
1245
case MODE_SCALE_ISOTROPIC:
1247
ASSERT(vectorParams.size() == 1);
1248
ASSERT(scalarParams.size() == 1);
1251
if(originMode == ORIGINMODE_SELECT)
1253
stream_cast(tmpStr,vectorParams[0]);
1255
p.name=TRANS("Origin");
1257
p.type=PROPERTY_TYPE_POINT3D;
1258
p.helpText=TRANS("Origin of scale trasnform");
1259
propertyList.addProperty(p,curGroup);
1262
stream_cast(tmpStr,scalarParams[0]);
1264
p.key=KEY_SCALEFACTOR;
1265
p.name=TRANS("Scale Fact.");
1267
p.type=PROPERTY_TYPE_REAL;
1268
p.helpText=TRANS("Enlargement factor for scaling around origin");
1269
propertyList.addProperty(p,curGroup);
1273
case MODE_SCALE_ANISOTROPIC:
1275
ASSERT(vectorParams.size() == 2);
1278
if(originMode == ORIGINMODE_SELECT)
1280
stream_cast(tmpStr,vectorParams[0]);
1282
p.name=TRANS("Origin");
1284
p.type=PROPERTY_TYPE_POINT3D;
1285
p.helpText=TRANS("Origin of scale trasnform");
1286
propertyList.addProperty(p,curGroup);
1289
stream_cast(tmpStr,vectorParams[1]);
1291
p.key=KEY_SCALEFACTOR_ANISOTROPIC;
1292
p.name=TRANS("Scale Fact.");
1294
p.type=PROPERTY_TYPE_REAL;
1295
p.helpText=TRANS("Enlargement factor for scaling around origin");
1296
propertyList.addProperty(p,curGroup);
1302
ASSERT(vectorParams.size() == 2);
1303
ASSERT(scalarParams.size() == 1);
1304
if(originMode == ORIGINMODE_SELECT)
1306
stream_cast(tmpStr,vectorParams[0]);
1308
p.name=TRANS("Origin");
1310
p.type=PROPERTY_TYPE_POINT3D;
1311
p.helpText=TRANS("Origin of rotation");
1312
propertyList.addProperty(p,curGroup);
1314
stream_cast(tmpStr,vectorParams[1]);
1315
p.key=KEY_ROTATE_AXIS;
1316
p.name=TRANS("Axis");
1318
p.type=(PROPERTY_TYPE_POINT3D);
1319
p.helpText=TRANS("Axis around which to revolve");
1320
propertyList.addProperty(p,curGroup);
1322
stream_cast(tmpStr,scalarParams[0]);
1323
p.key=KEY_ROTATE_ANGLE;
1324
p.name=TRANS("Angle (deg)");
1326
p.type=PROPERTY_TYPE_REAL;
1327
p.helpText=TRANS("Angle to perform rotation (ACW, as viewed from axis towards origin)");
1328
propertyList.addProperty(p,curGroup);
1331
case MODE_VALUE_SHUFFLE:
1337
case MODE_SPATIAL_NOISE:
1339
for(unsigned int ui=0;ui<NOISETYPE_END;ui++)
1340
choices.push_back(make_pair(ui,getNoiseTypeString(ui)));
1341
tmpStr= choiceString(choices,noiseType);
1344
p.name=TRANS("Noise Type");
1346
p.type=PROPERTY_TYPE_CHOICE;
1347
p.helpText=TRANS("Method to use to degrade point data");
1348
p.key=KEY_NOISETYPE;
1349
propertyList.addProperty(p,curGroup);
1352
stream_cast(tmpStr,scalarParams[0]);
1353
if(noiseType == NOISETYPE_WHITE)
1354
p.name=TRANS("Noise level");
1355
else if(noiseType == NOISETYPE_GAUSSIAN)
1356
p.name=TRANS("Standard dev.");
1363
p.type=PROPERTY_TYPE_REAL;
1364
p.helpText=TRANS("Amplitude of noise");
1365
p.key=KEY_NOISELEVEL;
1366
propertyList.addProperty(p,curGroup);
1376
propertyList.setGroupTitle(curGroup,TRANS("Transform Params"));
1380
bool TransformFilter::setProperty( unsigned int key,
1381
const std::string &value, bool &needUpdate)
1390
for(size_t ui=0;ui<MODE_ENUM_END;ui++)
1392
if(value == TRANS(TRANSFORM_MODE_STRING[ui]))
1397
ASSERT(false); // This should not happen
1402
vectorParams.clear();
1403
scalarParams.clear();
1404
switch(transformMode)
1406
case MODE_SCALE_ISOTROPIC:
1407
vectorParams.push_back(Point3D(0,0,0));
1408
scalarParams.push_back(1.0f);
1410
case MODE_SCALE_ANISOTROPIC:
1411
vectorParams.push_back(Point3D(0,0,0));
1412
vectorParams.push_back(Point3D(1,1,1));
1414
case MODE_TRANSLATE:
1415
vectorParams.push_back(Point3D(0,0,0));
1417
case MODE_TRANSLATE_VALUE:
1418
scalarParams.push_back(100.0f);
1421
vectorParams.push_back(Point3D(0,0,0));
1422
vectorParams.push_back(Point3D(1,0,0));
1423
scalarParams.push_back(0.0f);
1425
case MODE_VALUE_SHUFFLE:
1427
case MODE_SPATIAL_NOISE:
1428
scalarParams.push_back(0.1f);
1437
//The rotation angle, and the scale factor are both stored
1438
//in scalaraparams[0]. All we need ot do is set that,
1439
//as either can take any valid floating pt value
1440
case KEY_ROTATE_ANGLE:
1441
case KEY_SCALEFACTOR:
1442
case KEY_NOISELEVEL:
1443
case KEY_ORIGIN_VALUE:
1445
ASSERT(scalarParams.size());
1448
if(stream_cast(newScale,value))
1451
if(scalarParams[0] != newScale )
1453
scalarParams[0] = newScale;
1459
case KEY_SCALEFACTOR_ANISOTROPIC:
1462
if(!newPt.parse(value))
1465
if(!(vectorParams[1] == newPt ))
1467
vectorParams[1] = newPt;
1477
if(!newPt.parse(value))
1480
if(!(vectorParams[0] == newPt ))
1482
vectorParams[0] = newPt;
1489
case KEY_ROTATE_AXIS:
1491
ASSERT(vectorParams.size() ==2);
1492
ASSERT(scalarParams.size() ==1);
1494
if(!newPt.parse(value))
1497
if(newPt.sqrMag() < std::numeric_limits<float>::epsilon())
1500
if(!(vectorParams[1] == newPt ))
1502
vectorParams[1] = newPt;
1509
case KEY_ORIGINMODE:
1512
for (i = 0; i < ORIGINMODE_END; i++)
1513
if (value == TRANS(getOriginTypeString(i).c_str())) break;
1515
if( i == ORIGINMODE_END)
1526
case KEY_TRANSFORM_SHOWORIGIN:
1528
string stripped=stripWhite(value);
1530
if(!(stripped == "1"|| stripped == "0"))
1533
showOrigin=(stripped=="1");
1542
for (i = 0; i < NOISETYPE_END; i++)
1543
if (value == TRANS(getNoiseTypeString(i).c_str())) break;
1545
if( i == NOISETYPE_END)
1563
std::string TransformFilter::getErrString(unsigned int code) const
1568
//User aborted in a callback
1569
case ERR_CALLBACK_FAIL:
1570
return std::string(TRANS("Aborted"));
1571
//Caught a memory issue
1573
return std::string(TRANS("Unable to allocate memory"));
1578
bool TransformFilter::writeState(std::ostream &f,unsigned int format, unsigned int depth) const
1583
case STATE_FORMAT_XML:
1585
f << tabs(depth) << "<" << trueName() << ">" << endl;
1586
f << tabs(depth+1) << "<userstring value=\""<< escapeXML(userString) << "\"/>" << endl;
1587
f << tabs(depth+1) << "<transformmode value=\"" << transformMode<< "\"/>"<<endl;
1588
f << tabs(depth+1) << "<originmode value=\"" << originMode<< "\"/>"<<endl;
1590
f << tabs(depth+1) << "<noisetype value=\"" << noiseType<< "\"/>"<<endl;
1597
f << tabs(depth+1) << "<showorigin value=\"" << tmpStr << "\"/>"<<endl;
1598
writeVectorsXML(f,"vectorparams",vectorParams,depth);
1599
writeScalarsXML(f,"scalarparams",scalarParams,depth);
1600
f << tabs(depth) << "</" << trueName() << ">" << endl;
1611
bool TransformFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileDir)
1613
//Retrieve user string
1615
if(XMLHelpFwdToElem(nodePtr,"userstring"))
1618
xmlChar *xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
1621
userString=(char *)xmlString;
1625
//Retrieve transformation type
1627
if(!XMLGetNextElemAttrib(nodePtr,transformMode,"transformmode","value"))
1629
if(transformMode>= MODE_ENUM_END)
1633
//Retrieve origination type
1635
if(!XMLGetNextElemAttrib(nodePtr,originMode,"originmode","value"))
1637
if(originMode>= ORIGINMODE_END)
1641
//Retrieve origination type
1643
if(!XMLGetNextElemAttrib(nodePtr,originMode,"noisetype","value"))
1645
if(noiseType>= NOISETYPE_END)
1649
//Retrieve origination type
1651
if(!XMLGetNextElemAttrib(nodePtr,originMode,"showorigin","value"))
1655
//Retrieve vector parameters
1657
if(XMLHelpFwdToElem(nodePtr,"vectorparams"))
1659
xmlNodePtr tmpNode=nodePtr;
1661
if(!readVectorsXML(nodePtr,vectorParams))
1666
//Retrieve scalar parameters
1668
if(XMLHelpFwdToElem(nodePtr,"scalarparams"))
1671
if(!readScalarsXML(nodePtr,scalarParams))
1675
//Check the scalar params match the selected primitive
1676
switch(transformMode)
1678
case MODE_TRANSLATE:
1679
if(vectorParams.size() != 1 || scalarParams.size() !=0)
1682
case MODE_SCALE_ISOTROPIC:
1683
if(vectorParams.size() != 1 || scalarParams.size() !=1)
1686
case MODE_SCALE_ANISOTROPIC:
1687
if(vectorParams.size() != 2 || scalarParams.size() !=0)
1691
if(vectorParams.size() != 2 || scalarParams.size() !=1)
1694
case MODE_TRANSLATE_VALUE:
1695
if(vectorParams.size() != 0 || scalarParams.size() !=1)
1698
case MODE_VALUE_SHUFFLE:
1699
case MODE_SPATIAL_NOISE:
1709
unsigned int TransformFilter::getRefreshBlockMask() const
1711
//Only ions cannot go through this filter.
1712
return STREAM_TYPE_IONS;
1715
unsigned int TransformFilter::getRefreshEmitMask() const
1718
return STREAM_TYPE_IONS | STREAM_TYPE_DRAW;
1720
return STREAM_TYPE_IONS;
1723
unsigned int TransformFilter::getRefreshUseMask() const
1725
return STREAM_TYPE_IONS;
1728
void TransformFilter::setPropFromBinding(const SelectionBinding &b)
1732
case BINDING_SPHERE_ORIGIN:
1733
b.getValue(vectorParams[0]);
1741
std::string TransformFilter::getOriginTypeString(unsigned int i)
1743
ASSERT(i<ORIGINMODE_END);
1744
return TRANSFORM_ORIGIN_STRING[i];
1747
std::string TransformFilter::getNoiseTypeString(unsigned int i)
1751
case NOISETYPE_WHITE:
1752
return std::string(TRANS("White"));
1753
case NOISETYPE_GAUSSIAN:
1754
return std::string(TRANS("Gaussian"));
1762
//Generate some synthetic data points, that lie within 0->span.
1763
//span must be a 3-wide array, and numPts will be generated.
1764
//each entry in the array should be coprime for optimal results.
1765
//filter pointer must be deleted.
1766
IonStreamData *synthDataPoints(unsigned int span[],unsigned int numPts);
1768
bool translateTest();
1770
bool scaleAnisoTest();
1776
inline bool operator()(const IonHit &h1,const IonHit &h2) const
1777
{return h1.getMassToCharge()<h2.getMassToCharge();};
1780
bool TransformFilter::runUnitTests()
1785
if(!translateTest())
1791
if(!scaleAnisoTest())
1800
IonStreamData *synthDataPoints(unsigned int span[], unsigned int numPts)
1802
IonStreamData *d = new IonStreamData;
1804
for(unsigned int ui=0;ui<numPts;ui++)
1807
h.setPos(Point3D(ui%span[0],
1808
ui%span[1],ui%span[2]));
1809
h.setMassToCharge(ui);
1810
d->data.push_back(h);
1818
//Simulate some data to send to the filter
1819
vector<const FilterStreamData*> streamIn,streamOut;
1820
IonStreamData *d= new IonStreamData;
1825
const unsigned int NUM_PTS=10000;
1828
//Build a sphere of data points
1829
//by rejection method
1830
d->data.reserve(NUM_PTS/2);
1831
for(unsigned int ui=0;ui<NUM_PTS;ui++)
1834
tmp=Point3D(rng.genUniformDev()-0.5f,
1835
rng.genUniformDev()-0.5f,
1836
rng.genUniformDev()-0.5f);
1838
if(tmp.sqrMag() < 1.0f)
1842
h.setMassToCharge(1);
1843
d->data.push_back(h);
1847
streamIn.push_back(d);
1849
//Set up the filter itself
1851
TransformFilter *f=new TransformFilter;
1852
f->setCaching(false);
1857
TEST(f->setProperty(KEY_MODE,
1858
TRANS(TRANSFORM_MODE_STRING[MODE_ROTATE]),needUp),"Set transform mode");
1860
tmpVal=rng.genUniformDev()*M_PI*2.0;
1861
stream_cast(s,tmpVal);
1862
TEST(f->setProperty(KEY_ROTATE_ANGLE,s,needUp),"Set rotate angle");
1865
//NOTE: Technically there is a nonzero chance of this failing.
1866
tmpPt=Point3D(rng.genUniformDev()-0.5f,
1867
rng.genUniformDev()-0.5f,
1868
rng.genUniformDev()-0.5f);
1869
stream_cast(s,tmpPt);
1870
TEST(f->setProperty(KEY_ROTATE_AXIS,s,needUp),"set rotate axis");
1872
TEST(f->setProperty(KEY_ORIGINMODE,
1873
TRANS(TRANSFORM_ORIGIN_STRING[ORIGINMODE_MASSCENTRE]),needUp),"Set origin");
1874
TEST(f->setProperty(KEY_TRANSFORM_SHOWORIGIN,"0",needUp),"Set no-show origin");
1878
//OK, so now do the rotation
1881
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
1884
TEST(streamOut.size() == 1,"stream count");
1885
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
1886
TEST(streamOut[0]->getNumBasicObjects() == d->data.size(),"Ion count invariance");
1888
const IonStreamData *outData=(IonStreamData*)streamOut[0];
1890
Point3D massCentre[2];
1891
massCentre[0]=massCentre[1]=Point3D(0,0,0);
1892
//Now check that the mass centre has not moved
1893
for(unsigned int ui=0;ui<d->data.size();ui++)
1894
massCentre[0]+=d->data[ui].getPos();
1896
for(unsigned int ui=0;ui<outData->data.size();ui++)
1897
massCentre[1]+=outData->data[ui].getPos();
1900
TEST((massCentre[0]-massCentre[1]).sqrMag() <
1901
2.0*sqrt(std::numeric_limits<float>::epsilon()),"mass centre invariance");
1903
//Rotating a sphere around its centre of mass
1904
// should not massively change the bounding box
1905
// however we don't quite have a sphere, so we could have (at the most extreme,
1908
IonHit::getBoundCube(d->data,bc[0]);
1909
IonHit::getBoundCube(outData->data,bc[1]);
1912
volumeRat = bc[0].volume()/bc[1].volume();
1914
TEST(volumeRat > 0.5f && volumeRat < 2.0f, "volume ratio test");
1916
delete streamOut[0];
1921
bool translateTest()
1926
//Simulate some data to send to the filter
1927
vector<const FilterStreamData*> streamIn,streamOut;
1929
const unsigned int NUM_PTS=10000;
1931
unsigned int span[]={
1934
d=synthDataPoints(span,NUM_PTS);
1935
streamIn.push_back(d);
1939
//Set up the filter itself
1941
TransformFilter *f=new TransformFilter;
1945
TEST(f->setProperty(KEY_MODE,
1946
TRANSFORM_MODE_STRING[MODE_TRANSLATE],needUp),"set translate mode");
1948
//NOTE: Technically there is a nonzero chance of this failing.
1949
offsetPt=Point3D(rng.genUniformDev()-0.5f,
1950
rng.genUniformDev()-0.5f,
1951
rng.genUniformDev()-0.5f);
1952
offsetPt[0]*=span[0];
1953
offsetPt[1]*=span[1];
1954
offsetPt[2]*=span[2];
1956
stream_cast(s,offsetPt);
1957
TEST(f->setProperty(KEY_ORIGIN,s,needUp),"Set Origin");
1958
TEST(f->setProperty(KEY_TRANSFORM_SHOWORIGIN,"0",needUp),"Set display origin");
1963
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
1966
TEST(streamOut.size() == 1,"stream count");
1967
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
1968
TEST(streamOut[0]->getNumBasicObjects() == d->data.size(),"Ion count invariance");
1970
const IonStreamData *outData=(IonStreamData*)streamOut[0];
1972
//Bound cube should move exactly as per the translation
1974
IonHit::getBoundCube(d->data,bc[0]);
1975
IonHit::getBoundCube(outData->data,bc[1]);
1977
for(unsigned int ui=0;ui<3;ui++)
1979
for(unsigned int uj=0;uj<2;uj++)
1982
f=bc[0].getBound(ui,uj) -bc[1].getBound(ui,uj);
1983
TEST(fabs(f-offsetPt[ui]) < sqrt(std::numeric_limits<float>::epsilon()), "bound translation");
1988
delete streamOut[0];
1996
//Simulate some data to send to the filter
1997
vector<const FilterStreamData*> streamIn,streamOut;
2003
const unsigned int NUM_PTS=10000;
2005
unsigned int span[]={
2008
d=synthDataPoints(span,NUM_PTS);
2009
streamIn.push_back(d);
2011
//Set up the filter itself
2013
TransformFilter *f=new TransformFilter;
2017
//Switch to scale mode (isotropic)
2018
TEST(f->setProperty(KEY_MODE,
2019
TRANS(TRANSFORM_MODE_STRING[MODE_SCALE_ISOTROPIC]),needUp),"Set scale mode");
2022
//Switch to mass-centre origin
2023
TEST(f->setProperty(KEY_ORIGINMODE,
2024
TRANS(TRANSFORM_ORIGIN_STRING[ORIGINMODE_MASSCENTRE]),needUp),"Set origin->mass mode");
2027
//Pick some scale, both positive and negative.
2028
if(rng.genUniformDev() > 0.5)
2029
scaleFact=rng.genUniformDev()*10;
2031
scaleFact=0.1f/(0.1f+rng.genUniformDev());
2033
stream_cast(s,scaleFact);
2035
TEST(f->setProperty(KEY_SCALEFACTOR,s,needUp),"Set scalefactor");
2036
//Don't show origin marker
2037
TEST(f->setProperty(KEY_TRANSFORM_SHOWORIGIN,"0",needUp),"Set show origin")
2043
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
2046
TEST(streamOut.size() == 1,"stream count");
2047
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
2048
TEST(streamOut[0]->getNumBasicObjects() == d->data.size(),"Ion count invariance");
2050
const IonStreamData *outData=(IonStreamData*)streamOut[0];
2052
//Scaling around its centre of mass
2053
// should scale the bounding box by the cube of the scale factor
2055
IonHit::getBoundCube(d->data,bc[0]);
2056
IonHit::getBoundCube(outData->data,bc[1]);
2058
float cubeOfScale=scaleFact*scaleFact*scaleFact;
2061
volumeDelta=fabs(bc[1].volume()/cubeOfScale - bc[0].volume() );
2063
TEST(volumeDelta < 100.0f*sqrt(std::numeric_limits<float>::epsilon()), "scaled volume test");
2065
delete streamOut[0];
2070
bool scaleAnisoTest()
2072
//Simulate some data to send to the filter
2073
vector<const FilterStreamData*> streamIn,streamOut;
2079
const unsigned int NUM_PTS=10000;
2081
unsigned int span[]={
2084
d=synthDataPoints(span,NUM_PTS);
2085
streamIn.push_back(d);
2087
//Set up the filter itself
2089
TransformFilter *f=new TransformFilter;
2093
//Switch to scale mode (isotropic)
2094
TEST(f->setProperty(KEY_MODE,
2095
TRANS(TRANSFORM_MODE_STRING[MODE_SCALE_ANISOTROPIC]),needUp),"Set scale mode");
2098
//Switch to mass-centre origin
2099
TEST(f->setProperty(KEY_ORIGINMODE,
2100
TRANS(TRANSFORM_ORIGIN_STRING[ORIGINMODE_MASSCENTRE]),needUp),"Set origin->mass mode");
2103
//Pick some random scale vector, both positive and negative.
2104
scaleFact=Point3D(rng.genUniformDev()*10,rng.genUniformDev()*10,rng.genUniformDev()*10);
2106
stream_cast(s,scaleFact);
2108
TEST(f->setProperty(KEY_SCALEFACTOR_ANISOTROPIC,s,needUp),"Set scalefactor");
2109
//Don't show origin marker
2110
TEST(f->setProperty(KEY_TRANSFORM_SHOWORIGIN,"0",needUp),"Set show origin")
2116
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
2119
TEST(streamOut.size() == 1,"stream count");
2120
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
2121
TEST(streamOut[0]->getNumBasicObjects() == d->data.size(),"Ion count invariance");
2123
delete streamOut[0];
2129
//Simulate some data to send to the filter
2130
vector<const FilterStreamData*> streamIn,streamOut;
2136
const unsigned int NUM_PTS=1000;
2138
unsigned int span[]={
2141
d=synthDataPoints(span,NUM_PTS);
2142
streamIn.push_back(d);
2144
//Set up the filter itself
2146
TransformFilter *f=new TransformFilter;
2149
//Switch to shuffle mode
2150
TEST(f->setProperty(KEY_MODE,
2151
TRANS(TRANSFORM_MODE_STRING[MODE_VALUE_SHUFFLE]),needUp),"refresh error code");
2155
//OK, so now run the shuffle
2158
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
2161
TEST(streamOut.size() == 1,"stream count");
2162
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
2163
TEST(streamOut[0]->getNumBasicObjects() == d->data.size(),"Ion count invariance");
2165
TEST(streamOut[0]->getNumBasicObjects() == d->data.size(),"Ion count invariance");
2167
IonStreamData *outData=(IonStreamData*)streamOut[0];
2169
//Check to see that the output masses each exist in the input,
2170
//but are not in the same sequence
2174
bool sequenceDifferent=false;
2175
for(size_t ui=0;ui<d->data.size();ui++)
2177
if(d->data[ui].getMassToCharge() != outData->data[ui].getMassToCharge())
2179
sequenceDifferent=true;
2183
TEST(sequenceDifferent,
2184
"Should be shuffled - Prob. of sequence being identical in both orig & shuffled cases is very low");
2187
std::sort(outData->data.begin(),outData->data.end(),cmp);
2188
std::sort(d->data.begin(),d->data.end(),cmp);
2191
for(size_t ui=0;ui<d->data.size();ui++)
2193
TEST(d->data[ui].getMassToCharge() == outData->data[ui].getMassToCharge(),"Shuffle + Sort mass should be the same");
2199
delete streamOut[0];