2
* voxelise.cpp - Compute 3D binning (voxelisation) of 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/>.
21
#include "filterCommon.h"
40
KEY_VOXEL_REPRESENTATION_MODE,
42
KEY_FILTER_BOUNDARY_MODE,
45
KEY_ENABLE_DENOMINATOR
48
//!Normalisation method
51
VOXELISE_NORMALISETYPE_NONE,// straight count
52
VOXELISE_NORMALISETYPE_VOLUME,// density
53
VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL, // concentration
54
VOXELISE_NORMALISETYPE_COUNT2INVOXEL,// ratio count1/count2
55
VOXELISE_NORMALISETYPE_MAX // keep this at the end so it's a bookend for the last value
61
VOXELISE_FILTERTYPE_NONE,
62
VOXELISE_FILTERTYPE_GAUSS,
63
VOXELISE_FILTERTYPE_MAX // keep this at the end so it's a bookend for the last value
67
//Boundary behaviour for filtering
70
VOXELISE_FILTERBOUNDMODE_ZERO,
71
VOXELISE_FILTERBOUNDMODE_BOUNCE,
72
VOXELISE_FILTERBOUNDMODE_MAX// keep this at the end so it's a bookend for the last value
79
VOXELISE_CONVOLVE_ERR,
80
VOXELISE_BOUNDS_INVALID_ERR
83
const char *NORMALISE_TYPE_STRING[] = {
84
NTRANS("None (Raw count)"),
85
NTRANS("Volume (Density)"),
86
NTRANS("All Ions (conc)"),
87
NTRANS("Ratio (Num/Denom)"),
90
const char *REPRESENTATION_TYPE_STRING[] = {
91
NTRANS("Point Cloud"),
95
const char *VOXELISE_FILTER_TYPE_STRING[]={
97
NTRANS("Gaussian (2𝜎)"),
100
const char *VOXELISE_FILTER_BOUND_STRING[] ={
105
//This is not a member of voxels.h, as the voxels do not have any concept of the IonHit
106
int countPoints(Voxels<float> &v, const std::vector<IonHit> &points,
107
bool noWrap,bool (*callback)(bool))
112
v.getSize(binCount[0],binCount[1],binCount[2]);
114
unsigned int downSample=MAX_CALLBACK;
115
for (size_t ui=0; ui<points.size(); ui++)
119
if(!(*callback)(false))
121
downSample=MAX_CALLBACK;
123
v.getIndexWithUpper(x,y,z,points[ui].getPos());
124
//Ensure it lies within the dataset
125
if (x < binCount[0] && y < binCount[1] && z< binCount[2])
129
value=v.getData(x,y,z)+1.0f;
131
//Prevent wrap-around errors
133
if (value > v.getData(x,y,z))
134
v.setData(x,y,z,value);
136
v.setData(x,y,z,value);
144
// == Voxels filter ==
145
VoxeliseFilter::VoxeliseFilter()
146
: fixedWidth(false), normaliseType(VOXELISE_NORMALISETYPE_NONE)
148
COMPILE_ASSERT(THREEDEP_ARRAYSIZE(NORMALISE_TYPE_STRING) == VOXELISE_NORMALISETYPE_MAX);
149
COMPILE_ASSERT(THREEDEP_ARRAYSIZE(VOXELISE_FILTER_TYPE_STRING) == VOXELISE_FILTERTYPE_MAX );
150
COMPILE_ASSERT(THREEDEP_ARRAYSIZE(VOXELISE_FILTER_BOUND_STRING) == VOXELISE_FILTERBOUNDMODE_MAX);
152
COMPILE_ASSERT(THREEDEP_ARRAYSIZE(REPRESENTATION_TYPE_STRING) == VOXEL_REPRESENT_END);
160
filterMode=VOXELISE_FILTERTYPE_NONE;
161
filterBoundaryMode=VOXELISE_FILTERBOUNDMODE_BOUNCE;
164
representation=VOXEL_REPRESENT_POINTCLOUD;
168
bc.setBounds(Point3D(0,0,0),Point3D(1,1,1));
170
for (unsigned int i = 0; i < INDEX_LENGTH; i++)
173
calculateWidthsFromNumBins(binWidth,nBins);
175
numeratorAll = false;
176
denominatorAll = true;
179
cache=true; //By default, we should cache, but decision is made higher up
186
Filter *VoxeliseFilter::cloneUncached() const
188
VoxeliseFilter *p=new VoxeliseFilter();
189
p->splatSize=splatSize;
195
p->isoLevel=isoLevel;
197
p->filterMode=filterMode;
198
p->filterBoundaryMode=filterBoundaryMode;
199
p->filterBins=filterBins;
200
p->gaussDev=gaussDev;
202
p->representation=representation;
203
p->splatSize=splatSize;
205
p->normaliseType=normaliseType;
206
p->numeratorAll=numeratorAll;
207
p->denominatorAll=denominatorAll;
211
for(size_t ui=0;ui<INDEX_LENGTH;ui++)
213
p->nBins[ui] = nBins[ui];
214
p->binWidth[ui] = binWidth[ui];
217
p->enabledIons[0].resize(enabledIons[0].size());
218
std::copy(enabledIons[0].begin(),enabledIons[0].end(),p->enabledIons[0].begin());
220
p->enabledIons[1].resize(enabledIons[1].size());
221
std::copy(enabledIons[1].begin(),enabledIons[1].end(),p->enabledIons[1].begin());
225
p->rsdIncoming=new RangeStreamData();
226
*(p->rsdIncoming) = *rsdIncoming;
233
p->userString=userString;
237
size_t VoxeliseFilter::numBytesForCache(size_t nObjects) const
239
//if we are using fixed width, we know the answer.
240
//otherwise we dont until we are presented with the boundcube.
241
//TODO: Modify the function description to pass in the boundcube
243
return nBins[0]*nBins[1]*nBins[2]*sizeof(float);
248
void VoxeliseFilter::initFilter(const std::vector<const FilterStreamData *> &dataIn,
249
std::vector<const FilterStreamData *> &dataOut)
251
const RangeStreamData *c=0;
252
//Determine if we have an incoming range
253
for (size_t i = 0; i < dataIn.size(); i++)
255
if(dataIn[i]->getStreamType() == STREAM_TYPE_RANGE)
257
c=(const RangeStreamData *)dataIn[i];
263
//we no longer (or never did) have any incoming ranges. Not much to do
266
//delete the old incoming range pointer
271
enabledIons[0].clear(); //clear numerator options
272
enabledIons[1].clear(); //clear denominator options
274
//Prevent normalisation type being set incorrectly
275
// if we have no incoming range data
276
if(normaliseType == VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL || normaliseType == VOXELISE_NORMALISETYPE_COUNT2INVOXEL)
277
normaliseType= VOXELISE_NORMALISETYPE_NONE;
283
//If we didn't have an incoming rsd, then make one up!
286
rsdIncoming = new RangeStreamData;
289
//set the numerator to all disabled
290
enabledIons[0].resize(rsdIncoming->rangeFile->getNumIons(),0);
291
//set the denominator to have all enabled
292
enabledIons[1].resize(rsdIncoming->rangeFile->getNumIons(),1);
297
//OK, so we have a range incoming already (from last time)
298
//-- the question is, is it the same
300
//Do a pointer comparison (its a hack, yes, but it should work)
301
if(rsdIncoming->rangeFile != c->rangeFile)
303
//hmm, it is different. well, trash the old incoming rng
306
rsdIncoming = new RangeStreamData;
309
//set the numerator to all disabled
310
enabledIons[0].resize(rsdIncoming->rangeFile->getNumIons(),0);
311
//set the denominator to have all enabled
312
enabledIons[1].resize(rsdIncoming->rangeFile->getNumIons(),1);
319
unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
320
std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
322
for(size_t ui=0;ui<dataIn.size();ui++)
324
//Disallow copying of anything in the blockmask. Copy everything else
325
if(!(dataIn[ui]->getStreamType() & getRefreshBlockMask() ))
326
getOut.push_back(dataIn[ui]);
329
//use the cached copy if we have it.
332
for(size_t ui=0;ui<filterOutputs.size();ui++)
333
getOut.push_back(filterOutputs[ui]);
340
bc.setInverseLimits();
342
for (size_t i = 0; i < dataIn.size(); i++)
344
//Check for ion stream types. Block others from propagation.
345
if (dataIn[i]->getStreamType() != STREAM_TYPE_IONS) continue;
347
const IonStreamData *is = (const IonStreamData *)dataIn[i];
348
//Don't work on empty or single object streams (bounding box needs to be defined)
349
if (is->getNumBasicObjects() < 2) continue;
352
bcTmp=getIonDataLimits(is->data);
354
//Bounds could be invalid if, for example, we had coplanar axis aligned points
355
if (!bcTmp.isValid()) continue;
359
//No bounding box? Tough cookies
360
if (!bc.isValid() || bc.isFlat()) return VOXELISE_BOUNDS_INVALID_ERR;
362
bc.getBounds(minP,maxP);
364
calculateNumBinsFromWidths(binWidth, nBins);
366
calculateWidthsFromNumBins(binWidth, nBins);
368
//Disallow empty bounding boxes (ie, produce no output)
372
VoxelStreamData *vs = new VoxelStreamData();
374
vs->data.setCallbackMethod(callback);
375
vs->data.init(nBins[0], nBins[1], nBins[2], bc);
376
vs->representationType= representation;
377
vs->splatSize = splatSize;
378
vs->isoLevel=isoLevel;
385
Voxels<float> vsDenom;
386
if (normaliseType == VOXELISE_NORMALISETYPE_COUNT2INVOXEL ||
387
normaliseType == VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL) {
388
//Check we actually have incoming data
390
vsDenom.setCallbackMethod(callback);
391
vsDenom.init(nBins[0], nBins[1], nBins[2], bc);
395
const IonStreamData *is;
399
for (size_t i = 0; i < dataIn.size(); i++)
402
//Check for ion stream types. Don't use anything else in counting
403
if (dataIn[i]->getStreamType() != STREAM_TYPE_IONS) continue;
405
is= (const IonStreamData *)dataIn[i];
408
//Count the numerator ions
411
//Check what Ion type this stream belongs to. Assume all ions
412
//in the stream belong to the same group
414
ionID = getIonstreamIonID(is,rsdIncoming->rangeFile);
417
if(ionID!=(unsigned int)-1)
418
thisIonEnabled=enabledIons[0][ionID];
420
thisIonEnabled=false;
424
countPoints(vs->data,is->data,true,callback);
428
//If the user requests normalisation, compute the denominator dataset
429
if (normaliseType == VOXELISE_NORMALISETYPE_COUNT2INVOXEL) {
432
//Check what Ion type this stream belongs to. Assume all ions
433
//in the stream belong to the same group
435
ionID = rsdIncoming->rangeFile->getIonID(is->data[0].getMassToCharge());
438
if(ionID!=(unsigned int)-1)
439
thisIonEnabled=enabledIons[1][ionID];
441
thisIonEnabled=false;
444
countPoints(vsDenom,is->data,true,callback);
446
} else if (normaliseType == VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL)
448
countPoints(vsDenom,is->data,true,callback);
451
if(!(*callback)(false))
454
return VOXELISE_ABORT_ERR;
458
//Perform normalsiation
459
if (normaliseType == VOXELISE_NORMALISETYPE_VOLUME)
460
vs->data.calculateDensity();
461
else if (normaliseType == VOXELISE_NORMALISETYPE_COUNT2INVOXEL ||
462
normaliseType == VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL)
467
//No range data. Just count
468
for (size_t i = 0; i < dataIn.size(); i++)
471
if(dataIn[i]->getStreamType() == STREAM_TYPE_IONS)
473
is= (const IonStreamData *)dataIn[i];
475
countPoints(vs->data,is->data,true,callback);
477
if(!(*callback)(false))
480
return VOXELISE_ABORT_ERR;
485
ASSERT(normaliseType != VOXELISE_NORMALISETYPE_COUNT2INVOXEL
486
&& normaliseType!=VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL);
487
if (normaliseType == VOXELISE_NORMALISETYPE_VOLUME)
488
vs->data.calculateDensity();
494
//Perform voxel filtering
497
case VOXELISE_FILTERTYPE_NONE:
499
case VOXELISE_FILTERTYPE_GAUSS:
501
Voxels<float> kernel,res;
503
map<unsigned int, unsigned int> modeMap;
506
modeMap[VOXELISE_FILTERBOUNDMODE_ZERO]=BOUND_ZERO;
507
modeMap[VOXELISE_FILTERBOUNDMODE_BOUNCE]=BOUND_MIRROR;
509
//FIXME: This will be SLOW. need to use IIR or some other
512
//Construct the gaussian convolution
513
kernel.setGaussianKernelCube(gaussDev,(float)filterBins,filterBins);
514
//Normalise the kernel
519
if(res.resize(vs->data))
520
return VOXELISE_MEMORY_ERR;
522
//Gaussian kernel is separable (rank 1)
523
if(vs->data.separableConvolve(kernel,res,modeMap[filterBoundaryMode]))
524
return VOXELISE_CONVOLVE_ERR;
537
vs->data.minMax(min,max);
541
stream_cast(sMin,min);
542
stream_cast(sMax,max);
543
consoleOutput.push_back(std::string(TRANS("Voxel Limits (min,max): (") + sMin + string(","))
549
filterOutputs.push_back(vs);
555
//Store the voxels on the output
556
getOut.push_back(vs);
558
//Copy the inputs into the outputs, provided they are not voxels
562
std::string VoxeliseFilter::getNormaliseTypeString(int type){
563
ASSERT(type < VOXELISE_NORMALISETYPE_MAX);
564
return TRANS(NORMALISE_TYPE_STRING[type]);
567
std::string VoxeliseFilter::getRepresentTypeString(int type) {
568
ASSERT(type<VOXEL_REPRESENT_END);
569
return std::string(TRANS(REPRESENTATION_TYPE_STRING[type]));
572
std::string VoxeliseFilter::getFilterTypeString(int type)
574
ASSERT(type < VOXELISE_FILTERTYPE_MAX);
575
return std::string(TRANS(VOXELISE_FILTER_TYPE_STRING[type]));
579
std::string VoxeliseFilter::getFilterBoundTypeString(int type)
581
ASSERT(type < VOXELISE_FILTERBOUNDMODE_MAX);
582
return std::string(TRANS(VOXELISE_FILTER_BOUND_STRING[type]));
585
void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
591
stream_cast(tmpStr, fixedWidth);
592
p.name=TRANS("Fixed width");
594
p.key=KEY_FIXEDWIDTH;
595
p.type=PROPERTY_TYPE_BOOL;
596
p.helpText=TRANS("If true, use fixed size voxels, otherwise use fixed count");
597
propertyList.addProperty(p,curGroup);
601
stream_cast(tmpStr,binWidth[0]);
602
p.name=TRANS("Bin width x");
604
p.key=KEY_WIDTHBINSX;
605
p.type=PROPERTY_TYPE_REAL;
606
p.helpText=TRANS("Voxel size in X direction");
607
propertyList.addProperty(p,curGroup);
609
stream_cast(tmpStr,binWidth[1]);
610
p.name=TRANS("Bin width y");
612
p.type=PROPERTY_TYPE_REAL;
613
p.helpText=TRANS("Voxel size in Y direction");
614
p.key=KEY_WIDTHBINSY;
615
propertyList.addProperty(p,curGroup);
618
stream_cast(tmpStr,binWidth[2]);
619
p.name=TRANS("Bin width z");
621
p.type=PROPERTY_TYPE_REAL;
622
p.helpText=TRANS("Voxel size in Z direction");
623
p.key=KEY_WIDTHBINSZ;
624
propertyList.addProperty(p,curGroup);
628
stream_cast(tmpStr,nBins[0]);
629
p.name=TRANS("Num bins x");
632
p.type=PROPERTY_TYPE_INTEGER;
633
p.helpText=TRANS("Number of voxels to use in X direction");
634
propertyList.addProperty(p,curGroup);
636
stream_cast(tmpStr,nBins[1]);
638
p.name=TRANS("Num bins y");
640
p.type=PROPERTY_TYPE_INTEGER;
641
p.helpText=TRANS("Number of voxels to use in Y direction");
642
propertyList.addProperty(p,curGroup);
644
stream_cast(tmpStr,nBins[2]);
647
p.name=TRANS("Num bins z");
648
p.type=PROPERTY_TYPE_INTEGER;
649
p.helpText=TRANS("Number of voxels to use in Z direction");
650
propertyList.addProperty(p,curGroup);
653
//Let the user know what the valid values for voxel value types are
654
vector<pair<unsigned int,string> > choices;
655
tmpStr=getNormaliseTypeString(VOXELISE_NORMALISETYPE_NONE);
656
choices.push_back(make_pair((unsigned int)VOXELISE_NORMALISETYPE_NONE,tmpStr));
657
tmpStr=getNormaliseTypeString(VOXELISE_NORMALISETYPE_VOLUME);
658
choices.push_back(make_pair((unsigned int)VOXELISE_NORMALISETYPE_VOLUME,tmpStr));
662
tmpStr=getNormaliseTypeString(VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL);
663
choices.push_back(make_pair((unsigned int)VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL,tmpStr));
664
//Ratio is only valid if we have a way of separation for the ions i.e. range
665
tmpStr=getNormaliseTypeString(VOXELISE_NORMALISETYPE_COUNT2INVOXEL);
666
choices.push_back(make_pair((unsigned int)VOXELISE_NORMALISETYPE_COUNT2INVOXEL,tmpStr));
669
tmpStr= choiceString(choices,normaliseType);
670
p.name=TRANS("Normalise by");
672
p.type=PROPERTY_TYPE_CHOICE;
673
p.helpText=TRANS("Method to use to normalise scalar value in each voxel");
674
p.key=KEY_NORMALISE_TYPE;
675
propertyList.addProperty(p,curGroup);
676
propertyList.setGroupTitle(curGroup,TRANS("Computation"));
683
p.name=TRANS("Numerator");
684
p.data=numeratorAll ? "1" : "0";
685
p.type=PROPERTY_TYPE_BOOL;
686
p.helpText=TRANS("Parmeter \"a\" used in fraction (a/b) to get voxel value");
687
p.key=KEY_ENABLE_NUMERATOR;
688
propertyList.addProperty(p,curGroup);
690
ASSERT(rsdIncoming->enabledIons.size()==enabledIons[0].size());
691
ASSERT(rsdIncoming->enabledIons.size()==enabledIons[1].size());
693
//Look at the numerator
694
for(unsigned int ui=0; ui<rsdIncoming->enabledIons.size(); ui++)
697
if(enabledIons[0][ui])
702
//Append the ion name with a checkbox
703
p.name=rsdIncoming->rangeFile->getName(ui);
705
p.type=PROPERTY_TYPE_BOOL;
706
p.helpText=TRANS("Enable this ion for numerator");
707
p.key=KEY_ENABLE_NUMERATOR*1000+ui;
708
propertyList.addProperty(p,curGroup);
715
if (normaliseType == VOXELISE_NORMALISETYPE_COUNT2INVOXEL && rsdIncoming)
717
p.name=TRANS("Denominator");
718
p.data=denominatorAll ? "1" : "0";
719
p.type=PROPERTY_TYPE_BOOL;
720
p.helpText=TRANS("Parameter \"b\" used in fraction (a/b) to get voxel value");
721
p.key=KEY_ENABLE_DENOMINATOR;
723
for(unsigned int ui=0; ui<rsdIncoming->enabledIons.size(); ui++)
726
if(enabledIons[1][ui])
731
//Append the ion name with a checkbox
732
p.key=KEY_ENABLE_DENOMINATOR*1000 + ui;
734
p.name=rsdIncoming->rangeFile->getName(ui);
735
p.type=PROPERTY_TYPE_BOOL;
736
p.helpText=TRANS("Enable this ion for denominator contribution");
738
propertyList.addProperty(p,curGroup);
743
//Start a new set for filtering
745
//TODO: Other filtering? threshold/median? laplacian? etc
749
//Post-filtering method
750
for(unsigned int ui=0;ui<VOXELISE_FILTERTYPE_MAX; ui++)
752
tmpStr=getFilterTypeString(ui);
753
choices.push_back(make_pair(ui,tmpStr));
755
tmpStr= choiceString(choices,filterMode);
757
p.name=TRANS("Filtering");
759
p.key=KEY_FILTER_MODE;
760
p.type=PROPERTY_TYPE_CHOICE;
761
p.helpText=TRANS("Smoothing method to use on voxels");
763
propertyList.addProperty(p,curGroup);
764
propertyList.setGroupTitle(curGroup,TRANS("Processing"));
765
if(filterMode != VOXELISE_FILTERTYPE_NONE)
769
stream_cast(tmpStr,filterBins);
770
p.name=TRANS("Kernel Bins");
772
p.key=KEY_FILTER_BINS;
773
p.type=PROPERTY_TYPE_INTEGER;
774
p.helpText=TRANS("Number of bins in convolution kernel");
775
propertyList.addProperty(p,curGroup);
776
//Boundary wrapping mode selection
778
for(unsigned int ui=0;ui<VOXELISE_FILTERBOUNDMODE_MAX; ui++)
780
tmpStr=getFilterBoundTypeString(ui);
781
choices.push_back(make_pair(ui,tmpStr));
784
tmpStr= choiceString(choices,filterBoundaryMode);
785
p.name=TRANS("Exterior values");
787
p.type=PROPERTY_TYPE_CHOICE;
788
p.helpText=TRANS("Method to use to treat boundaries of voxel data for convolution");
789
p.key=KEY_FILTER_BOUNDARY_MODE;
790
propertyList.addProperty(p,curGroup);
792
propertyList.setGroupTitle(curGroup,TRANS("Filtering"));
796
//start a new group for the visual representation
797
//----------------------------
799
tmpStr=getRepresentTypeString(VOXEL_REPRESENT_POINTCLOUD);
800
choices.push_back(make_pair((unsigned int)VOXEL_REPRESENT_POINTCLOUD,tmpStr));
801
tmpStr=getRepresentTypeString(VOXEL_REPRESENT_ISOSURF);
802
choices.push_back(make_pair((unsigned int)VOXEL_REPRESENT_ISOSURF,tmpStr));
804
tmpStr= choiceString(choices,representation);
806
p.name=TRANS("Representation");
808
p.type=PROPERTY_TYPE_CHOICE;
809
p.helpText=TRANS("3D display method");
810
p.key=KEY_VOXEL_REPRESENTATION_MODE;
811
propertyList.addProperty(p,curGroup);
812
propertyList.setGroupTitle(curGroup,TRANS("Appearance"));
814
switch(representation)
816
case VOXEL_REPRESENT_POINTCLOUD:
818
stream_cast(tmpStr,splatSize);
819
p.name=TRANS("Spot size");
821
p.type=PROPERTY_TYPE_REAL;
822
p.helpText=TRANS("Size of the spots to use for display");
824
propertyList.addProperty(p,curGroup);
826
stream_cast(tmpStr,1.0-a);
827
p.name=TRANS("Transparency");
829
p.type=PROPERTY_TYPE_REAL;
830
p.helpText=TRANS("How \"see through\" each point is (0 - opaque, 1 - invisible)");
831
p.key=KEY_TRANSPARANCY;
832
propertyList.addProperty(p,curGroup);
835
case VOXEL_REPRESENT_ISOSURF:
837
stream_cast(tmpStr,isoLevel);
838
p.name=TRANS("Isovalue");
840
p.type=PROPERTY_TYPE_REAL;
841
p.helpText=TRANS("Scalar value to show as isosurface");
843
propertyList.addProperty(p,curGroup);
847
//Convert the ion colour to a hex string
848
genColString((unsigned char)(r*255),(unsigned char)(g*255),
849
(unsigned char)(b*255),(unsigned char)(a*255),tmpStr);
850
p.name=TRANS("Colour");
852
p.type=PROPERTY_TYPE_COLOUR;
853
p.helpText=TRANS("Colour of isosurface");
855
propertyList.addProperty(p,curGroup);
857
stream_cast(tmpStr,1.0-a);
858
p.name=TRANS("Transparency");
860
p.type=PROPERTY_TYPE_REAL;
861
p.helpText=TRANS("How \"see through\" each facet is (0 - opaque, 1 - invisible)");
862
p.key=KEY_TRANSPARANCY;
863
propertyList.addProperty(p,curGroup);
872
//----------------------------
875
bool VoxeliseFilter::setProperty( unsigned int key,
876
const std::string &value, bool &needUpdate)
885
if(stream_cast(b,value))
888
//if the result is different, the
889
//cache should be invalidated
901
if(stream_cast(i,value))
908
//if the result is different, the
909
//cache should be invalidated
914
calculateWidthsFromNumBins(binWidth, nBins);
922
if(stream_cast(i,value))
927
//if the result is different, the
928
//cache should be invalidated
933
calculateWidthsFromNumBins(binWidth, nBins);
941
if(stream_cast(i,value))
946
//if the result is different, the
947
//cache should be invalidated
952
calculateWidthsFromNumBins(binWidth, nBins);
960
if(stream_cast(f,value))
969
calculateNumBinsFromWidths(binWidth, nBins);
977
if(stream_cast(f,value))
986
calculateNumBinsFromWidths(binWidth, nBins);
994
if(stream_cast(f,value))
1002
calculateNumBinsFromWidths(binWidth, nBins);
1007
case KEY_NORMALISE_TYPE:
1010
for(i = 0; i < VOXELISE_NORMALISETYPE_MAX; i++)
1011
if (value == getNormaliseTypeString(i)) break;
1012
if (i == VOXELISE_NORMALISETYPE_MAX)
1014
if(normaliseType!=i)
1025
if(stream_cast(f,value))
1034
//Go in and manually adjust the cached
1035
//entries to have the new value, rather
1036
//than doing a full recomputation
1039
for(unsigned int ui=0;ui<filterOutputs.size();ui++)
1042
d=(VoxelStreamData*)filterOutputs[ui];
1043
d->splatSize=splatSize;
1050
case KEY_TRANSPARANCY:
1053
if(stream_cast(f,value))
1055
if(f < 0.0f || f > 1.0)
1058
//Alpha is opacity, which is 1-transparancy
1060
//Go in and manually adjust the cached
1061
//entries to have the new value, rather
1062
//than doing a full recomputation
1065
for(unsigned int ui=0;ui<filterOutputs.size();ui++)
1068
d=(VoxelStreamData*)filterOutputs[ui];
1077
if(stream_cast(f,value))
1083
//Go in and manually adjust the cached
1084
//entries to have the new value, rather
1085
//than doing a full recomputation
1088
for(unsigned int ui=0;ui<filterOutputs.size();ui++)
1091
d=(VoxelStreamData*)filterOutputs[ui];
1092
d->isoLevel=isoLevel;
1099
unsigned char newR,newG,newB,newA;
1101
parseColString(value,newR,newG,newB,newA);
1103
if(newB != b || newR != r ||
1104
newG !=g || newA != a)
1109
//Go in and manually adjust the cached
1110
//entries to have the new value, rather
1111
//than doing a full recomputation
1114
for(unsigned int ui=0;ui<filterOutputs.size();ui++)
1117
d=(VoxelStreamData*)filterOutputs[ui];
1125
case KEY_VOXEL_REPRESENTATION_MODE:
1128
for (i = 0; i < VOXEL_REPRESENT_END; i++)
1129
if (value == getRepresentTypeString(i)) break;
1130
if (i == VOXEL_REPRESENT_END)
1134
//Go in and manually adjust the cached
1135
//entries to have the new value, rather
1136
//than doing a full recomputation
1139
for(unsigned int ui=0;ui<filterOutputs.size();ui++)
1142
d=(VoxelStreamData*)filterOutputs[ui];
1143
d->representationType=representation;
1148
case KEY_ENABLE_NUMERATOR:
1151
if(stream_cast(b,value))
1153
//Set them all to enabled or disabled as a group
1154
for (size_t i = 0; i < enabledIons[0].size(); i++)
1155
enabledIons[0][i] = b;
1161
case KEY_ENABLE_DENOMINATOR:
1164
if(stream_cast(b,value))
1167
//Set them all to enabled or disabled as a group
1168
for (size_t i = 0; i < enabledIons[1].size(); i++)
1169
enabledIons[1][i] = b;
1176
case KEY_FILTER_MODE:
1179
for (i = 0; i < VOXEL_REPRESENT_END; i++)
1180
if (value == getFilterTypeString(i)) break;
1181
if (i == VOXEL_REPRESENT_END)
1191
case KEY_FILTER_BOUNDARY_MODE:
1194
for (i = 0; i < VOXELISE_FILTERBOUNDMODE_MAX; i++)
1195
if (value == getFilterBoundTypeString(i)) break;
1196
if (i == VOXELISE_FILTERTYPE_MAX)
1199
if(i != filterBoundaryMode)
1201
filterBoundaryMode=i;
1207
case KEY_FILTER_BINS:
1210
if(stream_cast(i,value))
1213
//FIXME: Min restriction is artificial and imposed due to incomplete separable convolution filter implementation
1214
if(i == 0 || i > std::min(nBins[0],std::min(nBins[1],nBins[2])))
1226
if (key >= KEY_ENABLE_DENOMINATOR*1000) {
1228
if(stream_cast(b,value))
1231
enabledIons[1][key - KEY_ENABLE_DENOMINATOR*1000]=b;
1233
denominatorAll = false;
1237
} else if (key >= KEY_ENABLE_NUMERATOR*1000) {
1239
if(stream_cast(b,value))
1242
enabledIons[0][key - KEY_ENABLE_NUMERATOR*1000]=b;
1244
numeratorAll = false;
1259
std::string VoxeliseFilter::getErrString(unsigned int code) const
1263
case VOXELISE_ABORT_ERR:
1264
return std::string(TRANS("Voxelisation aborted"));
1265
case VOXELISE_MEMORY_ERR:
1266
return std::string(TRANS("Out of memory"));
1267
case VOXELISE_CONVOLVE_ERR:
1268
return std::string(TRANS("Unable to perform filter convolution"));
1269
case VOXELISE_BOUNDS_INVALID_ERR:
1270
return std::string(TRANS("Voxelisation bounds are invalid"));
1273
return std::string("BUG! Should not see this (VoxeliseFilter)");
1276
bool VoxeliseFilter::writeState(std::ostream &f,unsigned int format, unsigned int depth) const
1281
case STATE_FORMAT_XML:
1283
f << tabs(depth) << "<" << trueName() << ">" << endl;
1284
f << tabs(depth+1) << "<userstring value=\"" << escapeXML(userString) << "\"/>" << endl;
1285
f << tabs(depth+1) << "<fixedwidth value=\""<<fixedWidth << "\"/>" << endl;
1286
f << tabs(depth+1) << "<nbins values=\""<<nBins[0] << ","<<nBins[1]<<","<<nBins[2] << "\"/>" << endl;
1287
f << tabs(depth+1) << "<binwidth values=\""<<binWidth[0] << ","<<binWidth[1]<<","<<binWidth[2] << "\"/>" << endl;
1288
f << tabs(depth+1) << "<normalisetype value=\""<<normaliseType << "\"/>" << endl;
1289
f << tabs(depth+1) << "<enabledions>" << endl;
1291
f << tabs(depth+2) << "<numerator>" << endl;
1292
for(unsigned int ui=0;ui<enabledIons[0].size(); ui++)
1293
f << tabs(depth+3) << "<enabled value=\"" << (enabledIons[0][ui]?1:0) << "\"/>" << endl;
1294
f << tabs(depth+2) << "</numerator>" << endl;
1296
f << tabs(depth+2) << "<denominator>" << endl;
1297
for(unsigned int ui=0;ui<enabledIons[1].size(); ui++)
1298
f << tabs(depth+3) << "<enabled value=\"" << (enabledIons[1][ui]?1:0) << "\"/>" << endl;
1299
f << tabs(depth+2) << "</denominator>" << endl;
1301
f << tabs(depth+1) << "</enabledions>" << endl;
1303
f << tabs(depth+1) << "<representation value=\""<<representation << "\"/>" << endl;
1304
f << tabs(depth+1) << "<isovalue value=\""<<isoLevel << "\"/>" << endl;
1305
f << tabs(depth+1) << "<colour r=\"" << r<< "\" g=\"" << g << "\" b=\"" <<b
1306
<< "\" a=\"" << a << "\"/>" <<endl;
1307
f << tabs(depth) << "</" << trueName() <<">" << endl;
1318
bool VoxeliseFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileDir)
1323
stack<xmlNodePtr> nodeStack;
1325
//Retrieve user string
1327
if(XMLHelpFwdToElem(nodePtr,"userstring"))
1330
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
1333
userString=(char *)xmlString;
1337
//Retrieve fixedWidth mode
1338
if(!XMLGetNextElemAttrib(nodePtr,tmpStr,"fixedwidth","value"))
1342
else if(tmpStr== "0")
1348
if(XMLHelpFwdToElem(nodePtr,"nbins"))
1350
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"values");
1353
std::vector<string> v1;
1354
splitStrsRef((char *)xmlString,',',v1);
1355
for (size_t i = 0; i < INDEX_LENGTH && i < v1.size(); i++)
1357
if(stream_cast(nBins[i],v1[i]))
1365
//Retrieve bin width
1366
if(XMLHelpFwdToElem(nodePtr,"binwidth"))
1368
xmlString=xmlGetProp(nodePtr,(const xmlChar *)"values");
1371
std::vector<string> v2;
1372
splitStrsRef((char *)xmlString,',',v2);
1373
for (size_t i = 0; i < INDEX_LENGTH && i < v2.size(); i++)
1375
if(stream_cast(binWidth[i],v2[i]))
1378
if(binWidth[i] <= 0)
1383
//Retrieve normaliseType
1384
if(!XMLGetNextElemAttrib(nodePtr,normaliseType,"normalisetype","value"))
1386
if(normaliseType >= VOXELISE_NORMALISETYPE_MAX)
1389
//Look for the enabled ions bit
1393
if(!XMLHelpFwdToElem(nodePtr,"enabledions"))
1396
nodeStack.push(nodePtr);
1397
if(!nodePtr->xmlChildrenNode)
1399
nodePtr=nodePtr->xmlChildrenNode;
1401
//enabled ions for numerator
1402
if(XMLHelpFwdToElem(nodePtr,"numerator"))
1405
nodeStack.push(nodePtr);
1407
if(!nodePtr->xmlChildrenNode)
1410
nodePtr=nodePtr->xmlChildrenNode;
1415
//Retrieve representation
1416
if(!XMLGetNextElemAttrib(nodePtr,c,"enabled","value"))
1420
enabledIons[0].push_back(true);
1422
enabledIons[0].push_back(false);
1425
nodePtr=nodePtr->next;
1428
nodePtr=nodeStack.top();
1431
//enabled ions for denominator
1432
if(XMLHelpFwdToElem(nodePtr,"denominator"))
1436
if(!nodePtr->xmlChildrenNode)
1439
nodeStack.push(nodePtr);
1440
nodePtr=nodePtr->xmlChildrenNode;
1445
//Retrieve representation
1446
if(!XMLGetNextElemAttrib(nodePtr,c,"enabled","value"))
1450
enabledIons[1].push_back(true);
1452
enabledIons[1].push_back(false);
1455
nodePtr=nodePtr->next;
1460
nodePtr=nodeStack.top();
1463
//Check that the enabled ions size makes at least some sense...
1464
if(enabledIons[0].size() != enabledIons[1].size())
1470
//Retrieve representation
1471
if(!XMLGetNextElemAttrib(nodePtr,representation,"representation","value"))
1473
if(representation >=VOXEL_REPRESENT_END)
1477
//Retrieve representation
1478
if(!XMLGetNextElemAttrib(nodePtr,isoLevel,"isovalue","value"))
1483
if(XMLHelpFwdToElem(nodePtr,"colour"))
1485
if(!parseXMLColour(nodePtr,r,g,b,a))
1493
unsigned int VoxeliseFilter::getRefreshBlockMask() const
1495
//Ions, plots and voxels cannot pass through this filter
1496
return STREAM_TYPE_IONS | STREAM_TYPE_PLOT | STREAM_TYPE_VOXEL;
1499
unsigned int VoxeliseFilter::getRefreshEmitMask() const
1501
return STREAM_TYPE_VOXEL | STREAM_TYPE_DRAW;
1504
unsigned int VoxeliseFilter::getRefreshUseMask() const
1506
return STREAM_TYPE_IONS | STREAM_TYPE_RANGE;
1509
void VoxeliseFilter::setPropFromBinding(const SelectionBinding &b)
1514
bool voxelSingleCountTest()
1516
//Test counting a single vector
1518
vector<IonHit> ionVec;
1521
ionVec[0].setPos(Point3D(0.1,0.1,0.1));
1522
ionVec[1].setPos(Point3D(0.1,0.0,0.1));
1523
ionVec[2].setPos(Point3D(0.0,0.1,0.1));
1524
ionVec[3].setPos(Point3D(0.1,0.1,0.0));
1525
ionVec[4].setPos(Point3D(0.0,0.1,0.0));
1527
for(unsigned int ui=0;ui<ionVec.size();ui++)
1528
ionVec[ui].setMassToCharge(1);
1530
IonStreamData *ionData = new IonStreamData;
1531
std::swap(ionData->data,ionVec);
1533
size_t numIons=ionData->data.size();
1535
VoxeliseFilter *f = new VoxeliseFilter;
1536
f->setCaching(false);
1539
TEST(f->setProperty(KEY_NBINSX,"4",needUpdate),"num bins x");
1540
TEST(f->setProperty(KEY_NBINSY,"4",needUpdate),"num bins y");
1541
TEST(f->setProperty(KEY_NBINSZ,"4",needUpdate),"num bins z");
1544
vector<const FilterStreamData*> streamIn,streamOut;
1545
streamIn.push_back(ionData);
1548
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
1551
TEST(streamOut.size() == 1,"stream count");
1552
TEST(streamOut[0]->getStreamType() == STREAM_TYPE_VOXEL,"Stream type");
1555
const VoxelStreamData *v= (const VoxelStreamData*)streamOut[0];
1557
TEST(v->data.max() <=numIons,
1558
"voxel max less than input stream")
1560
TEST(v->data.min() >= 0.0f,"voxel counting minimum sanity");
1564
sumVoxels(v->data,dataSum);
1565
TEST(fabs(dataSum - (float)numIons ) <
1566
sqrt(std::numeric_limits<float>::epsilon()),"voxel counting all input ions ");
1569
delete streamOut[0];
1574
bool voxelMultiCountTest()
1576
//Test counting multiple data streams containing ranged data
1578
vector<const FilterStreamData*> streamIn,streamOut;
1579
vector<IonHit> ionVec;
1582
ionVec[0].setPos(Point3D(0.1,0.1,0.1));
1583
ionVec[1].setPos(Point3D(0.1,0.0,0.1));
1584
ionVec[2].setPos(Point3D(0.0,0.1,0.1));
1585
ionVec[3].setPos(Point3D(0.1,0.1,0.0));
1586
ionVec[4].setPos(Point3D(0.0,0.1,0.0));
1588
IonStreamData *ionData[2];
1589
RangeStreamData *rngStream;
1590
rngStream = new RangeStreamData;
1591
rngStream->rangeFile= new RangeFile;
1593
RGBf col; col.red=col.green=col.blue=1.0f;
1595
const unsigned int MAX_NUM_RANGES=2;
1596
for(unsigned int ui=0;ui<MAX_NUM_RANGES;ui++)
1600
//Add a new ion "a1, a2... etc"
1603
stream_cast(sTmp2,ui);
1605
ionNum=rngStream->rangeFile->addIon(sTmp,sTmp,col);
1606
rngStream->rangeFile->addRange((float)ui-0.5f,(float)ui+0.5f,ionNum);
1608
//Change m/c value for ion
1609
for(unsigned int uj=0;uj<ionVec.size();uj++)
1610
ionVec[uj].setMassToCharge(ui);
1612
ionData[ui]= new IonStreamData;
1613
ionData[ui]->data.resize(ionVec.size());
1614
std::copy(ionVec.begin(),ionVec.end(),ionData[ui]->data.begin());
1615
streamIn.push_back(ionData[ui]);
1618
rngStream->enabledIons.resize(rngStream->rangeFile->getNumIons());
1619
rngStream->enabledRanges.resize(rngStream->rangeFile->getNumRanges());
1621
streamIn.push_back(rngStream);
1623
VoxeliseFilter *f = new VoxeliseFilter;
1625
//Initialise range data
1626
f->initFilter(streamIn,streamOut);
1629
f->setCaching(false);
1632
TEST(f->setProperty(KEY_NBINSX,"4",needUpdate),"num bins x");
1633
TEST(f->setProperty(KEY_NBINSY,"4",needUpdate),"num bins y");
1634
TEST(f->setProperty(KEY_NBINSZ,"4",needUpdate),"num bins z");
1637
TEST(f->setProperty(KEY_NORMALISE_TYPE,
1638
TRANS(NORMALISE_TYPE_STRING[VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL]),needUpdate),
1639
"Set normalise mode");
1642
TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
1644
for(unsigned int ui=0;ui<MAX_NUM_RANGES;ui++)
1645
delete streamIn[ui];
1646
TEST(streamOut.size() == 2,"stream count");
1647
TEST(streamOut[1]->getStreamType() == STREAM_TYPE_VOXEL,"Stream type");
1649
const VoxelStreamData *v= (const VoxelStreamData*)streamOut[1];
1651
TEST(v->data.max() <=1.0f,
1652
"voxel max less than input stream")
1653
TEST(v->data.min() >= 0.0f,"voxel counting minimum sanity");
1656
for(unsigned int ui=0;ui<v->data.getSize();ui++)
1659
delta=(v->data.getData(ui) - v->data.getData(0) );
1660
ASSERT( v->data.getData(ui) == 0 || delta < std::numeric_limits<float>::epsilon());
1665
delete rngStream->rangeFile;
1672
bool VoxeliseFilter::runUnitTests()
1675
if(!voxelSingleCountTest())
1678
if(!voxelMultiCountTest())