[3depict] 02/07: * Update to upstream effd078610a7

D Haley mycae-guest at moszumanska.debian.org
Wed Aug 3 23:47:17 UTC 2016


This is an automated email from the git hooks/post-receive script.

mycae-guest pushed a commit to branch master
in repository 3depict.

commit b88bbc247b2d5b59585559c78dd58d8824ed62d7
Author: D Haley <mycae at gmx.com>
Date:   Thu Aug 4 00:18:23 2016 +0200

    * Update to upstream effd078610a7
---
 .../patches/diff-upstream-0.0.18_to_212d6e1e6b14   | 1490 --------------------
 debian/patches/diff_0.0.19_to_effd078610a7         | 1235 ++++++++++++++++
 debian/patches/lowercase-textdomain.patch          |   10 +-
 debian/patches/series                              |    2 +-
 4 files changed, 1241 insertions(+), 1496 deletions(-)

diff --git a/debian/patches/diff-upstream-0.0.18_to_212d6e1e6b14 b/debian/patches/diff-upstream-0.0.18_to_212d6e1e6b14
deleted file mode 100644
index 3053a6f..0000000
--- a/debian/patches/diff-upstream-0.0.18_to_212d6e1e6b14
+++ /dev/null
@@ -1,1490 +0,0 @@
-diff -ruh ./src/3Depict.cpp ./3Depict.cpp
---- ./src/3Depict.cpp	2016-02-08 02:38:00.000000000 +0100
-+++ ./src/3Depict.cpp	2016-02-08 02:41:11.805762472 +0100
-@@ -21,6 +21,9 @@
- #include <wx/cmdline.h>
- #include <wx/filename.h>
- #include <wx/stdpaths.h>
-+#ifndef DEBUG
-+#include <wx/log.h>
-+#endif
- 
- #ifdef __APPLE__
- #include "CoreFoundation/CoreFoundation.h"
-@@ -39,11 +42,12 @@
- 
- class threeDepictApp: public wxApp {
- private:
--#ifndef DEBUG
--	//instance of this class suppresses internal wx error dialogs.
--	// these are a nuisance in release code, as recovered errors often annoy the user
--	wxLogNull nullifyLogs;
--#endif
-+
-+
-+	void redirectWxLogging();
-+
-+	std::ofstream debugLogStream;
-+
- 
- 	MainWindowFrame* MainFrame ;
- 	wxArrayString commandLineFiles;
-@@ -101,11 +105,29 @@
- {
-        	MainFrame=0;usrLocale=0;
- 	dontLoad=false;
--#ifndef DEBUG
-+	
- 	//Wx 2.9 and up now has assertions auto-enabled. 
- 	//Disable for release builds
-+#ifndef DEBUG
- 	wxSetAssertHandler(NULL);
- #endif
-+	redirectWxLogging();
-+}
-+
-+void threeDepictApp::redirectWxLogging()
-+{
-+	//Disable user visible logging on the main thread, this can throw up "error dialogs"
-+	// to the user that seem to be false positives, such as "Error: Sucess" type messages
-+	// ihstead try first to log to file. If that fails, just disable it
-+        wxStandardPaths &paths = wxStandardPaths::Get();
-+	wxString filePath = paths.GetDocumentsDir();
-+	filePath+=("/.")+string(PROGRAM_NAME) + string("log.txt");
-+	debugLogStream.open(filePath.c_str());
-+
-+	if(!debugLogStream)
-+		wxLog::EnableLogging(false);	
-+	else
-+		wxLog::SetActiveTarget(new wxLogStream(&debugLogStream));
- }
- 
- int threeDepictApp::OnExit()
-diff -ruh ./src/backend/APT/APTFileIO.cpp ./backend/APT/APTFileIO.cpp
---- ./src/backend/APT/APTFileIO.cpp	2016-02-08 01:39:07.246999770 +0100
-+++ ./src/backend/APT/APTFileIO.cpp	2016-02-08 02:41:11.833762535 +0100
-@@ -473,7 +473,7 @@
- 	//we define this as the split value being able to generate 
- 	//1) Enough data to make interpretable columns
- 	//2) Enough columns that can be interpreted.
--	while(!CFile.eof() && curPos < maxPos)
-+	while(CFile.good() && !CFile.eof() && curPos < maxPos)
- 	{
- 		string s;
- 		curPos = CFile.tellg();
-@@ -505,7 +505,7 @@
- 	}	
- 
- 	//could not find any data.. only header.
--	if(CFile.eof() || curPos >=maxPos)
-+	if(!CFile.good() || CFile.eof() || curPos >=maxPos)
- 		return TEXT_ERR_ONLY_HEADER;
- 
- 
-@@ -526,7 +526,7 @@
- 	newLinePositions.push_back(curPos);
- 	bool seenNumeric=false;
- 	buffer = new char[BUFFER_SIZE];
--	while(!CFile.eof() && curPos < maxPos)
-+	while(CFile.good() && !CFile.eof() && curPos < maxPos)
- 	{
- 		size_t bytesToRead;
- 
-diff -ruh ./src/backend/APT/APTRanges.cpp ./backend/APT/APTRanges.cpp
---- ./src/backend/APT/APTRanges.cpp	2016-02-08 01:39:07.246999770 +0100
-+++ ./src/backend/APT/APTRanges.cpp	2016-02-08 02:41:11.637762095 +0100
-@@ -1135,10 +1135,8 @@
- 
- 		//Now, spin forwards until we either hit EOF or our double-dash marker
- 
--		while(!f.eof())
-+		while(!getline(f,tmpStr))
- 		{
--			getline(f,tmpStr);
--
- 			if(tmpStr.size() > 2 &&
- 				tmpStr[0] == '-' && tmpStr[1] == '-')
- 			{
-@@ -1149,7 +1147,7 @@
- 			}
- 		}
- 
--		if(f.eof())
-+		if(!f.good())
- 		{
- 			//we did not see a double-dash, must be a vanilla ORNL file
- 			typeStatus[RANGE_FORMAT_DBL_ORNL]=STATUS_IS_NOT;
-@@ -1505,6 +1503,7 @@
- 	bool beyondRanges=false;
- 	bool haveNumRanges=false;	
- 	bool haveNameBlock=false;	
-+	bool haveSeenRevHeader=false;	
- 	vector<string> strVec;
- 
- 	//Read file until we get beyond the range length
-@@ -1527,6 +1526,13 @@
- 		if(!s.size())
- 			continue;
- 
-+		//If we have
-+		if(!haveSeenRevHeader && (s== "Rev_2.0"))
-+		{
-+			haveSeenRevHeader=true;
-+			continue;
-+		}
-+
- 		//Try different delimiters to split string
- 		splitStrsRef(s.c_str(),"\t ",strVec);
- 
-diff -ruh ./src/backend/filters/algorithms/mass.cpp ./backend/filters/algorithms/mass.cpp
---- ./src/backend/filters/algorithms/mass.cpp	2016-02-08 01:39:07.250999771 +0100
-+++ ./src/backend/filters/algorithms/mass.cpp	2016-02-08 02:41:11.645762113 +0100
-@@ -87,19 +87,18 @@
- 	const unsigned int MIN_REQUIRED_AVG_COUNTS=10;
- 	const unsigned int MIN_REQUIRED_BINS=10;
- 
--	//CHECKME : The number of bins is the same in TOF as well as in 
--	// m/c space. 	
--	size_t nBins = (backParams.massEnd - backParams.massStart) / backParams.binWidth;
--	float filterStep = (sqrt(backParams.massEnd) - sqrt(backParams.massStart) )/ nBins; 
-+	size_t nBinsTof = (sqrt(backParams.massEnd) - sqrt(backParams.massStart)) / backParams.binWidth;
-+	float filterStep = (sqrt(backParams.massEnd) - sqrt(backParams.massStart) )/ nBinsTof; 
- 
- 	//we cannot perform a test with fewer than this number of bins
--	if ( nBins < MIN_REQUIRED_BINS)
-+	if ( nBinsTof < MIN_REQUIRED_BINS)
- 		return BACKGROUND_PARAMS::FIT_FAIL_MIN_REQ_BINS;
- 
--	float averageCounts = sqrtFiltMass.size()/ (float)nBins; 
-+	float averageCounts = sqrtFiltMass.size()/ (float)nBinsTof; 
- 	if( averageCounts < MIN_REQUIRED_AVG_COUNTS)
- 		return BACKGROUND_PARAMS::FIT_FAIL_AVG_COUNTS; 
- 
-+	//Check that the TOF-space histogram is gaussian
- 	vector<float> histogram;
- 	makeHistogram(sqrtFiltMass,sqrt(backParams.massStart),
- 			sqrt(backParams.massEnd), filterStep,histogram);	
-@@ -120,14 +119,34 @@
- 	if(andersonStat > STATISTIC_THRESHOLD || undefCount == histogram.size())
- 		return BACKGROUND_PARAMS::FIT_FAIL_DATA_NON_GAUSSIAN;
- 
--	//Intensity PER AMU
--	//backgroundIntensity= meanVal/filterStep;
- 	//Intensity PER BIN in TOF space
- 	backParams.intensity= meanVal;
- 
- 	return 0;
- }
- 
-+
-+//Start and end mass, and step size (to get bin count).
-+// tofBackIntensity is the intensity level per unit time in the background, as obtained by doFitBackground
-+// the histogram is 
-+void createMassBackground(float massStart, float massEnd, unsigned int nBinsMass,
-+			float tofBackIntensity, vector<float> &histogram)
-+{
-+	const float MC_BIN_STEP = (massEnd-massStart)/nBinsMass;
-+
-+	//compute fitted value analytically
-+	histogram.resize(nBinsMass);
-+	for(size_t ui=0;ui<histogram.size();ui++)
-+	{
-+		float mcX;
-+		mcX=(float)ui*MC_BIN_STEP+ massStart;
-+		if ( mcX <=0)
-+			histogram[ui]=0;
-+		else
-+			histogram[ui]= tofBackIntensity/(2.0*sqrt(mcX))*MC_BIN_STEP;
-+	}
-+}
-+
- #ifdef DEBUG
- #include "common/mathfuncs.h"
- 
-@@ -163,7 +182,7 @@
- 	return true;
- }
- 
--bool testBackgroundFit()
-+bool testBackgroundFitMaths()
- {
- 	RandNumGen rng;
- 	rng.initTimer();
-@@ -173,15 +192,14 @@
- 	
- 	ionData = new IonStreamData;
- 
--	const unsigned int NUM_IONS =10000;
--	const float SIMULATED_INTENSITY= 100.0f;
-+	const unsigned int NUM_IONS =100000;
- 	
- 	//Simulate a histogram of NUM_IONS
- 	// between a lower and upper limit. 
- 	// This is flat in TOF space, with mean intensity
- 	// given by NUM_IONS/NUM_BINS
- 	//---
--	const float TOF_LIMIT[2] = { 1.0,10};	
-+	const float TOF_LIMIT[2] = { 0.0,100};	
- 	
- 	vector<float> rawData;
- 	ionData->data.resize(NUM_IONS);
-@@ -194,20 +212,7 @@
- 		rawData[ui] = simTof;	
- 	}
- 
--	const float BIN_STEP=0.1f;
--	vector<float> histogramRes;
--	makeHistogram(rawData,TOF_LIMIT[0],TOF_LIMIT[1],
--		BIN_STEP,histogramRes);
--	//---
- 
--	//Find the mean and std. deviation for the tof  histogram
--	float meanV,stdV;
--	meanAndStdev(histogramRes,meanV,stdV);
--
--	//check that the TOF histogram's mean matches the expected value 	
--	const float EXPECTED_MEAN = NUM_IONS*BIN_STEP/(TOF_LIMIT[1] - TOF_LIMIT[0]);
--	TEST(meanV > 0.95*EXPECTED_MEAN &&
--		meanV < EXPECTED_MEAN*1.15,"expected mean should fall (well) within anticipated bounds, but does not"); 
- 
- 
- 	//Now perform the fit in m/c space, and after, check that it matches the anticipated m/c histogram.
-@@ -217,41 +222,39 @@
- 	vector<float> massData;
- 	massData.resize(NUM_IONS);
- 	for(size_t ui=0;ui<NUM_IONS;ui++)
--		massData[ui] = sqrt(rawData[ui]);
-+		massData[ui] = rawData[ui]*rawData[ui];
- 	vector<float> massHist;
- 	
- 	//Recompute the bin step parameter, as the stepping in m/c space to yield 
- 	// the same number of bins will e radially different
--	const float NBINS = ( TOF_LIMIT[1] - TOF_LIMIT[0] )/BIN_STEP;
--	const float MC_BIN_STEP = (sqrt(TOF_LIMIT[1])-sqrt(TOF_LIMIT[0]))/NBINS;
--	makeHistogram(massData,sqrt(TOF_LIMIT[0]),sqrt(TOF_LIMIT[1]),MC_BIN_STEP,massHist);	
-+	const float NBINS_TOF = 20;
-+	const float NBINS_MASS= NBINS_TOF; 
-+	const float MASS_LIMIT[2] =  {TOF_LIMIT[0]*TOF_LIMIT[0], TOF_LIMIT[1]*TOF_LIMIT[1]};
-+	
-+
-+	//time-space intensity per unit time
-+	const float TOF_MEAN_INT= NUM_IONS/(TOF_LIMIT[1] - TOF_LIMIT[0]);
-+
-+	const float MC_BIN_STEP = (MASS_LIMIT[1]-MASS_LIMIT[0])/NBINS_MASS;
-+	makeHistogram(massData,MASS_LIMIT[0],MASS_LIMIT[1],MC_BIN_STEP,massHist);	
- 
- 	//compute fitted value analytically
- 	vector<float > fittedMassHist;
--	fittedMassHist.resize(NBINS);
--	for(size_t ui=0;ui<histogramRes.size();ui++)
--	{
--		float mcX;
--		mcX=(float)ui*MC_BIN_STEP + sqrtf(TOF_LIMIT[0]);
--		fittedMassHist[ui]= meanV/(2*mcX);
--	}
--	ASSERT(massHist.size() == histogramRes.size());
-+	createMassBackground(MASS_LIMIT[0],MASS_LIMIT[1],NBINS_MASS,TOF_MEAN_INT,fittedMassHist);	
- 
--	//FIXME: Test appears to be broken
--	WARN(false,"Test non-functional, and algorithm broken. Fixme.");
--	//check that the numerical and analytical results match
--	for(size_t ui=0;ui<massHist.size();ui++)
-+	//check that the numerical and analytical results match.
-+	// notably, skip the first one as the fit is unstable
-+	for(size_t ui=1;ui<massHist.size();ui++)
- 	{
- 		float midV;
--		midV = massHist[ui] + histogramRes[ui];
-+		midV = massHist[ui] + fittedMassHist[ui];
- 		midV*=0.5f;
- 		float errorFraction;
--		errorFraction= fabs((massHist[ui] - histogramRes[ui])/midV);
--		//ASSERT(errorFraction < 0.5f);
-+		errorFraction= fabs((massHist[ui] - fittedMassHist[ui])/midV);
-+		ASSERT(errorFraction < 0.5f);
- 	}	
- 	//---
- 
- 	return true;	
-  }
--
- #endif
-diff -ruh ./src/backend/filters/algorithms/mass.h ./backend/filters/algorithms/mass.h
---- ./src/backend/filters/algorithms/mass.h	2016-02-08 01:39:07.250999771 +0100
-+++ ./src/backend/filters/algorithms/mass.h	2016-02-08 02:41:11.645762113 +0100
-@@ -194,7 +194,7 @@
- 
- //Check that the background fitting routine can fit
- // a random TOF data histogram
--bool testBackgroundFit();
-+bool testBackgroundFitMaths();
- 
- #endif
- #endif
-diff -ruh ./src/backend/filters/algorithms/rdf.cpp ./backend/filters/algorithms/rdf.cpp
---- ./src/backend/filters/algorithms/rdf.cpp	2016-02-08 01:39:07.250999771 +0100
-+++ ./src/backend/filters/algorithms/rdf.cpp	2016-02-08 02:41:11.645762113 +0100
-@@ -193,7 +193,7 @@
- 
- 	unsigned int dummyProg;
- 	vector<Point3D> theHull;
--	if(computeConvexHull(points,progress,wantAbort,theHull,false))
-+	if(computeConvexHull(points,progress,wantAbort,theHull,false,false))
- 		return 2;
- 
- 	Point3D midPoint(0,0,0);
-@@ -1044,5 +1044,14 @@
- 
- }
- 
-+bool qhullTest()
-+{
-+#if defined(__WIN64)
-+	//If using a cross-compile (at least)
-+	// qhull under win64 must use long long, or we get random crashes
-+	// The definition is set in qhull/mem.h
-+	COMPILE_ASSERT(sizeof(ptr_intT) == sizeof(long long))
-+#endif
-+}
- 
- 
-diff -ruh ./src/backend/filters/dataLoad.cpp ./backend/filters/dataLoad.cpp
---- ./src/backend/filters/dataLoad.cpp	2016-02-08 01:39:07.258999772 +0100
-+++ ./src/backend/filters/dataLoad.cpp	2016-02-08 02:41:11.645762113 +0100
-@@ -67,7 +67,7 @@
- 					NTRANS("Text Data"),
- 					NTRANS("ATO Data"),
- 					};
--const char *DEFAULT_LABEL="Mass-to-Charge (amu/e)";
-+const char *DEFAULT_LABEL="Mass-to-Charge (Da/e)";
- 
- 
- 
-diff -ruh ./src/backend/filters/geometryHelpers.cpp ./backend/filters/geometryHelpers.cpp
---- ./src/backend/filters/geometryHelpers.cpp	2016-02-08 01:39:07.258999772 +0100
-+++ ./src/backend/filters/geometryHelpers.cpp	2016-02-08 02:41:11.669762167 +0100
-@@ -455,18 +455,21 @@
- 		//rotate ion position into cylindrical coordinates
- 		quat_rot_apply_quat(&p,&qA);
- 
-+		fSqrRad=p.fx*p.fx + p.fy*p.fy;
- 		//Check inside upper and lower bound of cylinder
- 		// and check inside cylinder radius
--		if(!(p.fz < fA && p.fz > -fA && 
--				p.fx*p.fx+p.fy*p.fy < fB))
-+		if(!( (p.fz < fA && p.fz > -fA ) &&  
-+					fSqrRad < fB))
- 			return (unsigned int)-1;
- 
--		fSqrRad=ptmp[0]*ptmp[0] + ptmp[1]*ptmp[1];
--		
- 	}
- 
-+	unsigned int mapPos;
-+	mapPos=(unsigned int)(fSqrRad/fB*(float)mapMax);
-+	ASSERT(mapPos < mapMax);
-+
- 	//Area is constant in square space
--	return (unsigned int)(fSqrRad/fB*(float)mapMax);
-+	return mapPos;
- 
- }
- 
-@@ -523,8 +526,11 @@
- 	ASSERT(!invertedClip);
- 	ASSERT(mapFunc);
- 	ASSERT(mapMax);
--	//return the 1D mapping for the ion
--	return (this->*mapFunc)(ionIn.getPosRef());
-+	//return the 1D mapping for the ion, or -1 for not mappable
-+	unsigned int mappingPos;
-+	mappingPos=(this->*mapFunc)(ionIn.getPosRef());
-+	ASSERT(mappingPos < mapMax || mappingPos == (unsigned int) -1);
-+	return mappingPos; 
- }
- 
- void CropHelper::setFilterMode(size_t filterMode)
-diff -ruh ./src/backend/filters/rangeFile.cpp ./backend/filters/rangeFile.cpp
---- ./src/backend/filters/rangeFile.cpp	2016-02-08 01:39:07.258999772 +0100
-+++ ./src/backend/filters/rangeFile.cpp	2016-02-08 02:41:11.673762176 +0100
-@@ -116,6 +116,11 @@
- 		return 0;
- 	}
- 
-+	progress.filterProgress=0;
-+	progress.stepName=TRANS("Ranging");
-+	progress.step=1;
-+	progress.maxStep=1;	
-+
- 
- 	ASSERT(enabledRanges.size() == rng.getNumRanges());
- 	ASSERT(enabledIons.size() == rng.getNumIons());
-@@ -148,11 +153,6 @@
- 		sameSize=true;
- 
- 
--		progress.step=1;
--		progress.filterProgress=0;
--		progress.stepName=TRANS("Pre-Allocate");
--		progress.maxStep=2;	
--
- 		vector<size_t> dSizes;
- 		dSizes.resize(d.size(),0);
- 		size_t totalSize=numElements(dataIn);
-@@ -270,7 +270,7 @@
- 		{
- 			//slightly over-allocate to allow for any variance
- 			for(size_t ui=0;ui<d.size();ui++)
--				d[ui]->data.reserve(dSizes[ui]*1.f*RANGE_ALLOC_STEP+10);
-+				d[ui]->data.reserve(dSizes[ui]*1.05f*RANGE_ALLOC_STEP+10);
- 		}
- 		catch(std::bad_alloc)
- 		{
-@@ -282,12 +282,6 @@
- 		dSizes.clear();
- 		//===================================
- 
--		//Update progress info
--		progress.step=2;
--		progress.filterProgress=0;
--		progress.stepName=TRANS("Range");
--
--
- 		
- 
- 		//Step 2: Go through each data stream, if it is an ion stream, range it.
-diff -ruh ./src/backend/filters/transform.cpp ./backend/filters/transform.cpp
---- ./src/backend/filters/transform.cpp	2016-02-08 01:39:07.262999773 +0100
-+++ ./src/backend/filters/transform.cpp	2016-02-08 02:41:11.681762194 +0100
-@@ -277,9 +277,10 @@
- 		DrawStreamData *d=makeMarkerSphere(s);
- 		if(s)
- 			devices.push_back(s);
--
--		cacheAsNeeded(d);
--		
-+		else
-+		{
-+			cacheAsNeeded(d);
-+		}	
- 		getOut.push_back(d);
- 	}
- 			
-diff -ruh ./src/backend/state.cpp ./backend/state.cpp
---- ./src/backend/state.cpp	2016-02-08 01:39:07.266999774 +0100
-+++ ./src/backend/state.cpp	2016-02-08 02:41:11.689762212 +0100
-@@ -979,7 +979,10 @@
- 	ASSERT(offset < savedCameras.size());
- 	savedCameras.erase(savedCameras.begin()+offset);
- 	if(activeCamera >=savedCameras.size())
--		activeCamera=0;
-+	{
-+		ASSERT(savedCameras.size())
-+		activeCamera=savedCameras.size()-1;
-+	}
- }
- 
- void AnalysisState::addCamByClone(const Camera *c)
-@@ -990,6 +993,8 @@
- 
- void AnalysisState::addCam(const std::string &camName, bool makeActive)
- {
-+	//Disallow unnamed cameras
-+	ASSERT(camName.size());
- 	//Duplicate the current camera, and give it a new name
- 	Camera *c=getCam(getActiveCam())->clone();
- 	c->setUserString(camName);
-@@ -1169,7 +1174,22 @@
- 	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
- 	stashedTrees.erase(stashedTrees.begin() + offset);
- }
-+		
-+void AnalysisState::eraseStashes(std::vector<size_t> &offsets)
-+{
-+	std::sort(offsets.begin(),offsets.end());
-+	ASSERT(std::unique(offsets.begin(),offsets.end()) == offsets.end());
-+
- 
-+
-+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
-+	for(unsigned int ui=offsets.size();ui>0;)
-+	{
-+		ui--;
-+		
-+		stashedTrees.erase(stashedTrees.begin() + offsets[ui]);
-+	}
-+}
- bool AnalysisState::hasStateOverrides() const
- {
- 	if(treeState.hasStateOverrides())
-diff -ruh ./src/backend/state.h ./backend/state.h
---- ./src/backend/state.h	2016-02-08 01:39:07.266999774 +0100
-+++ ./src/backend/state.h	2016-02-08 02:41:11.689762212 +0100
-@@ -368,6 +368,9 @@
- 
- 		//!Add a new camera to the scene
- 		void addCam(const std::string &camName, bool makeActive=false);
-+	
-+		bool defaultCamActive() const { return activeCamera == 0;}
-+
- 		//=====
- 
- 		//Effect functions
-@@ -417,8 +420,11 @@
- 
- 		//!Insert  the given stash into the tree as a child of the given parent filter
- 		void addStashedToFilters(const Filter *parentFilter, unsigned int stashOffset);
--		//Remove the stash at the specified offset
-+		//Remove the stash at the specified offset. Numbers will
-+		// be reset, so previous offsets will no longer be valid
- 		void eraseStash(size_t offset);
-+		//Remove the given stashew at the specified offsets
-+		void eraseStashes(std::vector<size_t> &offset);
- 
- 		//Return the number of stash elements
- 		size_t getStashCount()  const { return stashedTrees.size();}
-diff -ruh ./src/backend/viscontrol.cpp ./backend/viscontrol.cpp
---- ./src/backend/viscontrol.cpp	2016-02-08 01:39:07.266999774 +0100
-+++ ./src/backend/viscontrol.cpp	2016-02-08 02:41:11.689762212 +0100
-@@ -670,7 +670,9 @@
- 
- void VisController::updateStashComboBox(wxComboBox *comboStash) const
- {
--	comboStash->Clear();
-+	//HACK: Calling ->Clear() under MSW causes a crash
-+	while(comboStash->GetCount())
-+		comboStash->Delete(0);
- 
- 	unsigned int nStashes = state.getStashCount();	
- 	for(unsigned int ui=0;ui<nStashes; ui++)
-@@ -687,7 +689,10 @@
- void VisController::updateCameraComboBox(wxComboBox *comboCamera) const
- {
- 	//Update the camera dropdown
--	comboCamera->Clear();
-+	//HACK: Calling ->Clear() under MSW causes a crash
-+	while(comboCamera->GetCount())
-+		comboCamera->Delete(0);
-+	
- 	size_t nCams = state.getNumCams();
- 	//The start from 1 is a hack to avoid the unnamed camera
- 	for(unsigned int ui=1;ui<nCams;ui++)
-@@ -698,8 +703,7 @@
- 		//Do not delete as this will be deleted by wx
- 		comboCamera->Append(camName,
- 				(wxClientData *)new wxListUint(ui));	
--		//If this is the active cam (1) set the selection and (2) remember
--		//the ID
-+		//If this is the active cam (1) set the selection 
- 		if(ui == state.getActiveCam())
- 			comboCamera->SetSelection(ui-1);
- 	}
-diff -ruh ./src/common/assertion.h ./common/assertion.h
---- ./src/common/assertion.h	2016-02-08 01:39:07.266999774 +0100
-+++ ./src/common/assertion.h	2016-02-08 02:41:11.693762221 +0100
-@@ -66,6 +66,8 @@
- 	#define TEST(f,g) if(!(f)) { std::cerr << "Test fail :" << __FILE__ << ":" << __LINE__ << "\t"<< (g) << std::endl;return false;}
- 	#endif
- 
-+	#define TRACE(f) { timespec timeval; clock_gettime(CLOCK_MONOTONIC, &timeval); std::cerr  << "<" << f <<">" __FILE__ << ":" << __LINE__ << " t: " << timeval.tv_sec << "." << timeval.tv_nsec/1000 << endl;} 
-+
- 	//A hack to generate compile time asserts (thanks Internet).
- 	//This causes gcc to give "duplicate case value", if the predicate is false
- 	#ifndef  HAVE_CPP_1X
-@@ -79,6 +81,7 @@
- 	#define COMPILE_ASSERT(f)
- 	#define WARN(f,g) 
- 	#define TEST(f,g)
-+	#define TRACE(f)
- 	//Do we want to trap floating point exceptions 
- 	void trapfpe (bool doTrap=true);
- 		
-diff -ruh ./src/common/voxels.h ./common/voxels.h
---- ./src/common/voxels.h	2016-02-08 01:39:07.270999774 +0100
-+++ ./src/common/voxels.h	2016-02-08 02:41:11.705762248 +0100
-@@ -31,6 +31,9 @@
- #undef I 
- #undef Complex
- #include <typeinfo>
-+#if defined(WIN32) || defined(WIN64)
-+#include <vigra/windows.h>
-+#endif
- #include <vigra/multi_array.hxx>
- #include <vigra/multi_convolution.hxx>
- 
-@@ -1506,6 +1509,8 @@
- 		{
- 			size_t slicePos;
- 			slicePos=roundf(offset*binCount[normal]);
-+			if(slicePos == binCount[normal])
-+				slicePos--;
- 			getSlice(normal,slicePos,p);
- 			break;
- 		}
-diff -ruh ./src/gl/drawables.cpp ./gl/drawables.cpp
---- ./src/gl/drawables.cpp	2016-02-08 01:39:07.270999774 +0100
-+++ ./src/gl/drawables.cpp	2016-02-08 02:41:11.709762257 +0100
-@@ -45,7 +45,6 @@
- unsigned int DrawableObj::winX;
- unsigned int DrawableObj::winY;
- 
--DrawTexturedQuad DrawPointLegendOverlay::dQuad;
- bool DrawPointLegendOverlay::quadSet=false;
- //==
- 
-@@ -182,8 +181,8 @@
- {
- 	//TODO: Could speedup with LINE_STRIP/LOOP. This is 
- 	//not a bottleneck atm though.
-+	glColor4f(r,g,b,a);
- 	glBegin(GL_LINES);
--		glColor4f(r,g,b,a);
- 		//Bottom corner out (three lines from corner)
- 		glVertex3f(pMin[0],pMin[1],pMin[2]);
- 		glVertex3f(pMax[0],pMin[1],pMin[2]);
-@@ -725,6 +724,21 @@
- 		gluDeleteQuadric(q);
- }
- 
-+DrawableObj *DrawSphere::clone() const
-+{
-+	DrawSphere *d = new DrawSphere();
-+	d->r=r;
-+	d->g=g;
-+	d->b=b;
-+	d->a=a;
-+	d->origin=origin;
-+	d->radius=radius;
-+	d->latSegments=latSegments;
-+	d->longSegments=longSegments;
-+
-+	d->q=gluNewQuadric();
-+	return d;
-+}
- 
- void DrawSphere::getBoundingBox(BoundCube &b) const
- {
-@@ -1858,6 +1872,8 @@
- 	}
- 
- 
-+	float visGrey=getHighContrastValue();
-+
- 	//Draw the completed Steps
- 	float thetaPerStep =  thetaPerFilter/maxStep;
- 	for(size_t ui=1;ui<step;ui++)
-@@ -1871,7 +1887,7 @@
- 		if(ui < step-1)
- 		{
- 			//Draw a line to mark the step
--			glColor4f(1.0f,0.0f,0.0f,1.0f);
-+			glColor4f(visGrey,0.0f,0.0f,1.0f);
- 			glBegin(GL_LINES);
- 				glVertex3f(radiusIn*sin(curTheta),radiusIn*cos(curTheta),0);
- 				glVertex3f(radiusOut*sin(curTheta),radiusOut*cos(curTheta),0);
-@@ -1961,14 +1977,15 @@
- 		return;
- 
- 
-+	float visGrey= getHighContrastValue();
- 
- 
- 	const float ALPHA_COMPLETE=0.5*alphaBase;
- 	const float ALPHA_INCOMPLETE=0.15*alphaBase;
- 	if(complete)	
--		glColor4f(1.0f,1.0f,1.0f,ALPHA_COMPLETE);
-+		glColor4f(visGrey,visGrey,visGrey,ALPHA_COMPLETE);
- 	else
--		glColor4f(1.0f,1.0f,1.0f,ALPHA_INCOMPLETE);
-+		glColor4f(visGrey,visGrey,visGrey,ALPHA_INCOMPLETE);
- 
- 	//Draw arc
- 	glBegin(GL_TRIANGLE_STRIP);
-@@ -2214,6 +2231,12 @@
- 
- }
- 
-+DrawableObj *DrawColourBarOverlay::clone() const
-+{
-+	DrawColourBarOverlay *newBar = new DrawColourBarOverlay(*this);
-+	return newBar;
-+}
-+
- void DrawColourBarOverlay::setColourVec(const vector<float> &r,
- 					const vector<float> &g,
- 					const vector<float> &b)
-@@ -2237,45 +2260,6 @@
- 
- 	std::string tmpStr =getDefaultFontFile();
- 	font = new FTGLPolygonFont(tmpStr.c_str());
--
--	//check to see if we need to init the texture quad
--	if(!quadSet &&  texPool)
--	{
--
--		dQuad.setUseColouring(false);
--
--		//Create a circular texture
--		const unsigned int N_CHANNELS=4;
--		unsigned int LEG_TEX_SIZE = 256; 
--		unsigned char colourWhite[N_CHANNELS]= { 255,255,255,255 };
--		unsigned char colourBlack[N_CHANNELS]= { 0,0,0,0 };
--
--		//TODO: Convert to single channel texture, to save space?
--		// DrawQuad does not support single channel at this time
--		dQuad.resize(LEG_TEX_SIZE,LEG_TEX_SIZE,N_CHANNELS);
--		const float HALF_CIRCLE_R2 = 0.25; 
--		#pragma omp parallel for
--		for(unsigned int nX=0;nX<LEG_TEX_SIZE;nX++)
--		{
--			float fx;
--			fx= (float) nX/(float)LEG_TEX_SIZE - 0.5;
--			for(unsigned int nY=0;nY<LEG_TEX_SIZE;nY++)
--			{
--				float fy;
--				fy = (float) nY/(float)LEG_TEX_SIZE -0.5;
--				if( fx*fx + fy*fy < HALF_CIRCLE_R2) 
--					dQuad.setData(nX,nY,colourWhite);
--				else
--					dQuad.setData(nX,nY,colourBlack);
--			}
--
--		}
--
--		dQuad.rebindTexture(GL_RGBA);
--
--		quadSet=true;
--
--	}
- }
- 
- DrawPointLegendOverlay::~DrawPointLegendOverlay()
-@@ -2318,7 +2302,6 @@
- 
- 	float delta = std::max(std::min(1.0f/legendItems.size(),0.02f),0.05f);
- 	float size = delta*0.9f; 
--	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- 
- 	float maxTextWidth=0;
- 
-@@ -2329,18 +2312,17 @@
- 	{
- 		for(;ui<legendItems.size();ui++)
- 		{
-+			Draw2DCircle dCirc;
- 
--			//Draw textured quad (circle)
-+			//Draw circle
- 			//--
--			dQuad.setVertex(0,Point3D(curX,curY,0));
--			dQuad.setVertex(1,Point3D(curX+size,curY,0));
--			dQuad.setVertex(2,Point3D(curX+size,curY+size,0));
--			dQuad.setVertex(3,Point3D(curX,curY+size,0));
-+			dCirc.setCentre(curX+size/2.0f,curY+size/2.0f);
-+			dCirc.setRadius(size/2.0f);
- 
- 			const RGBFloat *f;
- 			f = &legendItems[ui].second;
--			glColor3f(f->v[0],f->v[1],f->v[2]);
--			dQuad.draw();
-+			dCirc.setColour(f->v[0],f->v[1],f->v[2]);
-+			dCirc.draw();
- 
- 
- 			//--
-@@ -2369,7 +2351,6 @@
- 		curX+=maxTextWidth + size;
- 		curY=position[1] + 0.5*delta;
- 	}
--	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- 
- void DrawPointLegendOverlay::addItem(const std::string &s, float r, float g, float b)
-@@ -2863,3 +2844,76 @@
- 	b.setInvalid();
- }
- 
-+Draw2DCircle::Draw2DCircle()
-+{
-+	angularStep = 2.0f*M_PI/180.0f;
-+	filled=true;
-+}
-+
-+void Draw2DCircle::draw() const
-+{
-+
-+	float nSteps = 2.0* M_PI/angularStep;
-+	WARN(nSteps > 1,"Draw2D Circle, too few steps");
-+	glColor4f(r,g,b,1.0f);
-+
-+	if(filled)
-+	{
-+		glBegin(GL_TRIANGLE_FAN);
-+			//Central vertex
-+			glVertex2fv(centre);
-+
-+			//vertices from [0,2PI)
-+			for(unsigned int ui=0;ui<nSteps;ui++)
-+			{
-+				float fx,fy,theta;
-+				theta = angularStep*ui;	
-+				fx = centre[0]+cos(-theta)*radius;
-+				fy = centre[1]+sin(-theta)*radius;
-+
-+				glVertex2f(fx,fy);
-+			}
-+
-+			//2PI vertex
-+			glVertex2f(centre[0]+radius,centre[1]);
-+		glEnd();
-+	}
-+	else
-+	{
-+		glBegin(GL_LINE_LOOP);
-+		//Central vertex
-+		for(unsigned int ui=0;ui<nSteps;ui++)
-+		{
-+			float fx,fy,theta;
-+			theta = angularStep*ui;	
-+			fx = centre[0]+cos(theta)*radius;
-+			fy = centre[1]+sin(theta)*radius;
-+
-+			glVertex2f(fx,fy);
-+		}
-+		glEnd();
-+	}
-+}
-+
-+void Draw2DCircle::getBoundingBox(BoundCube &b) const
-+{
-+
-+	b.setBounds(centre[0]-radius, centre[1]-radius,
-+			centre[0]+radius, centre[1]+radius,
-+			0,0);
-+}
-+
-+unsigned int Draw2DCircle::getType() const
-+{
-+	return DRAW_TYPE_2D_CIRCLE; 	
-+}
-+
-+
-+DrawableObj *Draw2DCircle::clone() const
-+{
-+	Draw2DCircle *p = new Draw2DCircle;
-+	*p = *this;	
-+
-+	return p;	
-+}
-+
-diff -ruh ./src/gl/drawables.h ./gl/drawables.h
---- ./src/gl/drawables.h	2016-02-08 01:39:07.274999775 +0100
-+++ ./src/gl/drawables.h	2016-02-08 02:41:11.709762257 +0100
-@@ -93,6 +93,7 @@
- 	DRAW_TYPE_CYLINDER,
- 	DRAW_TYPE_DISPLAYLIST,
- 	DRAW_TYPE_GLTEXT,
-+	DRAW_TYPE_2D_CIRCLE,
- 	DRAW_TYPE_RECTPRISM,
- 	DRAW_TYPE_COLOURBAR,
- 	DRAW_TYPE_TEXTUREDOVERLAY,
-@@ -531,6 +532,8 @@
- 		//! Destructor
- 		virtual ~DrawSphere();
- 
-+		virtual DrawableObj *clone() const;
-+
- 		virtual unsigned int getType() const {return DRAW_TYPE_SPHERE;};	
- 		//!Sets the location of the sphere's origin
- 		void setOrigin(const Point3D &p);
-@@ -899,6 +902,7 @@
- 		DrawColourBarOverlay(const DrawColourBarOverlay &o);
- 		~DrawColourBarOverlay(){delete font;};
- 		
-+		virtual DrawableObj *clone() const; 
- 
- 		virtual unsigned int getType() const {return DRAW_TYPE_COLOURBAR;}
- 
-@@ -1030,7 +1034,6 @@
- class DrawPointLegendOverlay : public DrawableOverlay
- {
- 	private:
--	static DrawTexturedQuad dQuad;
- 	static bool quadSet;
- 
- 	FTFont *font;
-@@ -1194,6 +1197,7 @@
- 		Point3D position;
- 		//!size
- 		float size;
-+
- 	public:
- 		DrawAxis();
- 		~DrawAxis();
-@@ -1211,5 +1215,41 @@
- 		void setPosition(const Point3D &p);
- 
- 		void getBoundingBox(BoundCube &b) const;
-+
- };
-+
-+
-+//Draw a 2D filled circle
-+class Draw2DCircle : public DrawableObj
-+{
-+	private:
-+		float centre[2];
-+		float angularStep;
-+		float radius; 
-+
-+		//Circle colour
-+		float r,g,b;
-+
-+		//Should the circle be drawn as an outline, or as a filled object
-+		bool filled;
-+	public:
-+		Draw2DCircle();
-+		
-+		void result() const; 
-+		//Obtain the type mask for this drawable
-+		virtual unsigned int getType() const;	
-+		virtual DrawableObj *clone() const; 
-+		virtual void getBoundingBox(BoundCube &b) const;
-+
-+		virtual void draw() const;
-+
-+		void setCentre(float fx,float fy) { centre[0] = fx; centre[1]= fy;};
-+		void setRadius(float r) { radius=r;}
-+		//Angular step in radiians
-+		void setAngularStep(float da) { angularStep = da;};
-+	
-+		void setColour(float rP, float gP, float bP) { r=rP;g=gP;b=bP;} ;
-+			
-+};
-+
- #endif
-diff -ruh ./src/gui/dialogs/animateFilterDialog.cpp ./gui/dialogs/animateFilterDialog.cpp
---- ./src/gui/dialogs/animateFilterDialog.cpp	2016-02-08 01:39:07.278999776 +0100
-+++ ./src/gui/dialogs/animateFilterDialog.cpp	2016-02-08 02:41:11.689762212 +0100
-@@ -408,7 +408,6 @@
- 	animationGrid->BeginBatch();
- 	if(animationGrid->GetNumberRows())
- 		animationGrid->DeleteRows(0,animationGrid->GetNumberRows());
--	animationGrid->EndBatch();
- 
- 
- 	animationGrid->AppendRows(propertyAnimator.getNumProps());
-@@ -441,6 +440,8 @@
- 		animationGrid->SetCellValue(ui,CELL_ENDFRAME, (str));
- 	}
- 
-+	animationGrid->EndBatch();
-+
- 	//Check for conflicting rows in the animation dialog,
- 	// and highlight them in colour
- 	set<size_t> conflictRows;
-@@ -641,12 +642,14 @@
- 
- void ExportAnimationDialog::OnFilterGridCellSelected(wxPropertyGridEvent &event)
- {
--	event.Veto();
- 
- 	wxTreeItemId tId=filterTreeCtrl->GetSelection();;
- 
- 	if(tId ==filterTreeCtrl->GetRootItem() || !tId.IsOk())
-+	{
-+		event.Veto();
- 		return;
-+	}
- 
- 	//Get the filter ID value 
- 	size_t filterId;
-@@ -859,6 +862,8 @@
- 			ASSERT(false); // that should cover all data types...
- 	}
- 
-+	event.Veto();
-+
- 	//Add property to animator
- 	propertyAnimator.addProp(frameProp);
- 
-diff -ruh ./src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp ./gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp
---- ./src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp	2016-02-08 01:39:07.278999776 +0100
-+++ ./src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp	2016-02-08 02:41:11.705762248 +0100
-@@ -25,6 +25,10 @@
- #include <wx/colordlg.h>
- 
- 
-+//TODO: Currently we change the foreground text in a button to set colour.
-+// better would be to use a "swatch"
-+
-+
- using std::string;
- 
- // begin wxGlade: ::extracode
-@@ -195,16 +199,26 @@
- {
- 	bool isOK=true;
- 	isOK&=startFrameOK;
--	isOK&=endFrameOK;
-+	isOK&= (endFrameOK || transitionMode == TRANSITION_STEP) ;
- 
--	//Ensure start frame is > end frame
- 	if(isOK)
- 	{
--		isOK&=(startFrame<endFrame);
-+		if(transitionMode == TRANSITION_INTERP) 
-+		{
-+			//Ensure start frame is > end frame, 
-+			// if we are using interp mode
-+			isOK&=(startFrame<endFrame);
-+		}
-+
- 		if(!isOK)
- 		{
-+			//If there is a problem, mark the problem with a colour background
- 			textFrameStart->SetBackgroundColour(*wxCYAN);
--			textFrameEnd->SetBackgroundColour(*wxCYAN);
-+			if(transitionMode == TRANSITION_INTERP)
-+				textFrameEnd->SetBackgroundColour(*wxCYAN);
-+			else
-+				textFrameEnd->SetBackgroundColour(wxNullColour);
-+				
- 		}
- 		else
- 		{
-@@ -212,7 +226,8 @@
- 			textFrameEnd->SetBackgroundColour(wxNullColour);
- 		}
- 	}
--	
-+
-+	//Enable the OK button if all properties are set appropriately
- 	buttonOK->Enable(isOK);
- }
- 
-diff -ruh ./src/gui/dialogs/StashDialog.cpp ./gui/dialogs/StashDialog.cpp
---- ./src/gui/dialogs/StashDialog.cpp	2016-02-08 01:39:07.274999775 +0100
-+++ ./src/gui/dialogs/StashDialog.cpp	2016-02-08 02:41:11.689762212 +0100
-@@ -164,12 +164,11 @@
- 		string strTmp;
- 		pair<std::string,FilterTree> stash;
- 		long itemIdx;
--		
-+		visControl->state.copyStashedTree(ui,stash);
- 		//First item is the stash name
- 		itemIdx = listStashes->InsertItem(ui,stash.first);
- 
- 		//Second column is num filters
--		visControl->state.copyStashedTree(ui,stash);
- 		stream_cast(strTmp,stash.second.size());
- 		listStashes->SetItem(itemIdx,1,(strTmp));
- 
-@@ -329,6 +328,7 @@
- void StashDialog::OnBtnRemove(wxCommandEvent &event)
- {
- 	//Spin through the list, to find the selected items
-+	vector<size_t> itemsToRemove;
- 	int item=-1;
- 	for ( ;; )
- 	{
-@@ -337,15 +337,12 @@
- 					     wxLIST_STATE_SELECTED);
- 		if ( item == -1 )
- 			break;
--
--		visControl->state.eraseStash(listStashes->GetItemData(item));
--		updateList();
--		updateTree();
--		updateGrid();
--		
-+		itemsToRemove.push_back(listStashes->GetItemData(item));
- 	}
- 	
-+	visControl->state.eraseStashes(itemsToRemove);
- 
-+	ready();
- }
- 
- void StashDialog::do_layout()
-diff -ruh ./src/gui/glPane.cpp ./gui/glPane.cpp
---- ./src/gui/glPane.cpp	2016-02-08 01:39:07.278999776 +0100
-+++ ./src/gui/glPane.cpp	2016-02-08 02:41:11.721762284 +0100
-@@ -210,6 +210,13 @@
- 	wxPaintEvent ptEvent;
- 	wxPostEvent(this,ptEvent);
- 
-+#ifdef WIN32
-+	//Hack for windows. Does not redraw otherwise.
-+	// Refresh and Update in tandom dont work.
-+	Show(false);
-+	Show(true);
-+#endif
-+
- }
- 
- // some useful events to use
-@@ -635,7 +642,7 @@
- 		}
- 		break;
- 		default:
--			event.Skip();
-+			event.Skip(true);
- 	}
- }
- 
-@@ -658,6 +665,7 @@
- 	if(event.ShiftDown())
- 		cameraMoveRate*=5;
- 
-+	bool update=true;
- 	switch(event.GetKeyCode())
- 	{
- 		case '-':
-@@ -687,10 +695,12 @@
- 			break;
- 		}
- 		default:
--			event.Skip();
-+			event.Skip(true);
-+			update=false;
- 	}
- 
--	Refresh();
-+	if(update)
-+		Refresh();
- }
- 
-  
-@@ -1051,10 +1061,11 @@
- 		free(imageBuffer);
- 
- 		combineWxImage(*image,imageOverlay);
-+
-+		//Free the tile buffer
-+		trDelete(tr);
- 	}
- 
--	//Free the tile buffer
--	trDelete(tr);
- 	
- 	//--------------	
- 	bool isOK=image->SaveFile(filename,wxBITMAP_TYPE_PNG);
-diff -ruh ./src/gui/mainFrame.cpp ./gui/mainFrame.cpp
---- ./src/gui/mainFrame.cpp	2016-02-08 01:39:07.282999776 +0100
-+++ ./src/gui/mainFrame.cpp	2016-02-08 02:41:11.805762472 +0100
-@@ -390,6 +390,7 @@
- 	verCheckThread=0;
- 	refreshThread=0;
- 	refreshControl=0;
-+	ensureResultVisible=false;
- 	lastProgressData.reset();
- 
- 	//Set up the program icon handler
-@@ -662,7 +663,6 @@
-     checkWeakRandom = new wxCheckBox(noteTools, ID_CHECK_WEAKRANDOM, TRANS("Fast and weak randomisation."));
-     checkWeakRandom->SetValue(true);
-     checkLimitOutput = new wxCheckBox(noteTools, ID_CHECK_LIMIT_POINT_OUT, TRANS("Limit Output Pts"));
-- //   checkLimitOutput->SetValue((visControl.getIonDisplayLimit() !=0));
-     std::string tmpStr;
- //    stream_cast(tmpStr,visControl.getIonDisplayLimit());
-     textLimitOutput = new wxTextCtrl(noteTools, ID_TEXT_LIMIT_POINT_OUT, (tmpStr),
-@@ -754,7 +754,16 @@
- 	initedOK=true;   
- 
- 
--
-+	// Set the limit value checkbox and text field with the
-+	// value obtained from the configuration file
-+	unsigned int ionLimit=visControl.getIonDisplayLimit(); 
-+	checkLimitOutput->SetValue((ionLimit!=0));
-+	if(ionLimit)
-+	{
-+		std::string sValue;
-+		stream_cast(sValue,visControl.getIonDisplayLimit());
-+		textLimitOutput->SetValue(sValue);
-+	}	
- 
- 
- 
-@@ -1083,6 +1092,8 @@
- 	//Load the file
- 	if(!loadFile(wxF.GetPath()))
- 	{
-+		//If the load failed, do not try to set the 
-+		// selection & visibility
- 		visControl.clearTreeFilterViewPersistence();
- 		return;
- 	}
-@@ -1093,15 +1104,6 @@
- 	configFile.addRecentFile(tmp);
- 	//Update the "recent files" menu
- 	recentHistory->AddFileToHistory(wxF.GetPath());
--
--	//If we are using the default camera,
--	//move it to make sure that it is visible
--	if(visControl.state.getNumCams() == 1)
--	{
--		visControl.scene.ensureVisible(3);
--	}
--
--	panelTop->forceRedraw();
- }
- 
- void MainWindowFrame::OnFileMerge(wxCommandEvent &event)
-@@ -1123,7 +1125,6 @@
- 
- 	statusMessage(TRANS("Merged file."),MESSAGE_INFO);
- 
--	panelTop->forceRedraw();
- 
- 	setSaveStatus();
- }
-@@ -1233,19 +1234,6 @@
- 		statusMessage(TRANS("Tip: You can use ctrl to merge"),MESSAGE_HINT);
- #endif
- 	}
--
--	if(loaded || rangeLoaded)
--		doSceneUpdate();
--
--	if(files.Count())
--	{
--		//If we are using the default camera,
--		//move it to make sure that it is visible
--		if(visControl.state.getNumCams() == 1)
--		{
--			visControl.scene.ensureVisible(3);
--		}
--	}
- }
- 
- bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate)
-@@ -1372,7 +1360,7 @@
- 	updateWxTreeCtrl(treeFilters);
- 
- 	if(!noUpdate)
--		return doSceneUpdate();
-+		return doSceneUpdate(true);
- 
- 	return true;
- }	
-@@ -1420,10 +1408,6 @@
- 		}
- 		
- 		setSaveStatus();
--
--		//make sure camera is properly centred
--		if(visControl.state.getNumCams() == 1)
--			visControl.scene.ensureVisible(3);
- 	}
- 
- }
-@@ -1725,7 +1709,12 @@
- 			unsigned int nFilters;
- 			nFilters = visControl.state.treeState.size();
- 			comboFilters->Enable(!locking && nFilters);
--			refreshButton->Enable(!locking && nFilters);
-+			if(locking)
-+				refreshButton->SetLabel(TRANS("Abo&rt"));
-+			else
-+				refreshButton->SetLabel(TRANS("&Refresh"));
-+			refreshButton->Enable(nFilters);
-+			
- 			btnFilterTreeErrs->Enable(!locking);
- 			treeFilters->Enable(!locking);	
- 
-@@ -2899,7 +2888,7 @@
- 	info.SetName((PROGRAM_NAME));
- 	info.SetVersion((PROGRAM_VERSION));
- 	info.SetDescription(TRANS("Quick and dirty analysis for point data.")); 
--	info.SetWebSite(wxT("https://sourceforge.net/apps/phpbb/threedepict/"));
-+	info.SetWebSite(wxT("https://threedepict.sourceforge.net/"));
- 
- 	info.AddDeveloper(wxT("D. Haley"));	
- 	info.AddDeveloper(wxT("A. Ceguerra"));	
-@@ -2991,7 +2980,7 @@
- void MainWindowFrame::OnComboFilterText(wxCommandEvent &event)
- {
- 	//prevent user from modifying text
--	comboFilters->ChangeValue(TRANS(ADD_FILTER_TEXT));
-+	//comboFilters->ChangeValue(TRANS(ADD_FILTER_TEXT));
- }
- 
- void MainWindowFrame::OnComboStash(wxCommandEvent &event)
-@@ -3447,7 +3436,9 @@
- void MainWindowFrame::OnGridCameraPropertyChange(wxPropertyGridEvent &event)
- {
- 
--	if(programmaticEvent)
-+	//Check for inited OK. Seem to be getting called before 
-+	//do_layout is complete.
-+	if(programmaticEvent || !initedOK)
- 	{
- 		event.Veto();
- 		return;
-@@ -3522,10 +3513,17 @@
- 	backCameraPropGrid->SetExtraStyle(PROPERTY_GRID_EXTRA_STYLE);
- 	
- 	visControl.updateCameraPropGrid(backCameraPropGrid,cameraId);
-+	int columnPos =gridCameraProperties->GetSplitterPosition();
- 	
- 	std::swap(backCameraPropGrid,gridCameraProperties);
- 	do_cameragrid_prop_layout();
-+	gridCameraProperties->SetSplitterPosition(columnPos);
- 
-+#ifdef __WIN32
-+	//Move the splitter panel
-+	splitLeftRight->SetSashPosition(splitLeftRight->GetSashPosition()+1);
-+	splitLeftRight->SetSashPosition(splitLeftRight->GetSashPosition()-1);
-+#endif
- 	//Ensure that the GL panel shows latest cam orientation 
- 	panelTop->forceRedraw();
- 	programmaticEvent=false;
-@@ -3583,7 +3581,7 @@
- 	}
- 	else
- 	{
--
-+		ASSERT(camName.size());
- 		//Create a new camera for the scene.
- 		visControl.state.addCam(camName,true);
- 		
-@@ -3773,7 +3771,7 @@
- 	
- }
- 
--bool MainWindowFrame::doSceneUpdate()
-+bool MainWindowFrame::doSceneUpdate(bool ensureVisible)
- {
- 	//Update scene
- 	ASSERT(!currentlyUpdatingScene);
-@@ -3800,6 +3798,8 @@
- 	//reset the progress timer animation
- 	visControl.scene.resetProgressAnim();
- 
-+	ensureResultVisible=ensureVisible;
-+
- 	ASSERT(!refreshControl);
- 	refreshControl = new RefreshController(visControl.state.treeState);
- 	refreshThread=new RefreshThread(this,refreshControl);
-@@ -3871,7 +3871,6 @@
- 	//Restore the UI elements to their interactive state
- 	setLockUI(false);
- 
--	panelTop->forceRedraw();
- 	panelSpectra->Refresh(false);	
- 
- 	updateEditRangeMenu();
-@@ -3945,6 +3944,19 @@
- 		MainFrame_statusbar->SetStatusText(TRANS("Complete"),1);
- 		MainFrame_statusbar->SetStatusText("",2);
- 	}
-+
-+
-+	if(ensureResultVisible)
-+	{
-+		//If we are using the default camera,
-+		//move it to make sure that it is visible
-+		if(visControl.state.getNumCams() == 1)
-+			visControl.scene.ensureVisible(3);
-+
-+		ensureResultVisible=false;
-+	}
-+
-+
- 	//restart the update timer, to check for updates from the backend
- 	updateTimer->Start(UPDATE_TIMER_DELAY);
- }
-@@ -4091,12 +4103,6 @@
- 
- 	if(requireFirstUpdate && !refreshThreadActive())
- 	{
--		//If we are using the default camera,
--		//move it to make sure that it is visible
--		if(visControl.state.getNumCams() == 1)
--			visControl.scene.ensureVisible(3);
--
--
- 		doSceneUpdate();
- 
- 		requireFirstUpdate=false;
-@@ -4178,6 +4184,7 @@
- 	}
- 
- 
-+	
- 	programmaticEvent=false;	
- }
- 
-@@ -4582,6 +4589,7 @@
- 	Thaw();
- }
- 
-+
- //This routine is used by other UI processes to trigger an abort
- void MainWindowFrame::OnProgressAbort(wxCommandEvent &event)
- {
-@@ -4609,8 +4617,12 @@
- 	if(!gridCameraProperties || !gridFilterPropGroup)
- 		return;
- 
-+	//Run abort code as needed
- 	if(currentlyUpdatingScene || refreshThreadActive())
-+	{
-+		OnProgressAbort(event);
- 		return;
-+	}
- 
- 	//dirty hack to get keyboard state.
- 	wxMouseState wxm = wxGetMouseState();
-@@ -4817,6 +4829,16 @@
- 		programmaticEvent=false;
- 
- 		setSaveStatus();
-+
-+		// There is one camera that we cannot access
-+		// TODO: This logic should not be here, but in the widget update
-+		if(visControl.state.getNumCams() > 1)
-+		{
-+			visControl.updateCameraComboBox(comboCamera);
-+			visControl.updateCameraPropGrid(gridCameraProperties,visControl.state.getActiveCam());
-+		}
-+		else
-+			gridCameraProperties->Clear();
- 	}
- 	
- }
-diff -ruh ./src/gui/mainFrame.h ./gui/mainFrame.h
---- ./src/gui/mainFrame.h	2016-02-08 01:39:07.282999776 +0100
-+++ ./src/gui/mainFrame.h	2016-02-08 02:41:11.805762472 +0100
-@@ -117,7 +117,7 @@
- 	//!Update the progress information in the status bar
- 	void updateProgressStatus();
- 	//!Perform an update to the 3D Scene. Returns false if refresh failed
--	bool doSceneUpdate();
-+	bool doSceneUpdate(bool ensureResultVisible=false);
- 	
- 	//!Complete the scene update. Returns false if failed
- 	void finishSceneUpdate(unsigned int errCode);
-@@ -162,6 +162,8 @@
- 	bool currentlyUpdatingScene;
- 	//!Have we aborted an update
- 	bool haveAborted;
-+	//!Should the gui ensure that the refresh result is visible at the next update?
-+	bool ensureResultVisible;
- 
- 	//!source item when dragging a filter in the tree control
- 	wxTreeItemId *filterTreeDragSource;
-diff -ruh ./src/testing/testing.cpp ./testing/testing.cpp
---- ./src/testing/testing.cpp	2016-02-08 01:39:07.286999777 +0100
-+++ ./src/testing/testing.cpp	2016-02-08 02:41:11.805762472 +0100
-@@ -553,7 +553,7 @@
- {
- 	if(!testAnderson())
- 		return false;
--	if(!testBackgroundFit())
-+	if(!testBackgroundFitMaths())
- 		return false;
- 
- 	if(!K3DMk2Tests())
diff --git a/debian/patches/diff_0.0.19_to_effd078610a7 b/debian/patches/diff_0.0.19_to_effd078610a7
new file mode 100644
index 0000000..27e5e88
--- /dev/null
+++ b/debian/patches/diff_0.0.19_to_effd078610a7
@@ -0,0 +1,1235 @@
+diff -r d478204af715 -r effd078610a7 docs/manual-latex/build-latex.sh
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/docs/manual-latex/build-latex.sh	Tue Jun 21 12:45:20 2016 +0100
+@@ -0,0 +1,37 @@
++#!/bin/bash
++LATEXBIN=pdflatex
++BIBTEXBIN=bibtex
++if [ x`which $LATEXBIN` == x"" ] ; then
++	echo "no $LATEXBIN" 
++	exit 1
++fi
++
++if [ x`which $BIBTEXBIN` == x"" ] ; then
++	echo "no $BIBTEXBIN" 
++	exit 1
++fi
++
++MANUAL=manual.tex
++
++#check to see if the manual and its bib file are around
++if [ ! -f $MANUAL  ] ; then
++	echo "$MANUAL is missing"
++	exit 1
++fi
++
++#Remove the old manul
++rm -f manual.pdf
++
++
++
++#run the multi-pass latex build. 
++$LATEXBIN $MANUAL || { echo "failed latex build (pass 1 of 2)"; exit 1 ; }
++$BIBTEXBIN `basename -s .tex $MANUAL` || { echo "failed bibtex build"; exit 1 ; }
++$LATEXBIN $MANUAL || { echo "failed latex build (pass 2 of 2)"; exit 1 ; }
++
++
++if [ ! -f manual.pdf ] ; then
++	echo "latex ran, but somehow we did not output the PDF...."
++	exit 1
++fi
++
+diff -r d478204af715 -r effd078610a7 src/backend/filter.cpp
+--- a/src/backend/filter.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filter.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -553,7 +553,7 @@
+ 		ASSERT(scatterIntensity.empty());
+ 
+ 
+-	ASSERT(plotType < PLOT_TYPE_ENUM_END);
++	ASSERT(plotStyle < PLOT_TYPE_ENUM_END);
+ }
+ void RangeStreamData::checkSelfConsistent() const
+ {
+diff -r d478204af715 -r effd078610a7 src/backend/filter.h
+--- a/src/backend/filter.h	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filter.h	Tue Jun 21 12:45:20 2016 +0100
+@@ -395,7 +395,7 @@
+ 		//Label for X, Y axes
+ 		std::string xLabel,yLabel;
+ 
+-		unsigned int plotType;
++		unsigned int plotStyle;
+ 
+ 		//!Structured XY data pairs for plotting curve
+ 		Array2D<float> xyData;
+diff -r d478204af715 -r effd078610a7 src/backend/filters/algorithms/rdf.cpp
+--- a/src/backend/filters/algorithms/rdf.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/algorithms/rdf.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -862,6 +862,8 @@
+ 		return RDF_ABORT_FAIL;
+ #endif
+ 
++	*progressPtr=100;
++
+ 	return 0;
+ }
+ 
+@@ -1003,6 +1005,8 @@
+ #endif
+ 
+ 	//Calculations complete!
++	*progressPtr=100;
++
+ 	return 0;
+ }
+ 
+diff -r d478204af715 -r effd078610a7 src/backend/filters/annotation.cpp
+--- a/src/backend/filters/annotation.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/annotation.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -167,8 +167,15 @@
+ 
+ 	//If we are not enabled, do not draw anything into the output
+ 	if(!active)
++	{
++		progress.filterProgress=100;
+ 		return 0;
++	}
+ 
++	progress.step=1;
++	progress.maxStep=1;
++	progress.stepName=TRANS("Draw");
++	
+ 	DrawStreamData *d; 
+ 	d = new DrawStreamData;
+ 	d->parent=this;
+@@ -505,6 +512,7 @@
+ 	d->cached=0;
+ 	getOut.push_back(d);
+ 
++	progress.filterProgress=100;
+ 	return 0;
+ }
+ 
+diff -r d478204af715 -r effd078610a7 src/backend/filters/boundingBox.cpp
+--- a/src/backend/filters/boundingBox.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/boundingBox.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -510,7 +510,6 @@
+ 				}
+ #endif
+ 				bTotal.expand(bThis);
+-				progress.filterProgress=100;
+ 				break;
+ 			}
+ 			default:
+@@ -521,6 +520,7 @@
+ 		getOut.push_back(dataIn[ui]);	
+ 	}
+ 
++	progress.filterProgress=100;
+ 	//Append the bounding box if it is valid
+ 	if(bTotal.isValid())
+ 	{
+diff -r d478204af715 -r effd078610a7 src/backend/filters/clusterAnalysis.cpp
+--- a/src/backend/filters/clusterAnalysis.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/clusterAnalysis.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -685,7 +685,7 @@
+ 		Plot2DStreamData *p = new Plot2DStreamData;
+ 		p->parent=this;
+ 
+-		p->plotType=PLOT_2D_SCATTER;
++		p->plotStyle=PLOT_2D_SCATTER;
+ 		p->dataLabel=TRANS("Morphology Plot");
+ 		p->xLabel=TRANS("\\lambda_1:\\lambda_2 ratio");
+ 		p->yLabel=TRANS("\\lambda_2:\\lambda_3 ratio");
+diff -r d478204af715 -r effd078610a7 src/backend/filters/ionColour.cpp
+--- a/src/backend/filters/ionColour.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/ionColour.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -271,12 +271,14 @@
+ 	
+ 
+ 	p.name=TRANS("Show Bar");
++	p.helpText=TRANS("Display the colour legend in the 3D view");
+ 	p.key=KEY_IONCOLOURFILTER_SHOWBAR;
+ 	p.data=boolStrEnc(showColourBar);
+ 	p.type=PROPERTY_TYPE_BOOL;
+ 	propertyList.addProperty(p,curGroup);
+ 	
+ 	p.name=TRANS("Opacity");
++	p.helpText=TRANS("How see-through to make the legend (0- transparent, 1- solid)");
+ 	p.key=KEY_IONCOLOURFILTER_ALPHA;
+ 	stream_cast(p.data,alpha);
+ 	p.type=PROPERTY_TYPE_REAL;
+diff -r d478204af715 -r effd078610a7 src/backend/filters/ionDownsample.cpp
+--- a/src/backend/filters/ionDownsample.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/ionDownsample.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -88,7 +88,7 @@
+ 			rsdIncoming = new RangeStreamData;
+ 			*rsdIncoming=*c;
+ 
+-			if(ionFractions.size() != c->rangeFile->getNumIons())
++			if(ionFractions.size() != c->rangeFile->getNumIons()+1)
+ 			{
+ 				//set up some defaults; seeded from normal
+ 				ionFractions.resize(c->rangeFile->getNumIons()+1,fraction);
+@@ -464,7 +464,8 @@
+ 
+ 	propertyList.setGroupTitle(curGroup,TRANS("Mode"));
+ 	curGroup++;
+-	if(rsdIncoming && perSpecies)
++
++	if(rsdIncoming && perSpecies && rsdIncoming->enabledIons.size())
+ 	{
+ 		unsigned int typeVal;
+ 		if(fixedNumOut)
+diff -r d478204af715 -r effd078610a7 src/backend/filters/ionInfo.cpp
+--- a/src/backend/filters/ionInfo.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/ionInfo.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -398,7 +398,7 @@
+ 
+ 
+ 	//"Pairwise events" - where we perform an action if both 
+-	//These
++	//these are set
+ 	if(wantIonCounts && wantVolume)
+ 	{
+ 		if(computedVol > sqrtf(std::numeric_limits<float>::epsilon()))
+@@ -420,6 +420,9 @@
+ 		}
+ 	}
+ 
++
++	progress.filterProgress=100;
++	
+ 	return 0;
+ }
+ 
+diff -r d478204af715 -r effd078610a7 src/backend/filters/spatialAnalysis.cpp
+--- a/src/backend/filters/spatialAnalysis.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/spatialAnalysis.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -655,115 +655,180 @@
+ 		return ERR_FILE_READ_FAIL;
+ 
+ 
+-
+-	progress.step=3;
+-	progress.stepName=TRANS("Build");
+-	progress.filterProgress=0;
+-
+-	//Build the search tree we will use to perform replacement
+-	K3DTreeMk2 tree;
+-	tree.resetPts(fileIons,false);
+-	if(!tree.build())
+-		return ERR_ABORT_FAIL;
+-	BoundCube b;
+-	tree.getBoundCube(b);
+-
+-	//map the offset of the nearest to
+-	//the tree ID 
+-	vector<size_t > nearestVec;
+-	nearestVec.resize(inIons.size());
+-
+-	//TODO: pair vector might be faster
+-	// as we can use it in sequence, and can use openmp
+-	map<size_t,size_t> matchedMap;
+-
+-	//Find the nearest point for all points in the dataset
+-
+-	#pragma omp parallel for 
+-	for(size_t ui=0;ui<inIons.size();ui++)
++	vector<IonHit> outIons;
++	if(inIons.empty() || fileIons.empty())
+ 	{
+-		nearestVec[ui]=tree.findNearestUntagged(inIons[ui].getPos(),b,false);
+-	}
+-
+-	float sqrReplaceTol=replaceTolerance*replaceTolerance;
+-
+-	//Filter this to only points that had an NN within range
+-	#pragma omp parallel for 
+-	for(size_t ui=0;ui<inIons.size();ui++)
+-	{
+-		if(nearestVec[ui]!=(size_t)-1 && inIons[ui].getPos().sqrDist(*tree.getPt(nearestVec[ui])) <=sqrReplaceTol)
++		//Performance increase if we have an empty item.
++		// - in this case we can swap sets around
++		switch(replaceMode)
+ 		{
+-			#pragma omp critical
+-			matchedMap[ui]=tree.getOrigIndex(nearestVec[ui]);
++			case REPLACE_MODE_UNION:
++			{
++				//If the local data is empty, then the union is just the "b" data (swap).
++				// if nonempty, then it is simply the "a" data 
++				if(inIons.empty())
++					outIons.swap(fileIons);
++				else
++					outIons.swap(inIons);
++				break;
++			}
++			case REPLACE_MODE_SUBTRACT:
++			{
++				//if either localdata OR bdata is empty, then we don't need to do anything.
++				// either way, input stays as it is
++				outIons.swap(inIons);
++				break;
++			}
++			case REPLACE_MODE_INTERSECT:
++			{
++				//intersection with empty set is empty set.
++				// might as well clear the ions incoming
++				inIons.clear();
++				break;
++			}
++			default:
++				ASSERT(false);
++
+ 		}
+ 	}
+-
+-	nearestVec.clear();
+-
+-
+-	progress.step=4;
+-	progress.stepName=TRANS("Compute");
+-	progress.filterProgress=0;
+-
+-	//Finish if no matches
+-	if(matchedMap.empty())
++	else
+ 	{
+-		progress.filterProgress=100;
+-		return 0;
+-	}
+-
+-	vector<IonHit> outIons;
+-	switch(replaceMode)
+-	{
+-		case REPLACE_MODE_SUBTRACT:
++
++		progress.step=3;
++		progress.stepName=TRANS("Build");
++		progress.filterProgress=0;
++
++		//TODO: Possible speed increase by finding the smaller of
++		// the two inputs, and using that to build the tree
++
++		//Build the search tree we will use to perform replacement
++		K3DTreeMk2 tree;
++		tree.resetPts(fileIons,false);
++		if(!tree.build())
++			return ERR_ABORT_FAIL;
++		BoundCube b;
++		tree.getBoundCube(b);
++
++		//map the offset of the nearest to
++		//the tree ID 
++		vector<size_t > nearestVec;
++		nearestVec.resize(inIons.size());
++
++		//TODO: pair vector might be faster
++		// as we can use it in sequence, and can use openmp
++		map<size_t,size_t> matchedMap;
++
++		//Find the nearest point for all points in the dataset
++		// maps the ith ion in "inions" to the tree value
++		#pragma omp parallel for 
++		for(size_t ui=0;ui<inIons.size();ui++)
+ 		{
+-			//In subtraction mode, we should have
+-			// at least this many ions
+-			if(inIons.size() > matchedMap.size())
+-				outIons.reserve(inIons.size()-matchedMap.size());
+-			
+-			//
+-			#pragma omp parallel for
+-			for(unsigned int ui=0;ui<inIons.size();ui++)
++			nearestVec[ui]=tree.findNearestUntagged(inIons[ui].getPos(),b,false);
++		}
++
++		float sqrReplaceTol=replaceTolerance*replaceTolerance;
++
++		//Filter this to only points that had an NN within range
++		#pragma omp parallel for 
++		for(size_t ui=0;ui<inIons.size();ui++)
++		{
++			if(nearestVec[ui]!=(size_t)-1 && inIons[ui].getPos().sqrDist(*tree.getPt(nearestVec[ui])) <=sqrReplaceTol)
+ 			{
+-				map<size_t,size_t>::iterator it;
+-				it=matchedMap.find(ui);
+-				if(it != matchedMap.end())
+-					continue;
+-
+ 				#pragma omp critical
+-				outIons.push_back(inIons[ui]);
++				matchedMap[ui]=tree.getOrigIndex(nearestVec[ui]);
+ 			}
+-			break;
+ 		}
+-		case REPLACE_MODE_INTERSECT:
++
++		nearestVec.clear();
++
++
++		progress.step=4;
++		progress.stepName=TRANS("Compute");
++		progress.filterProgress=0;
++
++
++		//now we have a map that matches as so:
++		// map ( "inIon" ID -> "fileIon" ID)
++		// inIon should be our "A" in "A operator B"
++		switch(replaceMode)
+ 		{
+-			outIons.reserve(matchedMap.size());
+-
+-			if(replaceMass)
++			case REPLACE_MODE_SUBTRACT:
+ 			{
+-				for(map<size_t,size_t>::const_iterator it=matchedMap.begin();it!=matchedMap.end();++it)
++				//If no matches, A-0 = A. Just return input
++				if(matchedMap.empty())
+ 				{
+-					outIons.push_back(fileIons[it->second]);
+-					ASSERT(fileIons[it->second].getPosRef().sqrDist(inIons[it->first].getPosRef()) < sqrReplaceTol);
++					outIons.swap(inIons);
++					break;
+ 				}
++				//In subtraction mode, we should have
++				// at least this many ions
++				if(inIons.size() > matchedMap.size())
++					outIons.reserve(inIons.size()-matchedMap.size());
++				
++				//
++				#pragma omp parallel for
++				for(unsigned int ui=0;ui<inIons.size();ui++)
++				{
++					map<size_t,size_t>::iterator it;
++					it=matchedMap.find(ui);
++					if(it != matchedMap.end())
++						continue;
++
++					#pragma omp critical
++					outIons.push_back(inIons[ui]);
++				}
++				break;
+ 			}
+-			else
++			case REPLACE_MODE_INTERSECT:
+ 			{
+-				for(map<size_t,size_t>::const_iterator it=matchedMap.begin();it!=matchedMap.end();++it)
++				//Finish if no matches
++				if(matchedMap.empty())
++					break;
++				
++				outIons.reserve(matchedMap.size());
++
++				if(replaceMass)
+ 				{
+-					outIons.push_back(inIons[it->first]);
++					for(map<size_t,size_t>::const_iterator it=matchedMap.begin();it!=matchedMap.end();++it)
++					{
++						outIons.push_back(fileIons[it->second]);
++						ASSERT(fileIons[it->second].getPosRef().sqrDist(inIons[it->first].getPosRef()) < sqrReplaceTol);
++					}
+ 				}
++				else
++				{
++					for(map<size_t,size_t>::const_iterator it=matchedMap.begin();it!=matchedMap.end();++it)
++					{
++						outIons.push_back(inIons[it->first]);
++					}
++				}
++				break;
+ 			}
+-			break;
++			case REPLACE_MODE_UNION:
++			{
++				outIons.swap(fileIons);
++				outIons.reserve(outIons.size() + fileIons.size() - matchedMap.size());
++				map<size_t,size_t>::const_iterator it=matchedMap.begin();
++				
++
++				for(unsigned int ui=0;ui<inIons.size();ui++)
++				{
++					if(it !=matchedMap.end() && (it->first == ui) )
++					{
++						it++;
++						continue;
++					}
++
++
++					outIons.push_back(inIons[ui]);
++				}
++
++
++				break;
++			}
++			default:
++				ASSERT(false);
+ 		}
+-		case REPLACE_MODE_UNION:
+-		{
+-			ASSERT(false);
+-			break;
+-		}
+-		default:
+-			ASSERT(false);
+ 	}
+ 
+ 	//Only output ions if any were found
+@@ -2779,6 +2844,8 @@
+ 				newD->data.resize(d->data.size());
+ 				if(stopMode == STOP_MODE_NEIGHBOUR)
+ 				{
++					unsigned int nProg=0;
++
+ 					bool spin=false;
+ 					#pragma omp parallel for shared(spin)
+ 					for(size_t uj=0;uj<d->data.size();uj++)
+@@ -2811,14 +2878,15 @@
+ 						}
+ 
+ 						res.clear();
++						#pragma atomic
++						nProg++;
+ 						
+ 						//Update progress as needed
+ 						if(!curProg--)
+ 						{
+ 							#pragma omp critical 
+ 							{
+-							n+=NUM_CALLBACK/(nnMax);
+-							progress.filterProgress= (unsigned int)(((float)n/(float)totalDataSize)*100.0f);
++							progress.filterProgress= (unsigned int)(((float)nProg/(float)totalDataSize)*100.0f);
+ 							if(*Filter::wantAbort)
+ 								spin=true;
+ 							curProg=NUM_CALLBACK/(nnMax);
+@@ -3063,9 +3131,10 @@
+ 				IonStreamData *newD = new IonStreamData;
+ 				newD->parent=this;
+ 
+-				//Adjust this number to provide more update thanusual, because we
++				//Adjust this number to provide more update than usual, because we
+ 				//are not doing an o(1) task between updates; yes, it is a hack
+-				unsigned int curProg=NUM_CALLBACK/(10*nnMax);
++				const unsigned int PROG_PER_PASS=NUM_CALLBACK/(10*nnMax);
++				unsigned int curProg=PROG_PER_PASS;
+ 				newD->data.reserve(d->data.size());
+ 				if(stopMode == STOP_MODE_NEIGHBOUR)
+ 				{
+@@ -3113,11 +3182,11 @@
+ 						{
+ 							#pragma omp critical 
+ 							{
+-							n+=NUM_CALLBACK/(nnMax);
++							n+=PROG_PER_PASS;
+ 							progress.filterProgress= (unsigned int)(((float)n/(float)totalDataSize)*100.0f);
+ 							if(*Filter::wantAbort)
+ 								spin=true;
+-							curProg=NUM_CALLBACK/(nnMax);
++							curProg=PROG_PER_PASS;
+ 							}
+ 						}
+ 					}
+@@ -3263,6 +3332,8 @@
+ 				break;
+ 		}
+ 	}
++	progress.filterProgress=100;
++
+ 	//If we have bad points, let the user know.
+ 	if(!badPts.empty())
+ 	{
+@@ -4230,11 +4301,8 @@
+ 				}
+ 
+ 				//distance between search pt and found pt
+-				float sqrDistance;
+-				sqrDistance = searchTree.getPtRef(ptIdx).sqrDist(pSource[ui].getPosRef());
+-
+-				if(sqrDistance > DISTANCE_EPSILON)
+-					ptsFound.insert(ptIdx);
++
++				ptsFound.insert(ptIdx);
+ 			}
+ 
+ 
+@@ -4244,8 +4312,14 @@
+ 			//Count the number of numerator and denominator ions, using the masses we set aside earlier
+ 			for(set<size_t>::iterator it=ptsFound.begin(); it!=ptsFound.end(); ++it)
+ 			{
++
++				//check that the distance is non-zero, to force no self-matching
++				float sqrDistance;
++				sqrDistance = searchTree.getPtRef(*it).sqrDist(pSource[ui].getPosRef());
++				if(sqrDistance < DISTANCE_EPSILON)
++					continue;
++
+ 				float ionMass;
+-				//check that the distance is non-zero, to force no self-matching
+ 				ionMass = dataMasses[searchTree.getOrigIndex(*it)];
+ 
+ 				unsigned int ionID;
+@@ -4326,9 +4400,11 @@
+ bool nnHistogramTest();
+ bool rdfPlotTest();
+ bool axialDistTest();
+-bool replaceTest();
++bool replaceIntersectAndUnionTest();
+ bool localConcTestRadius();
+ bool localConcTestNN();
++bool replaceSubtractTest();
++bool replaceUnionTest();
+ 
+ bool SpatialAnalysisFilter::runUnitTests()
+ {
+@@ -4343,8 +4419,15 @@
+ 
+ 	if(!axialDistTest())
+ 		return false;
+-	if(!replaceTest())
++	if(!replaceIntersectAndUnionTest())
+ 		return false;
++
++	if(!replaceSubtractTest())
++		return false;
++
++	if(!replaceUnionTest())
++		return false;
++
+ 	if(!localConcTestRadius())
+ 		return false;
+ 
+@@ -4617,7 +4700,7 @@
+ 	return true;
+ }
+ 
+-bool replaceTest()
++bool replaceIntersectAndUnionTest()
+ {
+ 	std::string ionFile=createTmpFilename(NULL,".pos");
+ 		
+@@ -4637,6 +4720,157 @@
+ 	//Create a spatial analysis filter
+ 	SpatialAnalysisFilter *f=new SpatialAnalysisFilter;
+ 	f->setCaching(false);	
++	//Set it to do a union calculation 
++	bool needUp;
++	string s;
++	s=TRANS(SPATIAL_ALGORITHMS[ALGORITHM_REPLACE]);
++	TEST(f->setProperty(KEY_ALGORITHM,s,needUp),"Set prop");
++	TEST(f->setProperty(KEY_REPLACE_FILE,ionFile,needUp),"Set prop");
++	s="1";
++	TEST(f->setProperty(KEY_REPLACE_VALUE,s,needUp),"Set prop");
++
++	vector<unsigned int> opVec;
++	opVec.push_back(REPLACE_MODE_INTERSECT);
++	opVec.push_back(REPLACE_MODE_UNION);
++	
++	ProgressData p;
++	vector<const FilterStreamData*> streamIn,streamOut;
++	streamIn.push_back(d);
++	for(unsigned int opId=0;opId<opVec.size();opId++)
++	{
++		s=TRANS(REPLACE_ALGORITHMS[opVec[opId]]);
++		TEST(f->setProperty(KEY_REPLACE_ALGORITHM,s,needUp),"Set prop");
++
++		//Do the refresh
++		TEST(!f->refresh(streamIn,streamOut,p),"refresh OK");
++
++		TEST(streamOut.size() == 1,"stream count");
++		TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
++		TEST(streamOut[0]->getNumBasicObjects() == NIONS,"Number objects");
++
++		//we should have taken the mass-to-charge from the file
++		const IonStreamData *outIons = (const IonStreamData*)streamOut[0];
++		for(unsigned int ui=0;ui<NIONS; ui++)
++		{
++			ASSERT(outIons->data[ui].getMassToCharge() == 1); 
++		}
++		delete streamOut[0];
++		streamOut.clear();
++	}
++	delete f;
++	delete d;
++	
++	wxRemoveFile(ionFile);
++
++	
++	return true;
++}
++
++bool replaceSubtractTest()
++{
++	std::string ionFile=createTmpFilename(NULL,".pos");
++		
++	vector<IonHit> ions;
++	const unsigned int NIONS=10;
++	const unsigned int DIFF_COUNT=5;	
++	for(unsigned int ui=0;ui<NIONS;ui++)
++	{
++		IonHit h;
++		h = IonHit(Point3D(ui,ui,ui),1);
++
++		//make some ions different to the (x,x,x) pattern
++		if(ui < DIFF_COUNT)
++		{
++			h.setPos(h.getPos() - Point3D(0,0,100));
++			ions.push_back(h);
++		}
++		else
++			ions.push_back(h);
++	}
++
++	vector<IonHit> tmpI;
++	for(unsigned int ui=0;ui<NIONS;ui++)
++	{
++		if(ions[ui][2] < 0 )
++		{
++			tmpI.push_back(ions[ui]);
++			tmpI.back().setMassToCharge(2);
++		}
++	}
++
++	IonHit::makePos(tmpI,ionFile.c_str());
++	tmpI.clear();
++
++	IonStreamData *d = new IonStreamData;
++	d->data.swap(ions);
++
++	//Create a spatial analysis filter
++	SpatialAnalysisFilter *f=new SpatialAnalysisFilter;
++	f->setCaching(false);	
++	
++	//Set it to do a subtraction calculation 
++	bool needUp;
++	string s;
++	s=TRANS(SPATIAL_ALGORITHMS[ALGORITHM_REPLACE]);
++	TEST(f->setProperty(KEY_ALGORITHM,s,needUp),"Set prop");
++	TEST(f->setProperty(KEY_REPLACE_FILE,ionFile,needUp),"Set prop");
++	s=TRANS(REPLACE_ALGORITHMS[REPLACE_MODE_SUBTRACT]);
++	TEST(f->setProperty(KEY_REPLACE_ALGORITHM,s,needUp),"Set prop");
++
++	//Do the refresh
++	ProgressData p;
++	vector<const FilterStreamData*> streamIn,streamOut;
++	streamIn.push_back(d);
++	TEST(!f->refresh(streamIn,streamOut,p),"refresh OK");
++	delete f;
++	delete d;
++	streamIn.clear();
++
++	TEST(streamOut.size() == 1,"stream count");
++	TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
++	TEST(streamOut[0]->getNumBasicObjects() == DIFF_COUNT,"Number objects");
++
++	//we should have taken the mass-to-charge from the original data,
++	// not the file
++	const IonStreamData *outIons = (const IonStreamData*)streamOut[0];
++	for(unsigned int ui=0;ui<outIons->getNumBasicObjects(); ui++)
++	{
++		ASSERT(outIons->data[ui].getMassToCharge() == 1); 
++		ASSERT(outIons->data[ui].getPos()[2] >= 0); 
++	}
++
++	wxRemoveFile(ionFile);
++
++	delete streamOut[0];
++	
++	return true;
++}
++
++bool replaceUnionTest()
++{
++	std::string ionFile=createTmpFilename(NULL,".pos");
++	
++	//"B" dataset	
++	vector<IonHit> ions;
++	ions.push_back(IonHit(Point3D(0,0,0),1));
++	ions.push_back(IonHit(Point3D(1,0,1),1));
++	ions.push_back(IonHit(Point3D(0,1,1),1));
++
++	IonHit::makePos(ions,ionFile.c_str());
++	ions.clear();
++
++	//"A" dataset	
++	ions.push_back(IonHit(Point3D(0,0,0),2));
++	ions.push_back(IonHit(Point3D(1,0,-1),2));
++	ions.push_back(IonHit(Point3D(0,1,-1),2));
++
++
++	IonStreamData *d = new IonStreamData;
++	d->data.swap(ions);
++
++	//Create a spatial analysis filter
++	SpatialAnalysisFilter *f=new SpatialAnalysisFilter;
++	f->setCaching(false);	
+ 	
+ 	//Set it to do a union calculation 
+ 	bool needUp;
+@@ -4644,13 +4878,11 @@
+ 	s=TRANS(SPATIAL_ALGORITHMS[ALGORITHM_REPLACE]);
+ 	TEST(f->setProperty(KEY_ALGORITHM,s,needUp),"Set prop");
+ 	TEST(f->setProperty(KEY_REPLACE_FILE,ionFile,needUp),"Set prop");
+-	s=TRANS(REPLACE_ALGORITHMS[REPLACE_MODE_INTERSECT]);
++	s=TRANS(REPLACE_ALGORITHMS[REPLACE_MODE_UNION]);
+ 	TEST(f->setProperty(KEY_REPLACE_ALGORITHM,s,needUp),"Set prop");
+-	
+ 	s="1";
+ 	TEST(f->setProperty(KEY_REPLACE_VALUE,s,needUp),"Set prop");
+ 
+-
+ 	//Do the refresh
+ 	ProgressData p;
+ 	vector<const FilterStreamData*> streamIn,streamOut;
+@@ -4662,14 +4894,16 @@
+ 
+ 	TEST(streamOut.size() == 1,"stream count");
+ 	TEST(streamOut[0]->getStreamType() == STREAM_TYPE_IONS,"stream type");
+-	TEST(streamOut[0]->getNumBasicObjects() == NIONS,"Number objects");
+-
+-	//we should have taken the mass-to-charge from the file
++	TEST(streamOut[0]->getNumBasicObjects() == 5,"Number objects");
++
++	//There should be
+ 	const IonStreamData *outIons = (const IonStreamData*)streamOut[0];
+-	for(unsigned int ui=0;ui<NIONS; ui++)
++	float sumV=0;
++	for(unsigned int ui=0;ui<outIons->getNumBasicObjects(); ui++)
+ 	{
+-		ASSERT(outIons->data[ui].getMassToCharge() == 1); 
++		sumV+=outIons->data[ui].getMassToCharge();
+ 	}
++	TEST( EQ_TOL(sumV,7.0f),"mass-to-charge check");
+ 
+ 	wxRemoveFile(ionFile);
+ 
+@@ -4678,7 +4912,6 @@
+ 	return true;
+ }
+ 
+-
+ //--- Local concentration tests --
+ const IonStreamData *createLCIonStream()
+ {
+diff -r d478204af715 -r effd078610a7 src/backend/filters/spectrumPlot.cpp
+--- a/src/backend/filters/spectrumPlot.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/spectrumPlot.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -517,6 +517,8 @@
+ 	
+ 	getOut.push_back(d);
+ 
++	progress.filterProgress=100;
++
+ 	return 0;
+ }
+ 
+diff -r d478204af715 -r effd078610a7 src/backend/filters/transform.cpp
+--- a/src/backend/filters/transform.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/filters/transform.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -998,8 +998,10 @@
+ 					break;
+ 				}
+ 			
++			}
+ 		}
+-		}
++
++		progress.filterProgress=100;
+ 	}
+ 	else
+ 	{
+@@ -1526,6 +1528,11 @@
+ 		case KEY_CROP_MINIMUM:
+ 		{
+ 			ASSERT(scalarParams.size() ==2);
++			float tmp;
++			if(stream_cast(tmp,value) || tmp >=scalarParams[1])
++				return false;
++
++
+ 			if(!applyPropertyNow(scalarParams[0],value,needUpdate))
+ 				return false;
+ 			break;
+@@ -1533,6 +1540,9 @@
+ 		case KEY_CROP_MAXIMUM:
+ 		{
+ 			ASSERT(scalarParams.size() ==2);
++			float tmp;
++			if(stream_cast(tmp,value) || tmp <=scalarParams[0])
++				return false;
+ 			if(!applyPropertyNow(scalarParams[1],value,needUpdate))
+ 				return false;
+ 			break;
+diff -r d478204af715 -r effd078610a7 src/backend/plot.cpp
+--- a/src/backend/plot.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/plot.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -29,7 +29,7 @@
+ 				NTRANS("Moving avg.")
+ 				};
+ 
+-const char *plotTypeStrings[]= {
++const char *traceStyleStrings[]= {
+ 	NTRANS("Lines"),
+ 	NTRANS("Bars"),
+ 	NTRANS("Steps"),
+@@ -119,15 +119,15 @@
+ string plotString(unsigned int plotMode)
+ {
+ 	ASSERT(plotMode< PLOT_TYPE_ENUM_END);
+-	return TRANS(plotTypeStrings[plotMode]); 
++	return TRANS(traceStyleStrings[plotMode]); 
+ }
+ 
+ unsigned int plotID(const std::string &plotString)
+ {
+-	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(plotTypeStrings) == PLOT_TYPE_ENUM_END);
++	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(traceStyleStrings) == PLOT_TYPE_ENUM_END);
+ 	for(unsigned int ui=0;ui<PLOT_TYPE_ENUM_END; ui++)
+ 	{
+-		if(plotString==TRANS(plotTypeStrings[ui]))
++		if(plotString==TRANS(traceStyleStrings[ui]))
+ 			return ui;
+ 	}
+ 
+@@ -298,7 +298,7 @@
+ 
+ PlotWrapper::PlotWrapper()
+ {
+-	//COMPILE_ASSERT(THREEDEP_ARRAYSIZE(plotTypeStrings) == PLOT_TYPE_ENUM_END);
++	//COMPILE_ASSERT(THREEDEP_ARRAYSIZE(traceStyleStrings) == PLOT_TYPE_ENUM_END);
+ 
+ 	applyUserBounds=false;
+ 	plotChanged=true;
+@@ -684,6 +684,12 @@
+ 	return visibleMode;
+ }
+ 
++unsigned int PlotWrapper::getPlotMode(unsigned int plotId) const
++{
++	ASSERT(plotId < plottingData.size());
++	return plottingData[plotId]->getPlotMode();
++}
++
+ void PlotWrapper::getVisibleIDs(vector<unsigned int> &visiblePlotIDs ) const
+ {
+ 
+@@ -791,7 +797,7 @@
+ 				if(!plottingData[ui]->visible)
+ 					continue;
+ 
+-				if(plottingData[ui]->getType()!= PLOT_MODE_1D)
++				if(plottingData[ui]->getMode()!= PLOT_MODE_1D)
+ 					continue;
+ 			
+ 				if(((Plot1D*)plottingData[ui])->wantLogPlot()) 
+@@ -809,7 +815,7 @@
+ 				float minYVal=0.1;
+ 				for(size_t ui=0;ui<plottingData.size();ui++)
+ 				{
+-					if(!plottingData[ui]->visible || plottingData[ui]->getType() !=PLOT_MODE_1D)
++					if(!plottingData[ui]->visible || plottingData[ui]->getMode() !=PLOT_MODE_1D)
+ 						continue;
+ 
+ 					float tmp ;
+@@ -929,7 +935,7 @@
+ 				Plot2DFunc *curPlot;
+ 				curPlot=(Plot2DFunc*)plottingData[ui];
+ 
+-				if(curPlot->getType() == PLOT_2D_DENS)
++				if(curPlot->getMode() == PLOT_2D_DENS)
+ 				{
+ 					wantColourbar=true;
+ 				}
+@@ -1047,11 +1053,6 @@
+ 	plottingData[plotIDHandler.getPos(plotId)]->regionGroup.getRegion(regionId,region);
+ }
+ 
+-unsigned int PlotWrapper::plotType(unsigned int plotId) const
+-{
+-	return plottingData[plotIDHandler.getPos(plotId)]->getPlotMode();
+-}
+-
+ 
+ void PlotWrapper::moveRegion(unsigned int plotID, unsigned int regionId, bool regionSelfUpdate,
+ 		unsigned int movementType, float newX, float newY) const
+@@ -1135,7 +1136,7 @@
+ 
+ void PlotBase::copyBase(PlotBase *target) const
+ {
+-	target->plotType=plotType;
++	target->traceStyle=traceStyle;
+ 	target->minX=minX;
+ 	target->maxX=maxX;
+ 	target->minY=minY;
+@@ -1157,12 +1158,12 @@
+ 
+ unsigned int PlotBase::getType() const
+ {
+-	return plotType;
++	return traceStyle;
+ }
+ 
+ unsigned int PlotBase::getMode() const
+ {
+-	switch(plotType)
++	switch(traceStyle)
+ 	{
+ 		case PLOT_LINE_LINES:
+ 		case PLOT_LINE_BARS:
+@@ -1181,7 +1182,7 @@
+ Plot1D::Plot1D()
+ {
+ 	//Set the default plot properties
+-	plotType=PLOT_LINE_LINES;
++	traceStyle=PLOT_LINE_LINES;
+ 	plotMode=PLOT_MODE_1D;
+ 	xLabel="";
+ 	yLabel="";
+@@ -1489,7 +1490,7 @@
+ 
+ 
+ 	//Plot the appropriate form	
+-	switch(plotMode)
++	switch(traceStyle)
+ 	{
+ 		case PLOT_LINE_LINES:
+ 			//Unfortunately, when using line plots, mathgl moves the data points to the plot boundary,
+@@ -1632,7 +1633,7 @@
+ Plot2DFunc::Plot2DFunc()
+ {
+ 	plotMode = PLOT_MODE_2D;
+-	plotType=PLOT_2D_DENS;
++	traceStyle=PLOT_2D_DENS;
+ }
+ 
+ void Plot2DFunc::setData(const Array2D<float> &a,
+@@ -1705,7 +1706,8 @@
+ 
+ Plot2DScatter::Plot2DScatter()
+ {
+-	plotType=PLOT_2D_SCATTER;
++	plotMode=PLOT_2D_SCATTER;
++	traceStyle=PLOT_LINE_POINTS;
+ 	scatterIntensityLog=false;
+ }
+ 
+diff -r d478204af715 -r effd078610a7 src/backend/plot.h
+--- a/src/backend/plot.h	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/plot.h	Tue Jun 21 12:45:20 2016 +0100
+@@ -217,7 +217,7 @@
+ class PlotBase
+ {
+ 	protected:
+-		//!Sub type of plot (eg lines, bars for 1D)
++		//!Type of plot 
+ 		unsigned int plotMode;
+ 		//!xaxis label
+ 		std::string xLabel;
+@@ -229,8 +229,9 @@
+ 		//plot colour (for single coloured plots)
+ 		float r,g,b;
+ 		
+-		//The type of plot (ie what class is it?)	
+-		unsigned int plotType;
++		//The sub-style of the plot trace (eg lines, points, bars, etc)
++		// FIXME: This is badly named, change to traceStyle, or dataStyle, or something
++		unsigned int traceStyle;
+ 		
+ 		void copyBase(PlotBase *target) const;
+ 
+@@ -297,8 +298,12 @@
+ 		void setStrings(const std::string &x, 
+ 			const std::string &y,const std::string &t);
+ 
++		//Set the colour of the plot trace
+ 		void setColour(float rNew, float gNew, float bNew);
+ 
++		//set the visual style for the trace (dots, lines, etc)
++		void setTraceStyle(unsigned int newStyle) { traceStyle=newStyle;}
++
+ 		std::string getXLabel() const { return xLabel;}
+ 		std::string getTitle() const { return title;}
+ 		std::string getYLabel() const { return yLabel;}
+@@ -309,6 +314,7 @@
+ 		void setPlotMode(unsigned int newMode) { plotMode= newMode;}
+ 
+ 
++		//get the  colour of the trace
+ 		void getColour(float &r, float &g, float &b) const ;
+ 
+ #ifdef DEBUG
+@@ -613,7 +619,7 @@
+ 	
+ 
+ 		//!obtain the type of a plot, given the plot's uniqueID
+-		unsigned int plotType(unsigned int plotId) const;
++		unsigned int getPlotMode(unsigned int plotId) const;
+ 
+ 		//Retrieve the types of visible plots
+ 		unsigned int getVisibleMode() const;
+diff -r d478204af715 -r effd078610a7 src/backend/viscontrol.cpp
+--- a/src/backend/viscontrol.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/backend/viscontrol.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -249,7 +249,7 @@
+ 						plotData->yLabel,plotData->dataLabel);
+ 					
+ 					//set the appearance of the plot
+-					//plotNew->setTraceStyle(plotStyle);
++					plotNew->setTraceStyle(plotData->plotStyle);
+ 					plotNew->setColour(plotData->r,plotData->g,plotData->b);
+ 					
+ 					
+@@ -275,7 +275,7 @@
+ 					unsigned int plotID;
+ 		
+ 					PlotBase *plotNew;
+-					switch(plotData->plotType) 
++					switch(plotData->plotStyle) 
+ 					{
+ 						case PLOT_2D_DENS:
+ 						{
+diff -r d478204af715 -r effd078610a7 src/common/basics.cpp
+--- a/src/common/basics.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/common/basics.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -1347,7 +1347,8 @@
+ 	while(CFile.good() && !CFile.eof() && atHeader)
+ 	{
+ 		//Grab a line from the file
+-		CFile.getline(inBuffer,BUFFER_SIZE);
++		if(!CFile.getline(inBuffer,BUFFER_SIZE))
++			break;
+ 
+ 		if(!CFile.good())
+ 			return ERR_FILE_FORMAT;
+@@ -1457,10 +1458,8 @@
+ 			
+ 		}
+ 		//Grab a line from the file
+-		CFile.getline(inBuffer,BUFFER_SIZE);
+-		
+-		if(!CFile.good() && !CFile.eof())
+-			return ERR_FILE_FORMAT;
++		if(!CFile.getline(inBuffer,BUFFER_SIZE))
++			break;
+ 	}
+ 
+ 	return 0;
+diff -r d478204af715 -r effd078610a7 src/gui/mainFrame.cpp
+--- a/src/gui/mainFrame.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/gui/mainFrame.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -1391,7 +1391,7 @@
+ 	updateWxTreeCtrl(treeFilters);
+ 
+ 	if(!noUpdate)
+-		return doSceneUpdate(true);
++		doSceneUpdate(true);
+ 
+ 	return true;
+ }	
+@@ -3834,7 +3834,7 @@
+ 	
+ }
+ 
+-bool MainWindowFrame::doSceneUpdate(bool ensureVisible)
++void MainWindowFrame::doSceneUpdate(bool ensureVisible)
+ {
+ 	//Update scene
+ 	ASSERT(!currentlyUpdatingScene);
+@@ -3864,6 +3864,11 @@
+ 	ensureResultVisible=ensureVisible;
+ 
+ 	ASSERT(!refreshControl);
++
++	//Hack to prevent crash on double-refresh
++	if(refreshControl)
++		return;
++
+ 	refreshControl = new RefreshController(visControl.state.treeState);
+ 	refreshThread=new RefreshThread(this,refreshControl);
+ 	progressTimer->Start(PROGRESS_TIMER_DELAY);
+@@ -3871,7 +3876,8 @@
+ 	refreshThread->Create();
+ 	refreshThread->Run();
+ 
+-	return true;
++	cerr << "Updating scene complete"<< endl;
++	return;
+ }
+ 
+ void MainWindowFrame::updateWxTreeCtrl( wxTreeCtrl *t, const Filter *f)
+@@ -3971,6 +3977,11 @@
+ 	ASSERT(!visControl.state.treeState.isRefreshing());
+ 	progressTimer->Stop();
+ 
++	//Hack to prevent crash on re-entry during refresh. Should never trigger.
++	if(!refreshControl)
++		return;
++
++
+ 	vector<std::pair<const Filter*, std::string> > consoleMessages;
+ 	consoleMessages=refreshControl->getConsoleMessages();
+ 
+diff -r d478204af715 -r effd078610a7 src/gui/mainFrame.h
+--- a/src/gui/mainFrame.h	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/gui/mainFrame.h	Tue Jun 21 12:45:20 2016 +0100
+@@ -124,7 +124,7 @@
+ 	//!Update the progress information in the status bar
+ 	void updateProgressStatus();
+ 	//!Perform an update to the 3D Scene. Returns false if refresh failed
+-	bool doSceneUpdate(bool ensureResultVisible=false);
++	void doSceneUpdate(bool ensureResultVisible=false);
+ 	
+ 	//!Complete the scene update. Returns false if failed
+ 	void finishSceneUpdate(unsigned int errCode);
+diff -r d478204af715 -r effd078610a7 src/gui/mathglPane.cpp
+--- a/src/gui/mathglPane.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/gui/mathglPane.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -627,7 +627,7 @@
+ 			thePlot->getRegion(plotId,regionId,r);
+ 
+ 			//TODO: Implement a more generic region handler?
+-			ASSERT(thePlot->plotType(plotId) == PLOT_MODE_1D);
++			ASSERT(thePlot->getPlotMode(plotId) == PLOT_MODE_1D);
+ 
+ 			float mglStartX,mglStartY;
+ 			toPlotCoords(draggingStart.x, draggingStart.y,mglStartX,mglStartY);
+@@ -1471,7 +1471,7 @@
+ 		return;
+ 
+ 
+-	ASSERT(thePlot->plotType(startMousePlot) == PLOT_MODE_1D);
++	ASSERT(thePlot->getPlotMode(startMousePlot) == PLOT_MODE_1D);
+ 
+ 	//See where extending the region is allowed up to.
+ 	thePlot->findRegionLimit(startMousePlot,startMouseRegion,
+@@ -1550,7 +1550,7 @@
+ 		{
+ 			//This needs to be extended to support more
+ 			//plot types.
+-			ASSERT(thePlot->plotType(startMousePlot) == PLOT_MODE_1D);
++			ASSERT(thePlot->getPlotMode(startMousePlot) == PLOT_MODE_1D);
+ 			
+ 			//Draw "ghost" limits markers for move,
+ 			//these appear as moving vertical bars to outline
+diff -r d478204af715 -r effd078610a7 src/wx/wxcomponents.cpp
+--- a/src/wx/wxcomponents.cpp	Mon Jun 27 11:07:17 2016 +0100
++++ b/src/wx/wxcomponents.cpp	Tue Jun 21 12:45:20 2016 +0100
+@@ -188,14 +188,14 @@
+ 
+ void CopyGrid::saveData()
+ {
+-	wxFileDialog *wxF = new wxFileDialog(this,TRANS("Save Data..."), wxT(""),
++	wxFileDialog wxF(this,TRANS("Save Data..."), wxT(""),
+ 		wxT(""),TRANS("Text File (*.txt)|*.txt|All Files (*)|*"),wxFD_SAVE);
+ 
+-	if( (wxF->ShowModal() == wxID_CANCEL))
++	if( (wxF.ShowModal() == wxID_CANCEL))
+ 		return;
+ 	
+ 
+-	std::string dataFile = stlStr(wxF->GetPath());
++	std::string dataFile = stlStr(wxF.GetPath());
+ 	ofstream f(dataFile.c_str());
+ 
+ 	if(!f)
diff --git a/debian/patches/lowercase-textdomain.patch b/debian/patches/lowercase-textdomain.patch
index 2c24b61..10fdaf0 100644
--- a/debian/patches/lowercase-textdomain.patch
+++ b/debian/patches/lowercase-textdomain.patch
@@ -1,11 +1,11 @@
 Description: Debian uses different text domain for the lang files
 Forwarded: not-needed
 Author: D Haley <mycae - gmx - com>
-Index: 3depict-0.0.18/src/3Depict.cpp
+Index: 3depict-0.0.19/src/3Depict.cpp
 ===================================================================
---- 3depict-0.0.18.orig/src/3Depict.cpp	2015-05-02 00:01:30.047635141 +0200
-+++ 3depict-0.0.18/src/3Depict.cpp	2015-05-02 00:01:30.043635141 +0200
-@@ -157,7 +157,7 @@
+--- 3depict-0.0.19.orig/src/3Depict.cpp	2016-08-04 00:12:38.260085712 +0200
++++ 3depict-0.0.19/src/3Depict.cpp	2016-08-04 00:12:38.256085765 +0200
+@@ -179,7 +179,7 @@
  		else
  		{
  			//Set the gettext language
@@ -14,7 +14,7 @@ Index: 3depict-0.0.18/src/3Depict.cpp
  			setlocale (LC_ALL, "");
  #ifdef __WXMAC__
  			bindtextdomain( PROGRAM_NAME, paths->GetResourcesDir().mb_str(wxConvUTF8) );
-@@ -189,8 +189,8 @@
+@@ -211,8 +211,8 @@
  					break;
  			}			
  #else
diff --git a/debian/patches/series b/debian/patches/series
index f1e4846..e898cbe 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,4 +1,4 @@
+diff_0.0.19_to_effd078610a7
 debian-desktop-naming.patch
 lowercase-textdomain.patch
 desktop-category.patch
-diff-upstream-0.0.18_to_212d6e1e6b14

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/3depict.git



More information about the debian-science-commits mailing list