[3depict] 01/04: * Fix git-import-orig failing to import (bug 778594)

D Haley mycae-guest at moszumanska.debian.org
Sun May 3 13:53:38 UTC 2015


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

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

commit 24439cafa5039d5c9aeaac15777d16b0e0a45b96
Author: D Haley <mycae at gmx.com>
Date:   Fri May 1 23:55:48 2015 +0200

    * Fix git-import-orig failing to import (bug 778594)
---
 ChangeLog                                          |   21 +
 Makefile.am                                        |    0
 Makefile.in                                        |    2 +
 TODO                                               |   23 +-
 acinclude.m4                                       |    0
 aclocal.m4                                         |   10 +-
 config.h.in                                        |   68 +
 config.rpath                                       |    0
 config.sub                                         |    0
 configure                                          |  445 ++-
 configure.ac                                       |   46 +-
 docs/manual-latex/manual.tex                       |   21 +-
 .../python-example.py                              |    0
 docs/samples/externalProg/bash-example.sh          |    0
 docs/samples/externalProg/python-example.py        |    0
 docs/web/about.html                                |   89 -
 docs/web/compiling-OSX.html                        |   74 -
 docs/web/compiling-cross.html                      |   56 -
 docs/web/compiling-linux.html                      |  153 -
 docs/web/compiling-macosx.html                     |   78 -
 docs/web/compiling.html                            |   91 -
 docs/web/contact.html                              |   55 -
 docs/web/documentation.html                        |  103 -
 docs/web/download.html                             |  113 -
 docs/web/home.html                                 |  159 -
 docs/web/images/3Depict-icon.png                   |  Bin 9132 -> 0 bytes
 docs/web/images/Screenshot-thumb.png               |  Bin 39680 -> 0 bytes
 docs/web/images/Screenshot.png                     |  Bin 303621 -> 0 bytes
 docs/web/images/cu-ppt-cluster-analysis.png        |  Bin 116809 -> 0 bytes
 docs/web/images/exportanimParamDialog.png          |  Bin 89361 -> 0 bytes
 docs/web/images/laser-data1.png                    |  Bin 322814 -> 0 bytes
 docs/web/images/laser-data2.png                    |  Bin 193987 -> 0 bytes
 docs/web/images/resolution-example.png             |  Bin 201393 -> 0 bytes
 docs/web/images/voxel-representations.png          |  Bin 1034586 -> 0 bytes
 docs/web/index.html                                |  163 -
 docs/web/manual.html                               |  304 --
 docs/web/news.html                                 |   98 -
 docs/web/questions.html                            |  174 --
 docs/web/rss.xml                                   |  144 -
 docs/web/screenshots.html                          |   60 -
 docs/web/style.css                                 |   60 -
 docs/web/videos.html                               |   70 -
 locales/de_DE/LC_MESSAGES/3Depict.mo               |  Bin 45179 -> 44825 bytes
 m4/freetype2.m4                                    |    0
 m4/ftgl.m4                                         |    0
 m4/gl.m4                                           |    0
 m4/gsl.m4                                          |    0
 m4/libxml2.m4                                      |    0
 m4/opengl.m4                                       |    0
 m4/wxwin.m4                                        |    0
 missing                                            |    0
 packaging/3Depict.desktop                          |    0
 packaging/RPM/3Depict.spec                         |   13 +-
 packaging/debian/3depict.1                         |    0
 packaging/debian/changelog                         |   18 +-
 packaging/debian/compat                            |    0
 packaging/debian/docs                              |    0
 packaging/debian/manpages                          |    0
 packaging/debian/menu                              |    0
 packaging/debian/source/format                     |    0
 packaging/debian/watch                             |    0
 packaging/deps/getDeps                             |   22 +-
 packaging/deps/sources/mgl_data_png.patch          |    0
 packaging/deps/sources/mgl_eps.patch               |    0
 packaging/deps/sources/mgl_export.patch            |    0
 packaging/howToRelease.txt                         |   47 +
 packaging/mac/2compile.sh                          |    0
 packaging/makeTarball.sh                           |   89 +-
 packaging/manpage/3Depict.1                        |    0
 packaging/mingw-debian-cross/bootstrap.sh          |  118 +-
 .../patches/gettext-fix-configure-versions         |   36 +
 packaging/mingw-debian-cross/patches/glew-makefile |    4 +-
 .../patches/mathgl-openmp-linker-flag              |   13 +
 .../patches/wx_changeset_76890.diff                |   46 +
 packaging/mingw-debian-cross/windows-installer.nsi |    2 +-
 src/3Depict.cpp                                    |   61 +-
 src/Makefile.am                                    |   13 +-
 src/Makefile.in                                    |   90 +-
 src/backend/APT/APTFileIO.cpp                      |   68 +-
 src/backend/APT/APTFileIO.h                        |   10 +-
 src/backend/APT/APTRanges.cpp                      |   96 +-
 src/backend/APT/APTRanges.h                        |   15 +-
 src/backend/APT/abundanceParser.cpp                |    2 +-
 src/backend/APT/abundanceParser.h                  |    2 +-
 src/backend/APT/ionhit.cpp                         |    2 +-
 src/backend/APT/ionhit.h                           |    2 +-
 src/backend/animator.cpp                           |   14 +-
 src/backend/animator.h                             |   16 +-
 src/backend/configFile.cpp                         |    3 +-
 src/backend/configFile.h                           |   11 +-
 src/backend/filter.cpp                             |  118 +-
 src/backend/filter.h                               |  112 +-
 src/backend/filters/algorithms/K3DTree-mk2.cpp     |   41 +-
 src/backend/filters/algorithms/K3DTree-mk2.h       |   27 +-
 src/backend/filters/algorithms/K3DTree.cpp         |   35 +-
 src/backend/filters/algorithms/K3DTree.h           |   18 +-
 src/backend/filters/algorithms/binomial.cpp        |   18 +-
 src/backend/filters/algorithms/binomial.h          |    2 +-
 src/backend/filters/algorithms/mass.cpp            |  257 ++
 src/backend/filters/algorithms/mass.h              |  200 ++
 src/backend/filters/algorithms/rdf.cpp             |   83 +-
 src/backend/filters/algorithms/rdf.h               |   19 +-
 src/backend/filters/allFilter.cpp                  |    2 +-
 src/backend/filters/allFilter.h                    |    6 +-
 src/backend/filters/annotation.cpp                 |   31 +-
 src/backend/filters/annotation.h                   |    6 +-
 src/backend/filters/boundingBox.cpp                |   22 +-
 src/backend/filters/boundingBox.h                  |    6 +-
 src/backend/filters/clusterAnalysis.cpp            |  968 +++---
 src/backend/filters/clusterAnalysis.h              |   57 +-
 src/backend/filters/compositionProfile.cpp         |  177 +-
 src/backend/filters/compositionProfile.h           |   15 +-
 src/backend/filters/dataLoad.cpp                   |   48 +-
 src/backend/filters/dataLoad.h                     |    8 +-
 src/backend/filters/externalProgram.cpp            |   76 +-
 src/backend/filters/externalProgram.h              |    8 +-
 src/backend/filters/filterCommon.cpp               |  101 +-
 src/backend/filters/filterCommon.h                 |   12 +-
 src/backend/filters/geometryHelpers.cpp            |  147 +-
 src/backend/filters/geometryHelpers.h              |   32 +-
 src/backend/filters/ionClip.cpp                    |   51 +-
 src/backend/filters/ionClip.h                      |   10 +-
 src/backend/filters/ionColour.cpp                  |   16 +-
 src/backend/filters/ionColour.h                    |    6 +-
 src/backend/filters/ionDownsample.cpp              |   77 +-
 src/backend/filters/ionDownsample.h                |    6 +-
 src/backend/filters/ionInfo.cpp                    |  286 +-
 src/backend/filters/ionInfo.h                      |   25 +-
 src/backend/filters/rangeFile.cpp                  |  118 +-
 src/backend/filters/rangeFile.h                    |   14 +-
 src/backend/filters/spatialAnalysis.cpp            |  495 ++-
 src/backend/filters/spatialAnalysis.h              |   65 +-
 src/backend/filters/spectrumPlot.cpp               |  511 +++-
 src/backend/filters/spectrumPlot.h                 |   32 +-
 src/backend/filters/transform.cpp                  |  153 +-
 src/backend/filters/transform.h                    |    6 +-
 src/backend/filters/voxelise.cpp                   |  206 +-
 src/backend/filters/voxelise.h                     |   15 +-
 src/backend/filtertree.cpp                         |  154 +-
 src/backend/filtertree.h                           |   47 +-
 src/backend/filtertreeAnalyse.cpp                  |   64 +-
 src/backend/filtertreeAnalyse.h                    |    8 +-
 src/backend/plot.cpp                               |  171 +-
 src/backend/plot.h                                 |   85 +-
 src/backend/state.cpp                              |  581 +++-
 src/backend/state.h                                |  299 +-
 src/backend/viscontrol.cpp                         | 1157 +------
 src/backend/viscontrol.h                           |  469 +--
 src/common/assertion.cpp                           |   37 +-
 src/common/assertion.h                             |   25 +-
 src/common/basics.cpp                              |   54 +-
 src/common/basics.h                                |  103 +-
 src/common/colourmap.h                             |    2 +-
 src/common/constants.cpp                           |    4 +-
 src/common/constants.h                             |   10 +-
 src/common/endianTest.h                            |    2 +-
 src/{testing/testing.h => common/gsl_helper.cpp}   |   46 +-
 src/{testing/mglTesting.h => common/gsl_helper.h}  |   15 +-
 src/common/mathfuncs.cpp                           |   45 +-
 src/common/mathfuncs.h                             |   24 +-
 src/common/mesh.cpp                                | 3236 ++++++++++++++++++++
 src/common/mesh.h                                  |  255 ++
 src/common/stringFuncs.cpp                         |    2 +-
 src/common/stringFuncs.h                           |    2 +-
 src/common/translation.h                           |    2 +-
 src/common/voxels.cpp                              |  152 +-
 src/common/voxels.h                                | 2238 +++-----------
 src/common/xmlHelper.cpp                           |    2 +-
 src/common/xmlHelper.h                             |    2 +-
 src/gl/cameras.cpp                                 |    4 +-
 src/gl/cameras.h                                   |    2 +-
 src/gl/drawables.cpp                               |  333 +-
 src/gl/drawables.h                                 |  110 +-
 src/gl/effect.cpp                                  |    2 +-
 src/gl/effect.h                                    |    2 +-
 src/gl/glDebug.h                                   |    5 +-
 src/gl/isoSurface.cpp                              |   95 +-
 src/gl/isoSurface.h                                |    5 +-
 src/gl/scene.cpp                                   |  112 +-
 src/gl/scene.h                                     |   55 +-
 src/gl/select.cpp                                  |    2 +-
 src/gl/select.h                                    |    2 +-
 src/gl/textures.cpp                                |    4 +-
 src/gl/textures.h                                  |    2 +-
 src/gui/art.h                                      |    2 +-
 src/gui/cropPanel.cpp                              |    2 +-
 src/gui/cropPanel.h                                |    2 +-
 src/gui/dialogs/ExportPos.cpp                      |   27 +-
 src/gui/dialogs/ExportPos.h                        |    2 +-
 src/gui/dialogs/ExportRngDialog.cpp                |    2 +-
 src/gui/dialogs/ExportRngDialog.h                  |    2 +-
 src/gui/dialogs/StashDialog.cpp                    |   33 +-
 src/gui/dialogs/StashDialog.h                      |    2 +-
 src/gui/dialogs/animateFilterDialog.cpp            |   10 +-
 src/gui/dialogs/animateFilterDialog.h              |   16 +-
 .../animateSubDialogs/choiceKeyFrameDialog.cpp     |    2 +-
 .../animateSubDialogs/choiceKeyFrameDialog.h       |    2 +-
 .../animateSubDialogs/colourKeyFrameDialog.cpp     |    2 +-
 .../animateSubDialogs/colourKeyFrameDialog.h       |    2 +-
 .../dialogs/animateSubDialogs/realKeyFrameDialog.h |    2 +-
 .../animateSubDialogs/stringKeyFrameDialog.cpp     |    2 +-
 .../animateSubDialogs/stringKeyFrameDialog.h       |    2 +-
 src/gui/dialogs/autosaveDialog.cpp                 |    2 +-
 src/gui/dialogs/autosaveDialog.h                   |    2 +-
 src/gui/dialogs/filterErrorDialog.cpp              |    2 +-
 src/gui/dialogs/filterErrorDialog.h                |    2 +-
 src/gui/dialogs/prefDialog.cpp                     |    2 +-
 src/gui/dialogs/prefDialog.h                       |    2 +-
 src/gui/dialogs/rangeEditDialog.cpp                |   15 +-
 src/gui/dialogs/rangeEditDialog.h                  |    2 +-
 src/gui/dialogs/resolutionDialog.cpp               |    2 +-
 src/gui/dialogs/resolutionDialog.h                 |    2 +-
 src/gui/glPane.cpp                                 |  170 +-
 src/gui/glPane.h                                   |   11 +-
 src/gui/mainFrame.cpp                              |  984 +++---
 src/gui/mainFrame.h                                |   51 +-
 src/gui/mathglPane.cpp                             |  107 +-
 src/gui/mathglPane.h                               |   12 +-
 src/myAppIcon.ico                                  |  Bin 4286 -> 39272 bytes
 src/testing/filtertesting.cpp                      |   19 +-
 src/testing/mglTesting.cpp                         |   16 +-
 src/testing/mglTesting.h                           |    2 +-
 src/testing/testing.cpp                            |   65 +-
 src/testing/testing.h                              |    2 +-
 src/wx/propertyGridUpdater.cpp                     |   23 +-
 src/wx/propertyGridUpdater.h                       |    4 +-
 src/wx/wxcommon.cpp                                |   15 +-
 src/wx/wxcommon.h                                  |    5 +-
 src/wx/wxcomponents.cpp                            |   19 +-
 src/wx/wxcomponents.h                              |    4 +-
 translations/3Depict_base.pot                      | 2549 +++++++--------
 translations/3Depict_de_DE.mo                      |  Bin 45179 -> 44825 bytes
 translations/3Depict_de_DE.po                      | 2586 ++++++++--------
 translations/makeTranslations                      |    2 +
 234 files changed, 14231 insertions(+), 10827 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6f343ea..cf84aaa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+* 05 Apr 2014 : 0.0.18
+	Features:
+	- User interface now uses its own CPU. Should be much more
+	  responsive during refreshes
+	- Spectra can be normalised to allow for easier inter-spectra
+	comparison. Several normalisation modes have been added.
+	- Clustering ellipse fitting implemented. Can now determine
+	 best-fit ellipsoids on clusters, and plot them in 2D to identify
+	 lath/rod/sphere/disc shapes
+	- Voxelisation filter now supports fast blurring 
+	
+	User Visible Changes:
+	- Axial slice in voxelisation can now be made translucent
+	- More noticable progress feedback, now as a progress "spinner"
+
+	Technical bugfixes/changes:
+	- Significant refactoring of internals to allow for threading. 
+		- State has been made more prominent, viscontrol less.
+		- New "TreeState" object created
+	- New library added for voxelisation/image support : libvigra
+
 * 01 Sep 2014 : 0.0.17
 	Features
 	- Upgrade to wxWidgets 3 (wx3)
diff --git a/Makefile.am b/Makefile.am
old mode 100755
new mode 100644
diff --git a/Makefile.in b/Makefile.in
index d645610..bfc23fd 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -238,6 +238,7 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LDFLAGS = @LDFLAGS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
+LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MGL_CFLAGS = @MGL_CFLAGS@
@@ -260,6 +261,7 @@ PNG_CFLAGS = @PNG_CFLAGS@
 PNG_LIBS = @PNG_LIBS@
 QHULL_CFLAGS = @QHULL_CFLAGS@
 QHULL_LIBS = @QHULL_LIBS@
+RANLIB = @RANLIB@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
diff --git a/TODO b/TODO
index c648e05..cd3b456 100644
--- a/TODO
+++ b/TODO
@@ -1,14 +1,11 @@
 TODO List - worlds simplest bugtracking system 
 
 --Packaging--
-	* Update manual with
-		- cluster ID 
-		- Set operations
 
 --Main app--
 	To Implement:
 		== Next version ==
-		* Alpha blending on plane slice?
+		* Voxelisation filter should not show ions disabled in upstream range
 	
 		== Eventually == 
 		* Voxel export dialog 
@@ -28,10 +25,8 @@ TODO List - worlds simplest bugtracking system
 		* Better progress during cluster ranging
 		* Billboard text
 		* Camera animation control (slerp?)
-		* CSR fit to NN Hist in spatial analysis 
 		* Bounding box tick values could be improved - scientific
 		  notation, better font size control, absol. coords, etc
-		* Voxelisation filter should not show ions disabled in upstream range
 		* Range file drag/drop ignores drop coordinates; this could be useful
 		  in trying to interpret where the user wants to drop the object
 		* Plots cannot currently be updated on name change. Need to add "needUpdate" to setUserString
@@ -40,12 +35,10 @@ TODO List - worlds simplest bugtracking system
 		== Next release==
 	
 		== Eventually==
-		* Fix voxelisation "filter" option 
 		* Cluster filter wont save state correctly if parent
 		  rangefile has disabled ions. It will output incorrect
 		  number of enabled ions, and get wiped during next ::initFilter
 		* Lighting calculations on isosurfaces can be problematic. See example package
-		* Loading a full pos file directly onto the video card cannot be aborted.
 		* Select an X-Y crop, camera coords, on a dataset,
 		  then view   target  (-9.14237,-0.995174,83.2904) origin
 		  (188.23,-0.995174,83.2904). Now press ctrl+space. Note
@@ -69,24 +62,30 @@ TODO List - worlds simplest bugtracking system
 		* Pos limit loader could alter its behaviour when
 		 sampling rates some percentage to be determined (load
 		 file, skip buffer).
-		* Clustering could be paralellised, but is complex. Split & weld along KD tree lines using 
-		  cluster BB interactions.
+		* Clustering can now be paralellised. 
+			- consider implementing/using sphere grab algorithm, which is much faster
 		* Examine performance characteristics of HULL_GRAB in ioninfo
 		* Conversion of double axis-angle rotations to single axis angle rotations - any value here?
-		* Ranging does two full passes, one to estimate output size, and one to actually range
-			- why not simply sample the input to estimate output size??
+			- Use TRIAD/ Wahba's problem to solve. Should be a lot faster?
 	
 	Misc:
 
 
 -- Refactor/cleanup --
+	* read/write xml could have default reader/writer routines
+	Variables could be registered, then an order independant parser
+	could be used to "vacuum" most of the data. Bounds/corretness
+	checking could be used thereafter, and fallback to traditional
+	xml tree traversal could be done after that
 	* Plotting code is a nightmare. Data model for plot.h is not
 	   very well thought out, leading to large duplication, and elaborate special-case-ing
 		- Plots need log/non-log axes
 		- Plots need to be connected/disconnected
 		- Plots need to be able to specify bounding boxes separate from their data
+			- This would aid things like bar plots, pie charts
 		- Plots need to be able to set strings/legends
 		- There is little diference between plot1D and plot2D, really.
+		- Error bar handling is a bit of a hack.  Error generation should be moved to filters
 	* work out which inline FIXMEs and TODOs are still valid, and need attention
 	* K3DTree currently requires public access to members of boundcube
 	* Enums should be, where possible, moved into their relevant class' namespace
diff --git a/acinclude.m4 b/acinclude.m4
old mode 100755
new mode 100644
diff --git a/aclocal.m4 b/aclocal.m4
index 170ae3e..927c749 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -318,10 +318,9 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 # configured tree to be moved without reconfiguration.
 
 AC_DEFUN([AM_AUX_DIR_EXPAND],
-[dnl Rely on autoconf to set up CDPATH properly.
-AC_PREREQ([2.50])dnl
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
 ])
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
@@ -788,7 +787,8 @@ to "yes", and re-run configure.
 END
     AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
   fi
-fi])
+fi
+])
 
 dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
 dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
diff --git a/config.h.in b/config.h.in
index affad5d..6166767 100644
--- a/config.h.in
+++ b/config.h.in
@@ -6,30 +6,71 @@
 /* Define to 1 if you have the `atexit' function. */
 #undef HAVE_ATEXIT
 
+/* Define to 1 if you have the <fenv.h> header file. */
+#undef HAVE_FENV_H
+
+/* Define to 1 if you have the `floor' function. */
+#undef HAVE_FLOOR
+
 /* Define to 1 if you have the <ft2build.h> header file. */
 #undef HAVE_FT2BUILD_H
 
+/* Define to 1 if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define to 1 if you have the `isascii' function. */
+#undef HAVE_ISASCII
+
 /* Define if you have the FREETYPE2 library */
 #undef HAVE_LIBFREETYPE
 
 /* Define to 1 if you have the `ftgl' library (-lftgl). */
 #undef HAVE_LIBFTGL
 
+/* Define to 1 if you have the <libintl.h> header file. */
+#undef HAVE_LIBINTL_H
+
 /* Define if you have the GNOME XML library */
 #undef HAVE_LIBXML
 
+/* Define to 1 if you have the <mach/mach.h> header file. */
+#undef HAVE_MACH_MACH_H
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#undef HAVE_MALLOC
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
 /* PNG compilation OK */
 #undef HAVE_PNG
 
+/* Define to 1 if you have the `pow' function. */
+#undef HAVE_POW
+
+/* Define to 1 if the system has the type `ptrdiff_t'. */
+#undef HAVE_PTRDIFF_T
+
 /* qhull compilation OK */
 #undef HAVE_QHULL
 
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+   and to 0 otherwise. */
+#undef HAVE_REALLOC
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
 /* Define to 1 if you have the `sqrt' function. */
 #undef HAVE_SQRT
 
@@ -42,15 +83,24 @@
 /* Define to 1 if you have the <stdlib.h> header file. */
 #undef HAVE_STDLIB_H
 
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
 /* Define to 1 if you have the <strings.h> header file. */
 #undef HAVE_STRINGS_H
 
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if you have the `sysinfo' function. */
+#undef HAVE_SYSINFO
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
 /* Define to 1 if you have the <sys/types.h> header file. */
 #undef HAVE_SYS_TYPES_H
 
@@ -93,6 +143,11 @@
 /* Version number of package */
 #undef VERSION
 
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT64_T
+
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
@@ -101,3 +156,16 @@
 #ifndef __cplusplus
 #undef inline
 #endif
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint64_t
diff --git a/config.rpath b/config.rpath
old mode 100755
new mode 100644
diff --git a/config.sub b/config.sub
old mode 100644
new mode 100755
diff --git a/configure b/configure
index 045e12b..5c027db 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for 3Depict 0.0.17.
+# Generated by GNU Autoconf 2.69 for 3Depict 0.0.18.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -577,8 +577,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='3Depict'
 PACKAGE_TARNAME='3depict'
-PACKAGE_VERSION='0.0.17'
-PACKAGE_STRING='3Depict 0.0.17'
+PACKAGE_VERSION='0.0.18'
+PACKAGE_STRING='3Depict 0.0.18'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -630,6 +630,8 @@ GSL_CFLAGS
 GSL_CONFIG
 MGL_LIBS
 MGL_CFLAGS
+RANLIB
+LN_S
 GL_LIBS
 PNG_LIBS
 PNG_CFLAGS
@@ -790,6 +792,7 @@ with_intl_libs
 enable_openmp_parallel
 enable_debug_checks
 enable_ubsan
+enable_experimental_cpp11
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1351,7 +1354,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures 3Depict 0.0.17 to adapt to many kinds of systems.
+\`configure' configures 3Depict 0.0.18 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1421,7 +1424,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of 3Depict 0.0.17:";;
+     short | recursive ) echo "Configuration of 3Depict 0.0.18:";;
    esac
   cat <<\_ACEOF
 
@@ -1439,6 +1442,7 @@ Optional Features:
   --enable-openmp-parallel  Enable OpenMP multi-CPU usage; requires GCC > 4.2 for parallel STL support
   --disable-debug-checks Disable any debug checking, provides faster operation, but less information needed to debug internal problems, or to provide problem reports to developers
  --disable-ubsan Disable undefined behaviour sanitizer. Only takes effect on certain ubsan supporting compilers. Useful for working around ubsan aborts that you cant fix (eg 3rd party libs
+  --enable-experimental-cpp11 Enable experimental C++11 support. Requires a full C++11 compiler (eg gcc/clang).
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1552,7 +1556,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-3Depict configure 0.0.17
+3Depict configure 0.0.18
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2246,6 +2250,60 @@ $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_type
 
+# ac_fn_c_find_uintX_t LINENO BITS VAR
+# ------------------------------------
+# Finds an unsigned integer type with width BITS, setting cache variable VAR
+# accordingly.
+ac_fn_c_find_uintX_t ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5
+$as_echo_n "checking for uint$2_t... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+     # Order is important - never check a type that is potentially smaller
+     # than half of the expected target width.
+     for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \
+	 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  case $ac_type in #(
+  uint$2_t) :
+    eval "$3=yes" ;; #(
+  *) :
+    eval "$3=\$ac_type" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       if eval test \"x\$"$3"\" = x"no"; then :
+
+else
+  break
+fi
+     done
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_find_uintX_t
+
 # ac_fn_c_check_func LINENO FUNC VAR
 # ----------------------------------
 # Tests whether FUNC exists, setting the cache variable VAR accordingly
@@ -2316,7 +2374,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by 3Depict $as_me 0.0.17, which was
+It was created by 3Depict $as_me 0.0.18, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2865,8 +2923,8 @@ test "$program_suffix" != NONE &&
 ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
 program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
 
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 if test x"${MISSING+set}" != xset; then
   case $am_aux_dir in
@@ -3179,7 +3237,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='3depict'
- VERSION='0.0.17'
+ VERSION='0.0.18'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -3271,6 +3329,7 @@ END
     as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
   fi
 fi
+
 ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -6693,10 +6752,114 @@ case "${host_os}" in
 		#Add GLEW dependency for opengl > 1.1
 		GL_LIBS="$GL_LIBS -lglew32"
 
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+		if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
 	    ;;
 	darwin*)
 		#This is handled by the --with-apple-opengl-framework
 		#option already. Nothing to do here
+
 	    ;;
 	 *)
 		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gluSphere in -lGLU" >&5
@@ -7135,9 +7298,9 @@ fi
 #Should we enable or disable debug checking?
 # Check whether --enable-debug-checks was given.
 if test "${enable_debug_checks+set}" = set; then :
-  enableval=$enable_debug_checks; enable_no_debug_checks="yes"
+  enableval=$enable_debug_checks; enable_debug_checks="no"
 else
-  enable_no_debug_checks="no"
+  enable_debug_checks="yes"
 fi
 
 
@@ -7149,6 +7312,11 @@ else
 fi
 
 
+# Check whether --enable-experimental-cpp11 was given.
+if test "${enable_experimental_cpp11+set}" = set; then :
+  enableval=$enable_experimental_cpp11;
+fi
+
 
 
 if test x"$enable_openmp_parallel" != x"" ;
@@ -7157,7 +7325,7 @@ then
 
 fi
 
-if test x"$enable_debug_checks" != x"no" ;
+if test x"$enable_debug_checks" == x"yes" ;
 then
 	if test x"$enable_openmp_parallel" != x"" ;
 	then
@@ -7212,12 +7380,21 @@ $as_echo "\"Enabling Gcc-UbSan\"" >&6; };
 	fi
 
 else
-	if test x"$enable_debug_checks" !=x"yes" ;
+	echo x"$enable_debug_checks"
+	echo x"no"
+
+	if test x"$enable_debug_checks" != x"no" ;
 	then
-		as_fn_error $? "\"Well something isnt right, debug checks should be enabled or disabled (yes/no\"" "$LINENO" 5
+		as_fn_error $? "\"Well something isnt right, debug checks should be enabled or disabled (yes/no), was $enable_debug_checks\"" "$LINENO" 5
 	fi
 fi
 
+if test x"$enable_experimental_cpp11" == x"yes" ;
+then
+	#Should work for gcc/clang
+	CXXFLAGS="$CXXFLAGS --std=c++11"
+fi
+
 if test x"$CXX" = xdistcc ; then
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: \"Adding distcc link flags\"" >&5
 $as_echo "\"Adding distcc link flags\"" >&6; };
@@ -7400,25 +7577,198 @@ $as_echo "#define const /**/" >>confdefs.h
 
 fi
 
+for ac_header in stdlib.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdlib_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_STDLIB_H 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5
+$as_echo_n "checking for GNU libc compatible malloc... " >&6; }
+if ${ac_cv_func_malloc_0_nonnull+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then :
+  ac_cv_func_malloc_0_nonnull=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *malloc ();
+#endif
+
+int
+main ()
+{
+return ! malloc (0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_func_malloc_0_nonnull=yes
+else
+  ac_cv_func_malloc_0_nonnull=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5
+$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; }
+if test $ac_cv_func_malloc_0_nonnull = yes; then :
+
+$as_echo "#define HAVE_MALLOC 1" >>confdefs.h
+
+else
+  $as_echo "#define HAVE_MALLOC 0" >>confdefs.h
+
+   case " $LIBOBJS " in
+  *" malloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
+ ;;
+esac
+
+
+$as_echo "#define malloc rpl_malloc" >>confdefs.h
+
+fi
+
+
+for ac_header in stdlib.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdlib_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_STDLIB_H 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5
+$as_echo_n "checking for GNU libc compatible realloc... " >&6; }
+if ${ac_cv_func_realloc_0_nonnull+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then :
+  ac_cv_func_realloc_0_nonnull=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *realloc ();
+#endif
+
+int
+main ()
+{
+return ! realloc (0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_func_realloc_0_nonnull=yes
+else
+  ac_cv_func_realloc_0_nonnull=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5
+$as_echo "$ac_cv_func_realloc_0_nonnull" >&6; }
+if test $ac_cv_func_realloc_0_nonnull = yes; then :
+
+$as_echo "#define HAVE_REALLOC 1" >>confdefs.h
+
+else
+  $as_echo "#define HAVE_REALLOC 0" >>confdefs.h
+
+   case " $LIBOBJS " in
+  *" realloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS realloc.$ac_objext"
+ ;;
+esac
+
+
+$as_echo "#define realloc rpl_realloc" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t"
+case $ac_cv_c_uint64_t in #(
+  no|yes) ;; #(
+  *)
+
+$as_echo "#define _UINT64_T 1" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint64_t $ac_cv_c_uint64_t
+_ACEOF
+;;
+  esac
+
 
 # Checks for library functions.
-for ac_func in atexit
+for ac_func in atexit sqrt
 do :
-  ac_fn_c_check_func "$LINENO" "atexit" "ac_cv_func_atexit"
-if test "x$ac_cv_func_atexit" = xyes; then :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_ATEXIT 1
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
 _ACEOF
 
 fi
 done
 
-for ac_func in sqrt
+for ac_func in floor getcwd gettimeofday isascii memset
 do :
-  ac_fn_c_check_func "$LINENO" "sqrt" "ac_cv_func_sqrt"
-if test "x$ac_cv_func_sqrt" = xyes; then :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_SQRT 1
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+for ac_func in pow setlocale strdup sysinfo
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
 _ACEOF
 
 fi
@@ -7467,6 +7817,51 @@ _ACEOF
 esac
 
 
+#check for some headers
+for ac_header in fenv.h libintl.h sys/time.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default"
+if test "x$ac_cv_type_ptrdiff_t" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PTRDIFF_T 1
+_ACEOF
+
+
+fi
+
+
+#Check platform specific headers
+case "${host_os}" in
+	darwin*)
+		 for ac_header in mach/mach.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "mach/mach.h" "ac_cv_header_mach_mach_h" "$ac_includes_default"
+if test "x$ac_cv_header_mach_mach_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_MACH_MACH_H 1
+_ACEOF
+
+fi
+
+done
+
+		;;
+	*)
+		;;
+esac
+
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -8014,7 +8409,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by 3Depict $as_me 0.0.17, which was
+This file was extended by 3Depict $as_me 0.0.18, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -8080,7 +8475,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-3Depict config.status 0.0.17
+3Depict config.status 0.0.18
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index c38c1c0..c6dd0e5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([3Depict], [0.0.17]) 
+AC_INIT([3Depict], [0.0.18]) 
 AM_INIT_AUTOMAKE([foreign subdir-objects])
 AC_PROG_CXX
 AC_PROG_CC
@@ -261,10 +261,13 @@ case "${host_os}" in
 		#Add GLEW dependency for opengl > 1.1
 		GL_LIBS="$GL_LIBS -lglew32"
 		AC_SUBST(GL_LIBS)
+		AC_PROG_LN_S 
+		AC_PROG_RANLIB 
 	    ;;
 	darwin*)
 		#This is handled by the --with-apple-opengl-framework 
 		#option already. Nothing to do here
+
 	    ;;
 	 *)
 		AC_CHECK_LIB(GLU, gluSphere, GL_LIBS="$GL_LIBS -lglu", [AC_MSG_ERROR([Could not find GLU library])])
@@ -374,12 +377,14 @@ AC_ARG_ENABLE(openmp-parallel,
   [  --enable-openmp-parallel  Enable OpenMP multi-CPU usage; requires GCC > 4.2 for parallel STL support ])
 #Should we enable or disable debug checking?
 AC_ARG_ENABLE(debug-checks,
-  [  --disable-debug-checks Disable any debug checking, provides faster operation, but less information needed to debug internal problems, or to provide problem reports to developers ],[enable_no_debug_checks="yes"],[enable_no_debug_checks="no"])
+  [  --disable-debug-checks Disable any debug checking, provides faster operation, but less information needed to debug internal problems, or to provide problem reports to developers ],[enable_debug_checks="no"],[enable_debug_checks="yes"])
 
 AC_ARG_ENABLE(ubsan,
 	[ --disable-ubsan Disable undefined behaviour sanitizer. Only takes effect on certain ubsan supporting compilers. Useful for working around ubsan aborts that you cant fix (eg 3rd party libs ],
 		[ enable_no_ubsan="yes"],[enable_no_ubsan="no"])
 
+AC_ARG_ENABLE(experimental-cpp11,
+  [  --enable-experimental-cpp11 Enable experimental C++11 support. Requires a full C++11 compiler (eg gcc/clang).])
 
 
 if test x"$enable_openmp_parallel" != x"" ; 
@@ -388,7 +393,7 @@ then
 	AC_SUBST(OPENMP_FLAGS)
 fi
 
-if test x"$enable_debug_checks" != x"no" ; 
+if test x"$enable_debug_checks" == x"yes" ; 
 then
 	if test x"$enable_openmp_parallel" != x"" ;
 	then
@@ -442,12 +447,21 @@ then
 	fi
 
 else 
-	if test x"$enable_debug_checks" !=x"yes" ;
+	echo x"$enable_debug_checks"
+	echo x"no"
+	
+	if test x"$enable_debug_checks" != x"no" ;
 	then
-		AC_MSG_ERROR(["Well something isnt right, debug checks should be enabled or disabled (yes/no"])
+		AC_MSG_ERROR(["Well something isnt right, debug checks should be enabled or disabled (yes/no), was $enable_debug_checks"])
 	fi
 fi
 
+if test x"$enable_experimental_cpp11" == x"yes" ;
+then
+	#Should work for gcc/clang
+	CXXFLAGS="$CXXFLAGS --std=c++11"
+fi
+
 if test x"$CXX" = xdistcc ; then
 	AC_MSG_RESULT(["Adding distcc link flags"]);
 	#Add -lstdc++ to libs for distcc
@@ -457,12 +471,30 @@ fi
 # Checks for typedefs, structures, and compiler characteristics.
 AC_HEADER_STDBOOL
 AC_C_CONST
+AC_FUNC_MALLOC 
+AC_FUNC_REALLOC 
+AC_TYPE_SIZE_T 
+AC_TYPE_UINT64_T 
 
 # Checks for library functions.
-AC_CHECK_FUNCS([atexit])
-AC_CHECK_FUNCS([sqrt])
+AC_CHECK_FUNCS([atexit sqrt])
+AC_CHECK_FUNCS([floor getcwd gettimeofday isascii memset ])
+AC_CHECK_FUNCS([pow setlocale strdup sysinfo])
 AC_C_INLINE()
 
+#check for some headers
+AC_CHECK_HEADERS([fenv.h libintl.h sys/time.h])
+AC_CHECK_TYPES([ptrdiff_t])
+
+#Check platform specific headers
+case "${host_os}" in 
+	darwin*)
+		 AC_CHECK_HEADERS([mach/mach.h])
+		;;
+	*)
+		;;
+esac
+
 AC_PROG_INSTALL
 AC_OUTPUT
 
diff --git a/docs/manual-latex/manual.tex b/docs/manual-latex/manual.tex
index 26d9ee5..36f168b 100644
--- a/docs/manual-latex/manual.tex
+++ b/docs/manual-latex/manual.tex
@@ -40,7 +40,7 @@
 \begin{minipage}{0.3\textwidth}
 \begin{flushright} \large
 \emph{Version:} \\
- 0.0.17, Sep 2014\end{flushright}
+ 0.0.18, Apr 2014\end{flushright}
 \end{minipage}
 
 \vfill
@@ -837,6 +837,18 @@ If the drawing primitive is set to be shown, then a drawable stream will be emit
  This will generate a histogram of the ``value'' of ions passing through the filter. Note that no output other than the histogram is generated. Plots can be assigned a colour, set to logarithmic or non-logarithmic mode, or restricted to only cover a specific region. The plot title is taken from the filter name, some limited \LaTeX is supported (note that the ``$\backslash$'' symbol is a special \LaTeX command; you may need to use ``$\backslash$$\backslash$'' to represent a single ``$\bac [...]
  
 
+\begin{itemize}
+\item \textbf{Bin Size} : The width of each histogram bin to use when computing the spectrum. 
+\item \textbf{Normalisation}: This option will rescale the spectrum, when enabled. When disabled, the spectrum will be plotted on a per-count basis. One normalisation mode is to rescale the data using te maximum value across the entire plot. Otherwise, a normalisation can be performed within two bounds.
+\item \textbf{Background (Mode)} : This option allows for the selects the method for removing the background from the given data. 
+\item \textbf{Logarithmic} : Specifies if the displayed plot is to be drawn in log mode (ticked), or in linear mode (unticked)
+	\begin{itemize}	
+		\item \textbf{Flat TOF} : this mode uses a sqrt-mass (as mass is proportional to the square of the TOF) extract and fit method to estimate the background in the spectrum. This uses the data between the specified start and end mass to perform fitting. If insufficient data has been obtained to validate the fit (binned data must form a gaussian distribution, anderson test), then no corrected spectrum is created, and a message is generated.  
+		\item \textbf{Mass Start} : This specifies the start of the cutoff window for choosing the data. The window must span a region of background.
+		\item \textbf{Mass End} : This specifies the end of the cutoff window for choosing the data. 
+.\end{itemize} 
+e that for 
+\end{itemize}
 {%
 \newcommand{\mc}[3]{\multicolumn{#1}{#2}{#3}}
 \begin{table}[!h]
@@ -903,15 +915,18 @@ Drawable will be emitted if the ``Show Primitive'' option is selected.
 \item \textbf{Radial distribution}: Computes the local environment for each ion, and generates a histogram of the number of points within a spherical section surrounding each ion. 
 \item \textbf{Axial distribution} : Computes the so-called ``directional RDF'' or 1D RDF, which can be used to measure spatial correlations between points. 
 \item \textbf{Binomial distribution} : Computes the binomial distribution probabilities for the dataset, using the method of Moody et al~\cite{Moody2008}.
+\item \textbf{Point em/re-placement} : Replace or load points with specified points from a file, using subtract, intersect and union modes
 \end{itemize}
 
 Local density and density filtering algorithms are relatively simple, and mostly are self-contained concepts. This can be used to identify the local density in your dataset, which in the case of APT, originates due to limitations in the technique. For the Radial Distribution Function (RDF) algorithm , this can be used to examine local correlations between points, which may or may not exist in your dataset. The  RDF technique is covered in several standard textbooks on APT~\cite{Gault2012}. 
 
-Axial distribution functions are covered in technical literature where they find use in APT and be referred to via a number of differing names, such as ``SDM''s~\cite{Geiser2007}, atom-vicinity~\cite{Boll2007}, or directional pair-correlation functions. The axial distribution function implementation in 3Depict, allows for users to select and drag out the region to be analysed, with the axis of the cylinder providing both the cropping orientation and the axial direction in which to perfor [...]
+\paragraph{Radial Distribution:} The radial distribution function counts pair-pair distances between species. This can be used to find local tests whereby certain point types are more likely to be  present at given distances from one another. Note that the distribution often requires normalisation by $y=x^2$, at this time this is not implemented and needs to be done manually.
 
-The binomial distribution function can be used to test for randomness in the spatial distribution of the points. The program computes a ``p'' value, which is the probability that the observed data was drawn from a  randomly distributed (at the scale of the analysis) set of values on fixed data points. Grouping is performed by a grid-extrusion algorithm, which assigns each set of points in the dataset to a given bin, and thus a given count in the output histogram. The output histogram sho [...]
+\paragraph{Axial Distribution:} Axial distribution functions are covered in technical literature where they find use in APT and be referred to via a number of differing names, such as ``SDM''s~\cite{Geiser2007}, atom-vicinity~\cite{Boll2007}, or directional pair-correlation functions. The axial distribution function implementation in \emph{3Depict}, allows for users to select and drag out the region to be analysed, with the axis of the cylinder providing both the cropping orientation and [...]
 
+\paragraph{Binomial:} The binomial distribution function can be used to test for randomness in the spatial distribution of the points. The program computes a ``p'' value, which is the probability that the observed data was drawn from a  randomly distributed (at the scale of the analysis) set of values on fixed data points. Grouping is performed by a grid-extrusion algorithm, which assigns each set of points in the dataset to a given bin, and thus a given count in the output histogram. Th [...]
 
+\paragraph{Point em/re-placement} : This function allows for merging points in the current data, $A$ from another file, $B$. There are several operating modes, ``subtract'',``intersect'' and ``union''. In the subtract mode, points which have a matching element in $B$ will be removed. In intersect mode, \emph{only} points that have a matching point in the file will be retained - the value to be assigned to the point is taken from $B$. Similarly, in union mode, both $A$ and $B$ are loaded, [...]
 {%
 \newcommand{\mc}[3]{\multicolumn{#1}{#2}{#3}}
 \begin{table}[!h]
diff --git a/docs/samples/externalProg/python-example.py b/docs/manual-latex/python-example.py
similarity index 100%
copy from docs/samples/externalProg/python-example.py
copy to docs/manual-latex/python-example.py
diff --git a/docs/samples/externalProg/bash-example.sh b/docs/samples/externalProg/bash-example.sh
old mode 100644
new mode 100755
diff --git a/docs/samples/externalProg/python-example.py b/docs/samples/externalProg/python-example.py
old mode 100644
new mode 100755
diff --git a/docs/web/about.html b/docs/web/about.html
deleted file mode 100644
index e2e973f..0000000
--- a/docs/web/about.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>About 3Depict </title> 
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-
-
-</head>
-
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-<div id="left">
-<div class="box">
-<h2>Problem not solved?</h2><br/>
-Check the <a href="https://sourceforge.net/apps/phpbb/threedepict/">forums</a> to see if your question has already been answered.<br/>
- 
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-
-
-<div id="content">
-<div id="right">
-
-<h1>About</h1>
-<h2>Motivation<br/>
-</h2>
-This program was written by D. Haley, starting in February 2010, during
-his spare time. The first usable prototype, prior to the 0.0.1 release
-was finished at the end of July 2010.<br/>
-<br/>
-A. Ceguerra helped with Mac OS porting for 0.0.1, and developed many of the features and bugfixes in the program since August 2010.
-<br/>
-<br/>
-The program was deloped to meet personal analysis needs. Understanding
-the relationship between the action of computer algorithms on point
-data, and the effect of input parameters (mainly distortion
-corrections), necessitated the development of a rapid-feedback system.
-Secondly the problem needed the abiilty to flexibly construct unusual
-combinations of operations.<br/>
-
-<br/>
-In short, if you can see something, you might just understand it.<br/>
-<h2>Contributions</h2>
-Many people have either had their work utilised, or have assisted in the
-creation of this program. Without their work, this program could never
-have got beyond the concept stage.<br/>
-<h3>Thanking:</h3>
-<ul>
-  <li>The <a href="http://wxwidgets.org">wxWidgets</a> team</li>
-  <li>Alexy Balakin (<a href="http://mathgl.sourceforge.net/">MathGL</a>), <br/>
-  </li>
-  <li><a href="http://ftgl.sourceforge.net">FTGL</a> and <a href="http://freetype.org">freetype</a> people, <br/>
-  </li>
-  <li>The <a href="http://tree.phi-sci.com/">tree.h</a> guy (Kasper Peeters)  </li>
-  <li><a href="http://www.gnu.org/software/gsl/">GNU Scientific Library</a> developers. </li>
-  <li><a href="http://www.cppcheck.sourceforge.net">Cppcheck</a> and <a href="http://www.valgrind.org">Valgrind</a> developers, for great debugging tools</li>
-
-  <li>Minimalistic design whose <a href="style.css">CSS</a> I modifed (GPLv3) for the website template.<br/>
-  </li>
-  <li>The random internet postings that help me debug weird and confusing problems<br/>
-  </li>
-
-</ul>
-
-And anyone else I missed!
-
-</div>
-</div>
-
-
-</body></html>
diff --git a/docs/web/compiling-OSX.html b/docs/web/compiling-OSX.html
deleted file mode 100644
index 56dc075..0000000
--- a/docs/web/compiling-OSX.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
-<link rel="stylesheet" href="style.css" type="text/css"/>
-<link rel="icon" type="image/png" href="favicon.png"/>
-
-<title>compiling.html</title></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Get the code</h2>
-<p> <a href="https://sourceforge.net/projects/threedepict/files/">Download</a> the source code for the latest version of 3Depict for your platform.</p> Alternately, download the latest <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/">development version</a>.
-</div>
-<div class="box">
-<h3>Stuck on how to compile?</h3>
-Try asking for help on the <a href="https://sourceforge.net/apps/phpbb/threedepict/">forums</a>
-<p></p>
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-<div id="content">
-<div id="right">
-
-<h1> Preface </h1>
-
-Mac OSX is currently a "contributed" build platform, and thus the build systems are more rough than for other systems. Some scripts are provided to smooth the process, however these may be out of date. The developers cannot continuously build this platform, as it is against the OSX licence to run OSX on virtual machines. Cross-compilation is not readily feasible either. If you can assist in building for this platform, please <a href="contact.html">contact us</a>.
-
-<h1>Compiling</h1>
-
-Mac OS does not have a proper package management system. You need XCode installed (<a href="http://developer.apple.com/mac/library/documentation/Xcode/Conceptual/XcodeCoexistence/Contents/Resources/en.lproj/Basics/Basics.html">here</a>). For the latest version of OSX for some reason this is not quite free, and you now are required to use the app store or something to get this or build gcc yourself (somehow) it appears. So we can't easily automate this procedure for you.
-
-Considering GCC (the bit we need) is completely free (GPL), it is unclear what the deal is here. To make matters more complex, there are <i>two</i> compilers (clang & GCC)  and <i>two</i> standard libraries (libstdc++ and libc++), all of which are incompatible with the alternate. This means you must compile everything with the same compiler the whole time - read <a href="https://trac.macports.org/wiki/UsingTheRightCompiler">here</a> for how to do this (macports & local). Check to mak [...]
-
-Note that at time of writing the wxWidgets version provided with XCode is several years out-of-date and won't work. Please don't try to use any of the built-in libraries provided with XCode - firstly, you cannot mix and match library linkage, it simply won't work (crashes) and secondly they are all very out of date.  In fact, using XCode at all is highly discouraged, due to its poor portability, and the difficultly scripting solutions and debugging problems.
-
-<br/>
-
-There is an automatic dependency grabbing script in the 3Depict source
-code under the "deps" folder. Run that (ensure you are connected to the
-Internet) to automatically download, build and install all the software
-dependencies. If it does not work, this is a bug, so let us know.<br/>
-
-<br/>
-To run the script, simply open terminal.app, navigate to the "packaging/deps" folder containing "getDeps", then type:<br/>
-<br/>
-<span style="font-family: monospace;">$ ./getDeps</span><br/>
-
-<br/>
-This will check your system for a compiler, and then will proceed to download and install the needed components in an automatic fashion. The compilation process can take several hours.<br/>
-<br/>
-
-<h2>Packaging</2>
-Due to some design decisions under OSX, programs cannot be directly run, but first must be packaged. Attempts to run programs without creating the <a href="http://wiki.wxwidgets.org/WxMac-specific_topics#Building_a_MacOSX_application_bundle">".app package"</a> (actually a folder with metadata), will cause the program to function incorrectly (eg user input will be ignored).
-
-To aid compilation and packaging, use the "3package.sh" script in the "packaging/mac" folder. Execute this to build and package the program. In this way, a .app package should be built and ready for use.
-
-</div></div>
-
-</body></html>
diff --git a/docs/web/compiling-cross.html b/docs/web/compiling-cross.html
deleted file mode 100644
index e8bb836..0000000
--- a/docs/web/compiling-cross.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
-<link rel="stylesheet" href="style.css" type="text/css"/>
-<link rel="icon" type="image/png" href="favicon.png"/>
-
-<title>compiling.html</title></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Get the code</h2>
-<p> <a href="https://sourceforge.net/projects/threedepict/files/">Download</a> the source code for the latest version of 3Depict for your platform.</p> Alternately, download the latest <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/">development version</a>.
-</div>
-<div class="box">
-<h3>Stuck on how to compile?</h3>
-Try asking for help on the <a href="https://sourceforge.net/apps/phpbb/threedepict/">forums</a>
-<p></p>
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-<div id="content">
-<div id="right">
-
-<h1>Cross Compilation</h1>
-
-<p>Compiling under windows is quite tricky.  Compiling for windows under a Linux environment is significantly easier. Easier still, we have automated the procedure to build windows executables via a technique known as <a href="https://en.wikipedia.org/wiki/Cross_compiler">Cross compiling</a>. This technique is used for the official 3Depict builds.</p>
-
-<p>To run the cross compilation script, you must first have an installed Debian-like Linux system. We recommend "Debian", however you can probably use Linux Mint or Ubuntu. We have not tried these. </p>
-
-<p>For windows users, you can install a <a href="http://www.brianlinkletter.com/installing-debian-linux-in-a-virtualbox-virtual-machine/">virtual machine</a> to do the trick, then start the build. Make sure you allocate lots of RAM and disk space (>8GB ram or swap needed, >10GB disk space recommended), to ensure the build works. On older machines, you may need to <a href="http://www.sysprobs.com/disable-enable-virtualization-technology-bios">enable</a> virtual machine access in the <a hr [...]
-
-
-<h1> Procedure</h1>
-<p>Having downloaded the source code, copy the <span style="font-family: monospace;">./packaging/mingw-debian-cross/</span> folder to your home directory, eg <span style="font-family: monospace;">/home/user/mingw-debian-cross/</span>. Once done, create a folder called <span style="font-family: monospace;">code</span> inside <span style="font-family: monospace;">mingw-debian-cross/</span>. This is case sensitive. Copy the <span style="font-family: monospace;">3Depict</span> folder into th [...]
-
-<p>Now run the  <span style="font-family: monospace;">bootstrap.sh</span> script to execute the cross-compilation - the process can take several hours on a standard system to execute a full build. On the first run you will be asked whether you wish to compile for 32 or 64 bit windows. Once complete, you will be given a 3Depict-VERSION-BIT.exe file that you can use under windows. </p>
-
-To get the file out of your virtual machine, we recommend using a USB key. Plug the USB key in, then select it in your virtual machine, and copy the file over. It is possible to also do this by drag and drop onto your desktop, but you need to enable the virtual machine's <a href="http://www.virtualbox.org/manual/ch04.html">guest additions</a>. This can be a tricky process in itself, so it is not recommended. Many other methods of copying the file are possible, but not discussed here.</p>
-</div></div>
-
-</body></html>
diff --git a/docs/web/compiling-linux.html b/docs/web/compiling-linux.html
deleted file mode 100644
index eb1ee82..0000000
--- a/docs/web/compiling-linux.html
+++ /dev/null
@@ -1,153 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
-<link rel="stylesheet" href="style.css" type="text/css"/>
-<link rel="icon" type="image/png" href="favicon.png"/>
-
-<title>compiling.html</title></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Get the code</h2>
-<p> <a href="https://sourceforge.net/projects/threedepict/files/">Download</a> the source code for the latest version of 3Depict for your platform.</p> Alternately, download the latest <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/">development version</a>.
-</div>
-<div class="box">
-<h3>Stuck on how to compile?</h3>
-Try asking for help on the <a href="https://sourceforge.net/apps/phpbb/threedepict/">forums</a>
-<p></p>
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-<div id="content">
-<div id="right">
-
-<h1>Compiling for Linux</h1>
-
-
-<p>
-
-
-In the following instructions, <span style="text-decoration: underline; font-family: monospace;">$</span> and <span style="text-decoration: underline; font-family: monospace;">#</span> indicate to enter command at a command prompt, as either a normal user or as an <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Root_user">administrator</a>. Do not type the <span style="font-family: monospace;">$</span> or <span style="font-family: monospace;">#</span> when entering the commands.  [...]
-</p>
-<p>
-If there are any problems or flaws with the instructions, please <a href="contact.html">contact the author</a>.
-</p>
-
-<h2> Installing dependencies</h2>
-<h3>Automatic dependancy retrieval</h3>
-<p>
-An experimental script "getDeps" is supplied in the <i>packaging/deps/</i> folder. Run this script to automatically detect your platform and download and install the required dependencies.</p>
-
-<p>
-Alternately, use the following instructions:</p>
-
-
-<h3>Debian and derivatives</h3>
-Debian Linux and its derivatives (Linux Mint, Ubuntu, etc), can use the following procedure.
-To build 3Depict, you need to install the following packages:
-<ul>
-<li>build-essential</li>
-<li>libwxgtk2.8-dev</li>
-<li>libmgl-dev</li>
-<li>libxml2-dev</li>
-<li>libftgl-dev</li>
-<li>libqhull-dev</li>
-</ul>
-
-
-This can be done through the command line (below), or using the gui (synaptic package manager)<br/>
-
-<br/>
-
-<span style="text-decoration: underline; font-family: monospace;">$</span><span style="font-family: monospace;"> sudo apt-get build-dep 3depict </span><br/>
-
-
-
-<h3>Fedora/RedHat:</h3>
-
-<ul>
-<li>"Development Tools"  group package</li>
-<li>mathgl-devel</li>
-<li>libxml2-devel</li>
-<li>ftgl-devel</li>
-<li>wxgtk-devel</li>
-<li>qhull-devel</li>
-
-</ul>
-
-
-This can be done through either the command line, or through the GUI <br/>
-
-<br/>
-
-
-<span style="text-decoration: underline; font-family: monospace;">$</span><span style="font-family: monospace;"> yum groupinstall "Development Tools"</span><br/>
-
-<span style="text-decoration: underline; font-family: monospace;">$</span><span style="font-family: monospace;"> yum install mathgl-devel libxml2-devel ftgl-devel wxgtk-devel qhull-devel</span><br/>
-<br/>
-
-<h3>OpenSuse:</h3>
-<i>These instructions may be slightly out of date</i>
-<ul>
-<li>wxGTK-devel</li>
-<li>libxml2-devel</li>
-<li>ftgl-devel</li>
-<li>qhull-devel</li>
-</ul>
-
-
-This can be done either through the graphical interface (YaST) or at the command line.<br/>
-
-<br/>
-
-
-<span style="font-family: monospace;">    $ zypper install -t devel_C_C++</span><br/>
-
-<span style="font-family: monospace;">    $ zypper install wxGTK-devel libxml2-devel ftgl-devel qhull-devel</span><br/>
-
-    <br/>
-
-Unfortunately, mathgl is not available as a pre-built package for Suse.
-You must therefore download and install it manually from the mathgl
-website:<br/>
-
-<ul>
-<li><a href="http://mathgl.sourceforge.net/">http://mathgl.sourceforge.net/</a></li>
-</ul>
-
-</ul>
-
-
-<h2> Compiling</h2>
-<p>
-To compile 3Depict, ensure you have installed all the required dependencies (above), then use the following commands in the 3Depict/ directory:</p>
-
-<p>
-<span style="font-family: monospace;">    $ ./configure</span> <br/><br/> Optionally, you may use <span style="font-family: monospace;">--enable-openmp-parallel</span> or <span style="font-family: monospace;">--disable-debug-checks</span> to enable parallelism or disable debug checks respectively. <br/>
-<span style="font-family: monospace;">    $ make</span><br/>to build 3depict.<br/><br/>
-
-Once built, you can install the program with:
-<span style="font-family: monospace;">make install</span></p>
-
-
-<h2> Compiling under eclipse</h2>
-Although not officially supported, some users report being able to compile 3Depict under the eclipse IDE. You can read more about the method on the <a href="http://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=26">forums</a>.
-
-
-</div></div>
-</body></html>
diff --git a/docs/web/compiling-macosx.html b/docs/web/compiling-macosx.html
deleted file mode 100644
index 1db56a9..0000000
--- a/docs/web/compiling-macosx.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
-<link rel="stylesheet" href="style.css" type="text/css"/>
-<link rel="icon" type="image/png" href="favicon.png"/>
-
-<title>compiling.html</title></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Get the code</h2>
-<p> <a href="https://sourceforge.net/projects/threedepict/files/">Download</a> the source code for the latest version of 3Depict for your platform.</p> Alternately, download the latest <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/">development version</a>.
-</div>
-<div class="box">
-<h3>Stuck on how to compile?</h3>
-
-Try asking for help on the <a href="https://sourceforge.net/apps/phpbb/threedepict/">forums</a>
-<p></p>
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-<div id="content">
-<div id="right">
-
-<h1> Preface </h1>
-
-Mac OSX is currently a "contributed" build platform, and thus the build systems are more rough than for other systems. Some scripts are provided to smooth the process, however these may be out of date. The developers cannot continuously build this platform, as it is against the OSX licence to run OSX on virtual machines. Cross-compilation is not readily feasible either. If you can assist in building for this platform, please <a href="contact.html">contact us</a>.
-
-
-<h1>Compiling</h1>
-<h2>Dependencies</h2>
-Mac OS does not have a proper package management system. You need XCode installed (<a href="http://developer.apple.com/mac/library/documentation/Xcode/Conceptual/XcodeCoexistence/Contents/Resources/en.lproj/Basics/Basics.html">here</a>). For the latest version of OSX for some reason this is not quite free, and you now are required to use the app store or something to get this or build gcc yourself (somehow) it appears. So we can't easily automate this procedure for you.
-
-Considering GCC (the bit we need) is completely free (GPL), it is unclear what the deal is here.
-
-Note that at time of writing the wxWidgets version provided with XCode is several years out-of-date and won't work. Please don't try to use any of the built-in libraries provided with XCode - firstly, you cannot mix and match library linkage, it simply won't work (crashes) and secondly they are all very out of date. 
-
-<br/>
-
-There is an automatic dependency grabbing script in the 3Depict source
-code under the "deps" folder. Run that (ensure you are connected to the
-Internet) to automatically download, build and install all the software
-dependencies. If it does not work, this is a bug, so let us know.<br/>
-
-<br/>
-To run the script, simply open terminal.app, navigate to the "packaging/deps" folder containing "getDeps", then type:<br/>
-<br/>
-<span style="font-family: monospace;">$ ./getDeps</span><br/>
-
-<br/>
-This will check your system for a compiler, and then will proceed to download and install the needed components in an automatic fashion. The compilation process can take several hours.<br/>
-<br/>
-
-<h2>Building and Packaging</h2>
-Due to some design decisions under OSX, programs cannot be directly run, but first must be packaged. Attempts to run programs without creating the ".app package" (actually a folder with metadata), will cause the program to function incorrectly (eg user input will be ignored).
-
-To aid compilation and packaging, use the "3package.sh" script in the "packaging/mac" folder. Execute this to build and package the program. In this way, a .app package should be built and ready for use.
-
-</div></div>
-
-</body></html>
-
diff --git a/docs/web/compiling.html b/docs/web/compiling.html
deleted file mode 100644
index ecad8f9..0000000
--- a/docs/web/compiling.html
+++ /dev/null
@@ -1,91 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
-<link rel="stylesheet" href="style.css" type="text/css"/>
-<link rel="icon" type="image/png" href="favicon.png"/>
-
-<title>compiling.html</title></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Get the code</h2>
-<p> <a href="https://sourceforge.net/projects/threedepict/files/">Download</a> the source code for the latest version of 3Depict for your platform.</p> Alternately, download the latest <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/">development version</a>.
-</div>
-<div class="box">
-<h3>Stuck on how to compile?</h3>
-Try asking for help on the <a href="https://sourceforge.net/apps/phpbb/threedepict/">forums</a>
-<p></p>
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-<div id="content">
-<div id="right">
-
-<h1>Source Compilation</h1>
-
-Source compilation is tricky, and requires an in-depth knowledge of
-both C++ programming and the internals of your computer's software. It
-is fraught with <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Command_line">cryptic peril</a>;
-you have been warned. If you are not familiar with the concept of
-source compilation, or software development in general, this procedure
-may prove very difficult. Some help is available on the forums, but
-this is a limited and typically slow method of communication.<br/>
-
-<br/>
-
-Looking for an easier install? <a href="download.html">Check</a> to see if your platform is supported. If it is not supported, you can <a href="contact.html">contact the author</a> for other options.<br/><br/>
-<hr/>
-<h2>Getting started</h2>
-
-There are two main stages to compiling, <br/>
-
-
-<ul>
-<li>setting up your computer to be able to build, and 
-</li><li>actually building the program.</li>
-</ul>
-
-
-
-In the following instructions, <span style="text-decoration: underline; font-family: monospace;">$</span> and <span style="text-decoration: underline; font-family: monospace;">#</span> indicate to enter command at a command prompt, as either a normal user or as an <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Root_user">administrator</a>. Do not type the <span style="font-family: monospace;">$</span> or <span style="font-family: monospace;">#</span> when entering the commands.  [...]
-<br/>
-
-If there are any problems or flaws with the instructions, please <a href="contact.html">contact the author</a>
-
-<h2>Get the code</h2>
-Either <a href="https://sourceforge.net/projects/threedepict/files/">Download</a> the source code (<a href="https://secure.wikimedia.org/wikipedia/en/wiki/Tar.gz">.tar.gz</a> file), or grab the <a href="http://mercurial.selenic.com">mercurial</a> repository using this clone command, or by opening the URL in your graphical mercurial client (the URL is the bit at the right). 
-<br/>
-<br/>
-<span style="font-family: monospace;">$ hg clone http://hg.code.sf.net/p/threedepict/code/ 3Depict</span>
-<br/>
-
-
-
-
-<h2>Compiling for your platform</h2>
-To compile, you must first have a working compilation environment. This is diferent for each system. Follow these notes to set up your environment for your system
-<ul>
-<li><a href="compiling-linux.html">Linux</a> - this is the recommended system for compiling, as it is the easiest to work with.</li>
-<li><a href="compiling-cross.html">Windows via Debian Linux</a> - this is the recommended method for building windows executables. We <b>do not</b> recommend building under windows directly, as it is very complicated.</li>
-<li><a href="compiling-macosx.html">Mac OSX</a> - Rough guide for OSX compilation.
-</ul>
-
-
-</div></div>
-
-</body></html>
diff --git a/docs/web/contact.html b/docs/web/contact.html
deleted file mode 100644
index 123de14..0000000
--- a/docs/web/contact.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>Contact 3Depict</title> 
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-
-</head>
-
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Looking for something?</h2><br/>
-Check the <a href="questions.html">FAQ</a> to see if your question has already been answered.<br/>
- 
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-
-
-<div id="content">
-<div id="right">
-
-<h1>Contact</h1>
-<h2>Forums<br/></h2>
-You can post messages to the <a href="https://sourceforge.net/apps/phpbb/threedepict/index.php">forums</a> -- registration not required. 
-
-<h2>Email</h2>
-<form id="emf-form" enctype="multipart/form-data" method="post" action="http://www.emailmeform.com/builder/form/ff3Jr0WI55h54"><table style="text-align:left;" cellpadding="2" cellspacing="0" border="0" bgcolor="transparent"><tr><td style="" colspan="2"></td></tr><tr valign="top"><td style="" ><font face="Verdana" size="2" color="#000000"><b>Name</b></font><span style="color:red;"><small>*</small></span></td></tr><tr><td style=""><input id="element_0" name="element_0" value="" size="30" c [...]
-<p>
-If you need to attach an image, you can do so using an image upload service, such as <a href="http://imagebin.ca/">Imagebin.ca</a>.
-</p>
-</div>
-</div>
-
-</body></html>
diff --git a/docs/web/documentation.html b/docs/web/documentation.html
deleted file mode 100644
index 25c4fa2..0000000
--- a/docs/web/documentation.html
+++ /dev/null
@@ -1,103 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html><head>
-
-  
-  <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"><title>Documentation</title>
-  <meta name="description" content="Documentation for 3Depict" />
-
-    	<link rel="icon" type="image/png" href="favicon.png"/>
-  	<link rel="stylesheet" href="style.css" type="text/css" meda="screen"></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-  <li><a href="index.html">Home</a></li>
-  <li><a href="download.html">Download</a></li>
-  <li><a href="questions.html">Questions</a></li>
-  <li><a href="documentation.html">Documentation</a></li>
-  <li><a href="contact.html">Contact</a></li>
-  <li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Want to help out?</h2>
-You can help, even without programming; (<a href="http://transifex.net/projects/p/3depict">translation</a>, web page maintenance, etc) — Just <a href="contact.html"/>get in touch</a>. <br/><br/> If you do program, try <a href="compiling.html">downloading the source</a> and get a build up and running, or just ask.
-<br>
- 
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-
-<div id="content">
-<div id="right"><br>
-<h1>Documentation</h1>
-
-
-<h2>User's Reference</h2>
-PDF Manual <a href="manual.pdf">here</a>. There is an <a href="manual/manual.html">HTML version</a>, automatically generated from the PDF.
-
-
-<h2>Screencasts</h2>
-
-New features 0.0.14 (July 2013):<br>
-<center>
-<video src="3Depict-0.0.14-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br></small>
-<small><a href="3Depict-0.0.14-screencast.ogg">download video</a></small>
-</center>
-
-<p>
-Animation and composition profiles in 3Depict 0.0.13  (Apr, 2013):<br>
-<center>
-<video src="3Depict-0.0.13-sphere_comp_and_animate-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br></small>
-<small><a href="33Depict-0.0.13-sphere_comp_and_animate-screencast.ogg">download video</a></small>
-</center>
-
-
-
-<center>
-<br>
-<br>
-Can't see the videos above? You can download the video using the links above, we recommend the <a href="http://www.videolan.org/vlc/">VideoLan Client</a> player for playing the downloaded files. <br> If you want in-browser playback, you might need to install <a href="http://xiph.org/quicktime/">XiphQT</a> (mac, safari) or <a href="http://xiph.org/dshow/">Xiph Directshow</a> (windows, IE). Alternately, you should be able to view this without downloading any plugins  under the <a href="htt [...]
-</center>
-
-You can view <a href="videos.html">more videos</a> from different program versions.
-</p>
-
-
-
-
-<h2> Sample Datasets </h2>
-<p>
-Some sample datasets are listed below, and whilst not very interesting can be used to understand the program's capabilities. If you have better looking datasets that you are willing to provide, please <a href="contact.html">let us know</a>.
-
-<!-- firefox tries to render the data as a page. Force the mime-type to override this.-->
-<ul>
-
-<li> <a href="samples/bcc-0.288-sim.pos" type="application/octet-stream">Simulated BCC crystal </a></li>
-<li> <a href="samples/BCT theta Al-Cu crystal-sim.pos" type="application/octet-stream">Simulated Al-Cu theta' crystal </a> (with <a href="samples/BCT theta Al-Cu crystal-sim.rng" type="application/octet-stream">range file</a>)</li>
-<li> <a href="samples/spatially-random.pos" type="application/octet-stream">Some spatially random data</a></li>
-<li> <a href="https://cosmicweb.mse.iastate.edu/wiki/display/experiments/RHIT9990">Blast-resistant martensitic steel dataset R06_09990- .pos and .rrng files</a> External website.</li>
-</ul>
-</p>
-
-<h2> Programmer's Reference</h2>
-<h3> Compilation instructions</h3>
-
-For programmers, and advanced users, instructions on how to compile 3Depict from source are available <a href="compiling.html">here</a>. The procedure can be a bit tricky though.
-
-<h3> Program structure</h3>
-3Depict has <a href="doxygen/index.html">documentation</a> regarding the program structure available. This is automatically generated using the <a href="http://www.stack.nl/~dimitri/doxygen/">Doxygen</a> "Doxyfile" in the source tarball. The documentation provides data such as call and inheritance diagrams, hyperlinked function declarations and descriptions, and provides a useful "first glance"  reference for working with the code. For a full program overview, try looking at the <a href= [...]
-</p>
-
-
-<small>
-<center>
-<p> Audio data obtained from <a href="http://commons.wikimedia.org/wiki/File:Pachelbel%27s_Canon.ogg">wikipedia commons</a>, by Lee Galloway, used under the creative commons sharealike 3.0 licence. Video data files are distributed under the same licence.</p>
-</center>
-</small>
-</div>
-</div>
-</body></html>
diff --git a/docs/web/download.html b/docs/web/download.html
deleted file mode 100644
index 18f6646..0000000
--- a/docs/web/download.html
+++ /dev/null
@@ -1,113 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html><head><title>Download 3Depict</title>
-
-    <meta name="description" content="Download 3Depict" />
-
-	<link rel="icon" type="image/png" href="favicon.png"/>
-	<link rel="stylesheet" href="style.css" type="text/css" meda="screen">
-
-</head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-  <li><a href="index.html">Home</a></li>
-  <li><a href="download.html">Download</a></li>
-  <li><a href="questions.html">Questions</a></li>
-  <li><a href="documentation.html">Documentation</a></li>
-  <li><a href="contact.html">Contact</a></li>
-  <li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-	<h2> Latest version: 0.0.16</h2>
-</div>
-
-<div class="box">
-<p>Can't find your platform listed? You can still <a href="compiling.html">build the program from source</a>, if you know
-how</p>
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-
-<div id="content">
-<div id="right">
-<h1>Download</h1>
-<p> Remember that 3Depict is currently in the <i>0.0.blah</i> series (early development). This is alpha software, and provided on a best-effort basis. If you find bugs in the software, please <a href="contact.html">let us know</a> and they will be fixed! Older releases are available in both source and binary (executable) form from our <a href="https://sourceforge.net/projects/threedepict/files/">files page</a>. </p>
-
-<p> Don't forget - support is available on our <a href="https://sourceforge.net/apps/phpbb/threedepict/index.php">forums</a>, or <a href="contact.html">by email!</a></p>
-
-<h2>Select your system:</h2>
-<ul>
-  <li><a href="#Ubuntu">Ubuntu</a></li>
-  <li><a href="#Fedoraredhat">Fedora / RedHat</a><br>
-  </li>
-  <li><a href="#Debian">Debian</a></li>
-  <li><a href="#Mac_OS_X">Mac OS X</a></li>
-  <li><a href="#Windows">Windows</a></li>
-  <li><a href="#Other_systems">Other</a><br>
-  </li>
-</ul>
-<h3><a name="Ubuntu"></a>Ubuntu </h3>
-<ul>
-  <li><a href="apt://3depict">Click to install</a> 
-	<ul>
-	<li>If you want a newer version, you can visit our <a href="https://launchpad.net/~tehuser/+archive/ppa">PPA</a>, or read more about PPAs <a href="https://help.ubuntu.com/community/PPA">here</a>  </li>
-	</ul>
-  </li>
-  
-</ul>
-<h3><a name="Fedoraredhat"></a>Fedora/redhat<br>
-</h3>
-<ul>
-  <li>  Package can be installed securely using "Add/remove programs", or by entering the following command in the terminal: <code>yum install 3Depict</code></li></ul>
-<h3><a name="Debian"></a>Debian</h3>
-<ul>
-  <li>Use your package manger to install, either using a <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Synaptic_%28software%29">Graphical method</a>, or entering the command: <code>aptitude install 3depict</code> as an administrator.</li>
-  
-</ul>
-<h3><a name="Mac_OS_X"></a>Mac OS X  </h3>
-<p><i>The following versions are quite old now. We are looking for help with the OSX version, please see the <a href="https://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=39">forum</a> thread!</i></p>
-<ul>
-      <li>10.7 "Lion" (0.0.13)</li>
-      <ul>
-      <li><a href="https://sourceforge.net/projects/threedepict/files/3Depict-0.0.13-10.7.pkg/download">64 bit Intel</a> <br>
-      </ul>
-    
-    </li>
-  <li>10.6 "Snow Leopard" (0.0.10) </li>
-	<ul>
-		<li><a href="https://sourceforge.net/projects/threedepict/files/3Depict-0.0.10-mac10.6.dmg/download">32 bit Intel</a> <br>
-	</ul>
-  </li>
-  
-</ul>
-<h3><a name="Windows"></a>Windows </h3>
-<small>how to tell if you want the 32 or 64 bit version : <a href="http://windows.microsoft.com/en-US/windows-vista/32-bit-and-64-bit-Windows-frequently-asked-questions">see here</a></small>. If you are still unsure, try the 64 bit version, then if it doesn't work, use the 32 bit one.
-
-
-<ul>
-  <li>0.0.16 :<a href="http://sourceforge.net/projects/threedepict/files/3Depict-0.0.16-win32.exe/download">32 Bit version</a> 
-  </li>
-<li>0.0.16: <a href="http://sourceforge.net/projects/threedepict/files/3Depict-0.0.16-win64.exe/download">64 Bit version</a> 
-  </li>
-</ul>
-
-
-<p>
-
-</p>
-<h3><a name="Other_systems"></a>Other systems</h3>
-<ul>
-  <li>Binaries (executables) not available for your system, sorry. You
-will have to <a href="compiling.html">compile from source</a>. The current compressed source archive is <a href="http://sourceforge.net/projects/threedepict/files/0.0.16/3Depict-0.0.16.tar.gz/download">3Depict 0.0.16</a>.</li> 
-  
-</ul>
-<br>
-</div>
-</div>
-
-</body></html>
diff --git a/docs/web/home.html b/docs/web/home.html
deleted file mode 100644
index 06e5108..0000000
--- a/docs/web/home.html
+++ /dev/null
@@ -1,159 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>3Depict Home</title> 
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-
-</head>
-
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2> Latest version: 0.0.3</h2>
-<p><a href="download.html">Download</a> the latest version of 3Depict for your platform</p>
-</div>
-<div class="box">
-
-<b>Questions?</b> Ask on the <a href="http://sourceforge.net/apps/phpbb/threedepict/">forum</a> 
-<br/>
-</div>
-<center>
-	<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37" width="125" align="middle"/></a><br/>
-	<small><a href="http://sourceforge.net/projects/threedepict/">SF project page</a></small>
-</center>
-</div>
-
-
-<div id="content">
-<div id="right">
-<h1><img src="images/3Depict-icon.png" width="44" height="44" align="middle" alt="program icon"/> 3Depict - Fast and Free</h1>
-<p>
-This software was developed to allow for analysis of scientific
-datasets commonly encountered in <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Atom_probe">atom probe tomography</a>. You can manipulate,
-interact with and analyse point based datasets. <br/>
-</p>
-
-<a href="images/Screenshot.png"><img style="margin: 1em; width: 300px; height: 200px;" alt="Program screenshot" src="images/Screenshot-thumb.png" align="right"/></a>
-
-
-<p></p>
-
-<h3>
-News:
-</h3>
-
-<ul>
-	<li><i>29th Nov. 2010: </i> 3Depict 0.0.3 has been released, so why not <a href="download.html">download it</a>.  Note that state files are not compatible between the 0.0.x series.</li>
-	<li><i>21st Nov. 2010: </i> 3Depict 0.0.3 preview has been uploaded to the sourceforge mercurial repository. If you are keen, you can compile the new version from source to give it a test drive. If you find any bugs, please <a href="contact.html">report them</a></li>
-	<li><i> 25th Sep. 2010: </i>3Depict 0.0.2 has been released. You can <a href="download.html">download it</a> now! </li>
-</ul>
-
-<h3>
-Changes in 0.0.3:
-</h3>
-
-<ul>
-	<li>Added Radial Distribution Function & Nearest neighbour analyses</li>
-	<li>Added on-plot range interaction </li>
-	<li>Add axis aligned box clip mode</li>
-	<li>Plot panning (hold shift key)</li>
-	<li>Add on-screen colourbar for ion colour</li>
-	<li>Add axis drawing</li>
-	<li>Show marker for transform in mass-centre & boundbox mode. Allow user to show or hide marker.</li>
-	<li>Add drag and drop support for file opening</li>
-	<li>Added better error messages to XML file read</li>
-	<li>Add mode to lock cylinder magnitude during rotation</li>
-	<li>Improvements to manual</li>
-</ul>
-
-
-
-<h3>
-Features:
-</h3>
-<ul><li>Graphical interface
-  <ul>
-    <li>Graphical interaction for intuitive and happy analysis.</li>
-  </ul></li>
-<li>Flexible
-  <ul>
-    <li>Customise your analyses using the flexible filtering system.</li>
-  </ul></li>
-<li>Cross platform 
-  <ul>
-    <li>Run anywhere, anytime! No special hardware
-required.</li>
-  </ul></li>
-<li>Free & <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Open_source_software">Open
-Source</a> 
-
-  <ul>
-    <li>No cost to distribute or modify, licensed under the <a href="http://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL 3</a>.
-
-  </li>
-  </ul></li>
-<li>Customisable 
-  <ul>
-    <li>Design your own on-the-fly analysis tools by connecting to external programs.</li>
-  </ul></li>
-</ul>
-
-
-<h3>
-Minimum Requirements:<br/>
-
-</h3>
-<ul><li>Who knows! Every computer I ran this on was fine.</li>
-	<li>I ran this on an <a href="https://secure.wikimedia.org/wikipedia/en/wiki/EEEPC#Eee_900_series">EEEPC
-901</a>(~US$500, 2008) with 2GB RAM running at 800Mhz ("power save" mode). it didn't have any problems.  </li>
-  <li>I even managed to make it work with <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Software_rendering">software
-rendering</a> under a virtual machine</li>
-  <li>Can you run it on anything
-less? Let me know! <br/></li>
-
-</ul>
-<h3>Limitations:</h3>
-
-<ul>
-  <li>General: Currently you can load approximately 2/3 of your system
-RAM's worth of data at any time; 
-
-  <ul>
-    <li>You can load files larger than this, just not all of it at once (subsets or samplings).</li>
-  </ul>
-</li>
-<li>32 bit versions: <br/>
-	<ul>
-		<li>General: You can only access files up to 4GB in size.<br/></li>
-		<li>Windows:  You can only access up to 4GB of system RAM in
-the application, <a href="http://msdn.microsoft.com/en-us/library/aa366778%28VS.85%29.aspx">regardless
-of how much you have </a><br/> 
-		</li>
-	</ul>
-
-  </li>
-</ul>
-
-</div>
-</div>
-
-</body>
-
-</html>
diff --git a/docs/web/images/3Depict-icon.png b/docs/web/images/3Depict-icon.png
deleted file mode 100644
index 650ac31..0000000
Binary files a/docs/web/images/3Depict-icon.png and /dev/null differ
diff --git a/docs/web/images/Screenshot-thumb.png b/docs/web/images/Screenshot-thumb.png
deleted file mode 100644
index c109dcc..0000000
Binary files a/docs/web/images/Screenshot-thumb.png and /dev/null differ
diff --git a/docs/web/images/Screenshot.png b/docs/web/images/Screenshot.png
deleted file mode 100644
index 4fda14a..0000000
Binary files a/docs/web/images/Screenshot.png and /dev/null differ
diff --git a/docs/web/images/cu-ppt-cluster-analysis.png b/docs/web/images/cu-ppt-cluster-analysis.png
deleted file mode 100644
index 7144eaa..0000000
Binary files a/docs/web/images/cu-ppt-cluster-analysis.png and /dev/null differ
diff --git a/docs/web/images/exportanimParamDialog.png b/docs/web/images/exportanimParamDialog.png
deleted file mode 100644
index 29e94d2..0000000
Binary files a/docs/web/images/exportanimParamDialog.png and /dev/null differ
diff --git a/docs/web/images/laser-data1.png b/docs/web/images/laser-data1.png
deleted file mode 100644
index 82dbb0c..0000000
Binary files a/docs/web/images/laser-data1.png and /dev/null differ
diff --git a/docs/web/images/laser-data2.png b/docs/web/images/laser-data2.png
deleted file mode 100644
index b27c76f..0000000
Binary files a/docs/web/images/laser-data2.png and /dev/null differ
diff --git a/docs/web/images/resolution-example.png b/docs/web/images/resolution-example.png
deleted file mode 100644
index 94e2338..0000000
Binary files a/docs/web/images/resolution-example.png and /dev/null differ
diff --git a/docs/web/images/voxel-representations.png b/docs/web/images/voxel-representations.png
deleted file mode 100644
index db4a452..0000000
Binary files a/docs/web/images/voxel-representations.png and /dev/null differ
diff --git a/docs/web/index.html b/docs/web/index.html
deleted file mode 100644
index 465eb32..0000000
--- a/docs/web/index.html
+++ /dev/null
@@ -1,163 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>3Depict Home</title> 
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-
-</head>
-
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2> Latest version: 0.0.16</h2>
-<p><a href="download.html">Download</a> the latest version of 3Depict for your platform</p>
-</div>
-<div class="box">
-
-<b>Questions?</b> Ask on the <a href="http://sourceforge.net/apps/phpbb/threedepict/">forum</a> 
-<br/>
-<br/>
-<b>Quick Peek?</b>: See some <a href="screenshots.html">Screenshots</a>
-
-</div>
-
-<center>
-	<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37" width="125" align="middle"/></a><br/>
-	<small><a href="http://sourceforge.net/projects/threedepict/">SF project page</a></small>
-</center>
-</div>
-
-
-<div id="content">
-<div id="right">
-<h1><img src="images/3Depict-icon.png" width="44" height="44" align="middle" alt="program icon"/> 3Depict - Fast and Free</h1>
-<p>
-This software was developed to allow for analysis of scientific
-datasets commonly encountered in <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Atom_probe">atom probe tomography</a>. You can manipulate,
-interact with and analyse point based datasets. <br/>
-</p>
-
-<a href="images/Screenshot.png"><img style="margin: 1em; width: 300px; height: 200px;" alt="Program screenshot" src="images/Screenshot-thumb.png" align="right"/></a>
-
-
-<p></p>
-
-<h3>
-News:
-</h3>
-<ul>
-	<li><i>24th Apr. 2014: </i> The console-only <i>Posgen</i> project, which covers atom probe data automation, has had its 0.0.1 release! <a href="http://apttools.sourceforge.net/">Check it out</a>!
-	<li><i>21th Apr. 2014: </i> 0.0.16 installers for windows 32 and 64 bit ready for <a href="download.html">download</a>!</li>
-	<li><i>17st Apr. 2014: </i> 3Depict 0.0.16 released. As previously mentioned, we have experimental LAWATAP ato file support, and have improved overall program stability and appearance. Check the complete <a href="changelog.txt">changelog</a>!</li>
-	<li><i>6th Apr. 2014</i> Source code repository has been updated for upcoming 0.0.16. You can see the upcoming features for 3Depict in the source-code repository's <a href="http://sourceforge.net/p/threedepict/code/ci/f03a128c15bc422c6ba5fc38245f77ee1248c19e/tree/ChangeLog">changelog</a>. Notably, experimental ATO file support (please submit samples!) has been added, and many plot area improvements have been made</li>
-
-	<li>Older news <a href="news.html">here</a>,</li>
-</ul>
-
-<h3>
-Enhancements in 0.0.16: 
-</h3>
-
-<ul>
-	<li>Experimental LAWATAP (.ATO) support. We need sample files to ensure this works for everyone, so please send them!</li>
-	<li>Animation is saved/restored</li>
-	<li>Filter tree interaction improved</li>
-	<li>Min-count mode for composition profiles</li>
-	<li>Automated GUI interaction checker</li>
-	<li> and more. Full details in the <a href="changelog.txt">Changelog</a></li>
-</ul>
-
-
-
-
-<h3>
-Features:
-</h3>
-<ul><li>Graphical interface
-  <ul>
-    <li>Graphical interaction for intuitive and happy analysis.</li>
-  </ul></li>
-<li>Flexible
-  <ul>
-    <li>Customise your analyses using the flexible filtering system.</li>
-  </ul></li>
-<li>Cross platform 
-  <ul>
-    <li>Run anywhere, anytime! No special hardware
-required.</li>
-  </ul></li>
-<li>Free & <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Open_source_software">Open
-Source</a> 
-
-  <ul>
-    <li>No cost to distribute or modify, licensed under the <a href="http://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL 3</a>.
-
-  </li>
-  </ul></li>
-<li>Customisable 
-  <ul>
-    <li>Design your own on-the-fly analysis tools by connecting to external programs.</li>
-  </ul></li>
-</ul>
-
-
-<h3>
-Minimum Requirements:<br/>
-
-</h3>
-<ul><li>Who knows! Every computer I ran this on was fine.</li>
-	<li>I ran this on an <a href="https://secure.wikimedia.org/wikipedia/en/wiki/EEEPC#Eee_900_series">EEEPC
-901</a>(~US$500, 2008) with 2GB RAM running at 800Mhz ("power save" mode). it didn't have any problems.  </li>
-  <li>I even managed to make it work with <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Software_rendering">software
-rendering</a> under a virtual machine</li>
-  <li>Can you run it on anything
-less? Let me know! <br/></li>
-
-</ul>
-<h3>Limitations:</h3>
-
-<ul>
-  <li>General: Currently you can load approximately 2/3 of your system
-RAM's worth of data at any time; 
-
-  <ul>
-    <li>You can load files larger than this, just not all of it at once (subsets or samplings).</li>
-  </ul>
-</li>
-<li>32 bit versions: <br/>
-	<ul>
-		<li>General: You can only access files up to 4GB in size.<br/></li>
-		<li>Windows :  You can only access up to 4GB of system RAM in
-the application, <a href="http://msdn.microsoft.com/en-us/library/aa366778%28VS.85%29.aspx">regardless
-of how much you have </a><br/> 
-		</li>
-	</ul>
-
-  </li>
-  <li> Most, but not all state files can be transmitted between differing 3Depict versions</li>
-</ul>
-
-</div>
-</div>
-
-</body>
-
-</html>
diff --git a/docs/web/manual.html b/docs/web/manual.html
deleted file mode 100644
index ab8db9a..0000000
--- a/docs/web/manual.html
+++ /dev/null
@@ -1,304 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>3Depict Home</title> 
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-</head>
-<body>
-<div id="left">
-<div class="box">
-<h2>Read the docs?</h2>
-Read the documentation and still don't know what to do? Ask at the <a href="https://sourceforge.net/apps/phpbb/threedepict/index.php">forums</a>  
-<br/>
- 
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-<div id="content">
-<div id="right">
-<h1>3Depict Documentation</h1>
-
-<h2>Introduction</h2>
-
-<p>
-3Depict is an open source computer program designed for the analysis of point clouds. The program is designed around interactive data analysis, with a view to combine the rapid feedback, ease of use and flexibility in a single system.
-<br/>
-3Depict is designed purely for post-processing of 3D point data, and was originally primarily targeted to users of Atom Probe Tomography. Other users may find the program useful, and are encouraged to seek assistance.
-</p>
-<br/>
-<h3>Background</h3>
-<p>
-3Depict attempts to fill a perceived need for freely available flexible point data visualisation. This program is designed to manipulate and modify point data in a way which the author has otherwise not found a suitable program to do. 
-<br/>
-With this program, point data can be visualised using a fully implemented camera system, edited with directly interactive objects, and subjected to various analysis algorithms. A real-time plotting system is also provided to generate analyses of your data on the fly. External programs can be enguaged as part of the system to create new analyses that "clip into" the analysis.
-</p>
-<br/>
-<h3>What is Open Source?</h3>
-<p>
-Open source programs are programs which distribute not only the executable code, which is understood by the computer (so called machine code), but also provides the version of the program as it was written by humans as well. This provides external users with the possibility of either by themselves, or by engaging a third party. With the source code  one can verify the correctness of the system, alter behaviour or otherwise modify the program, or even reuse sub-sections of the program els [...]
-<br/>
-To provide the user with these capabilities, the program is distributed with a so-called "free" copyright licence. The program is distributed at no cost to the end user, and the copyright attached to the program explicitly allows modification and re-distribution (copying) of the program to other parties, without requiring the author's consent.
-<br/>
-Note that there are restrictions on what may be done with the program, for example it is in violation of the licence to claim ownership of the program, or to use technical measures to prevent access to the program, or modification thereof. The licence used in the program is a generic one shared by many free (as in freedom) software programs
-<br/>
-If you have been charged for this program, it is suggested that you request a refund and obtain a free copy from the main website. If you wish to have the full licence details (GNU General Public Licence Version 3), please see the COPYING file distributed with this program. If this is not available, please see the project website, or perform an internet search for the licence name.
-</p>
-<h3>Who wrote this program?</h3>
-<p>
-This program was writen by D. Haley, in his spare time. A. Ceguerra provided assistance with debugging and fixing the Macintosh version, and providing executable versions of the program for OSX.
-</p>
-<h3>Getting help</h3>
-<p>
-Assistance with this program may be freely obtained over the internet at http://threedepict.sourceforge.net. Questions regarding use of the program, feature or bug reports will be attended to as soon as possible.
-</p>
-<h3>Alternate documentation</h3>
-<p>
-For the more visually inclined, screencasts of the program have been created, and are available on the project website.
-</p>
-<h2> Getting started </h2>
-<h3> Requirements</h3>
-<p>
-The minimum requirements for running 3Depict are not known. The author wrote a substantial portion of the program on a machine with a 4 and 12 Gigabyte drives, and a 1.6GHz processor, which normally runs at 800MHz and 1 GB of ram.  
-<br/>
-Whilst a newer machine may run the program faster, intelligent use of the filter system may allow for complex analyses even on low-end machines. Every effort is expended by the author to ensure that the program can be run on as many devices as possible; if your platform is not supported, it may be possible for either you, or the author to generate executables for your system. See the section "Getting Help" for contact details.
-<br/>
-If you are experiencing video card problems, first ensure that other 3D programs do not experience the same problems. Otherwise, please contact the author for assistance -- there should be no requirement for vendor-specific hardware.
-</p>
-<h3>Platform specific notes</h3>
-Note that whilst every effort is made to ensure that the program will run on a variety of systems, small system-specific quirks may be evident, particularly on platforms to which the authors do not use regularly (e.g. windows). Secondly, due to slight differences between platforms some functions may be remapped to other mouse/key combinations.
-<br/>
-Linux: No notes
-Mac: CTRL keys may sometimes be mapped to the Command key ('⌘' symbol)
-Windows: A current outstanding bug is a visible "flicker" when interacting with the plot view.
-<br/>
-<h3>Licence</h3>
-This program is distributed under the GNU General Public Licence Version 3 (GPLV3). Information on the copyright of this program is available under the COPYING file in the program directory. The following pre-amble is included here:
-<br/>
-<br/>
-<h3>Installing the program</h3>
-The installation method for the program depends upon your chosen operating system.  The most-up-to-date notes are available on the project website.
-<br/>
-<h2>Understanding the interface</h2>
-<p>
-The program interface consists of three different views. On the left, there is the data, cameras and tools panels, with are used to generate data for visualisation, and to provide an interface into changing properties in a structured manner. On the right, the view is split into two sections; at the top, there is the 3D view. At the bottom are the plotting, raw data and console output panels. 
-<br/>
-Each panel may be hidden, either by double clicking the "sash" between the two windows, by selecting the respective item from the view menu or by its keyboard shortcut key.
-<br/>
-At the very bottom of the program, a status bar is shown -- here messages are shown to provide hints on how to use the program, or to communicate information relating to the program's internal state.
-</p>
-<h3>The 3D View</h3>
-<p>
-The 3D view is used to show the three-dimensional objects generated during a data analysis, and provides a direct method of interaction with the 3D Scene. Through the use of the mouse (or other pointing device), the 3D view can be manipulated to change the view position and orientations. Some objects in the 3D view are interactive, and will be indicated by an overlay in the top right of the window when the pointer is on top of such an object.
-</p>
-<h4>Basic movement</h4>
-<p>
-The 3D view represents your camera into a 3D scene of your construction; it is by manipulation of cameras that the view is interacted with; so you may zoom, orbit, pan, roll or swivel the camera view. If you are lost at any time, you may reset the view by tapping the space bar. To change the axis along which the view is reset, hold the CTRL or SHIFT buttons whilst resetting.
-<br/>
-The basic 3D view consists of a "target" based camera, so when you move the camera, the camera will orbit around this target. To interact with a scene, hold down the left mouse button and move the mouse to control the camera.
-<br/>
-The basic keys for controlling the camera move mode are :
-<br/>
-<ul>
-<li> NONE: Orbit camera </li>
-<li>CTRL: Pan camera </li>
-<li> TAB : Swivel camera (Look about)</li>
-<li> CTRL +TAB : roll camera around viewport centre. Note that the rolling motion is controlled by the position of the mouse click.</li>
-</ul>
-For any motion, the SHIFT key may be used to increase the camera move speed.
-</p>
-<h4>Creating a scene</h4>
-<p>
-Initially the program window will appear blank. To provide a more interesting view, it is necessary to inject data into the program. 
-<br/>
-To do so, select the File menu, and then select using "Open". At time of writing, only two formats are currently supported. Firstly are "POS" files, which consist of X,Y,Z and a value (usually mass-to-charge) records, these are repeated across the file. The technical description of these files is fixed-width-records of IEEE594 32 bit floating point in big-endian (PPC) byte order, totalling 16 bytes for each (X,Y,Z,V) record.
-<br/>
-To load a file, navigate to an existing POS file on your disk. If you do not have a POS file, small example files are available on the project website. 
-<br/>
-Upon selecting the file and then OPEN/OK, the file will be loaded into the viewport. Note that the entire file is not loaded, but rather a random selection of elements in the file. It is recommended that for performing analyses, any analyses  are initially conducted with this small random sub-selection, and then the entire dataset can be loaded when the analysis "tree" is ready.
-</p>
-<h4>The analysis tree</h4>
-<p>
-Loading this file populates a small treeview on the data pane (at the left). This tree is referred to  as the "analysis" tree, and each item in the tree is called a "filter". The tree is responsible for producing the output data in the scene, and a good understanding of the behaviour of this tree is required to extract the maximum benefit from the program. 
-<br/>
-To modify the tree, you may add new or remove existing components of the tree. Changes to the tree may be undone using the "Edit" menu, or with the keyboard shortcut Ctrl-Z.
-<br/>
-Note that with every modification of the tree, the 3D scene and any plots will be recomputed. The time of computation is dependent upon the amount of data that is to be analysed, and can be reduced through sampling or volume restriction methods.
-</p>
-<h4>Basic manipulation</h4>
-<p>
-Initially the tree will be empty, and contain no items. To add a base to the tree, a file must be loaded, which will add a "base" element.
-<br/>
-This will initially be "collapsed" so that only the first element is visible. To expand the tree, either select the symbol to the left of the tree view (+ or >). Clicking this again will collapse this section of the tree. This allows for the viewing of only the component that is actively being worked on
-<br/>
-To delete an item, simply select the item to delete with the mouse, and then use either the "DELETE" or "BACKSPACE" keys on your keyboard. Note that clicking on an already selected item will activate the name edit mode. To exit this mode, press ESCAPE.
-</p>
-<h4>Adding new items</h4>
-<p>
-New items can be added to the tree by selecting the filter to add from the dropdown box immediately above the tree. When selecting a new filter to add; an element in the tree must be selected, where the new filter will be placed. If there is no item selected, an error will be shown in the status bar.
-<br/>
-Once an item is added, the filter tree is thus modified and a recomputation of the scene will occur. Approximate progress on the filter update is visible in the status bar. During an update, only limited interaction with the program is permitted. An update may be cancelled at any time with the ESCAPE key.
-</p>
-<h3>Plots</h3>
-The available plots are listed on the right hand side of the plot view panel. YOu can select the active plot from the list. The ites in the list take their name from the filter from which they originates name (there are exceptions to this rule, i.e. composition profiles). Several plots may be drawn at once by holding down the CTRL key when selecting the plot to draw from the plot list box. 
-<h2>The analysis</h2>
-<h3> Tree view</h3>
-<h4>Understanding the tree</h4>
-<p>
-The tree is a flexible and powerful system for constructing your own analyses, after some use this will become a familiar and readily modifiable system for performing your analyses, however the initial structure of the program may take some getting used to. If you are familiar with programs such as PARAVIEW, you may already be familiar with this concept.
-<br/>
-The filter tree essentially is a system for injection, manipulation and display of the data in the program. The tree becomes an "assembly line" for the view of data in the 3D and plot views. Data may be considered to propagate from the "base" of the tree downwards, with each filter in a direct line somehow modifying the data from above in some way.
-<br/>
-When data reaches the end of the filter tree it is "picked up" by the 3D,plot or console panels, depending upon the nature of the data.
-<br/>
-Each node in the tree may be considered in what is called a "parent-child" relationship. Each element in the tree (except the first) has a "parent", and thus may have their own "child" elements. Each "parent" may, in fact, have many children.
-<br/>
-The basic method for data flow is that a parent gives a copy of the data it has processed to its "children" to modify in some way. Each "child" has its own copy* of the data from the parent, which it modifes. In turn this child then gives a copy of the data to each of its own children. If a filter has no children it then passes the data to either the 3D view, the plot view or the console view, depending upon the data type.
-<br/>
-Using this method, one may create a variety of different analyses; for example, one may wish to subsample data before performing a time-consuming spatial analysis, or one may wish to clip the data to remove unwanted sections before generation of a mass spectrum. The flexibility of the filter system supports this concept.it is not the object that moves, but it is rather yourself
-<br/>
-* - technical note: the "copy" system is at the discretion of each filter. Child filters are given a reference to the parent data which restricts modification of the parent's data by the children; children may or may not duplicate this data, propagate or terminate the reference.
-</p>
-<h4>Editing the tree</h4>
-<p>
-Note that items in the filter tree can be moved. You may move any filter to a new parent, to copy instead of moving, hold down the CTRL (under mac use the APPLE key) whilst moving to duplicate the filter, rather than moving it. 
-<br/>
-You may also rename filters in the tree; The filter name may be used by the filter to generate its output, e.g. spectrum plots will take the plot title from the filter name. 
-</p>
-<h4>Data Types</h4>
-Different data can propagate through the filtering system before it is seen in the 3D view. The currently available types are ion, range and plot types. Although these are used internally by the program, understanding the type system may enable more advanced use of the program. If you are not interested in this, skip to the next section.
-<br/>
-<h5>Ions</h5>
-Each ion represents a point in space, which has a value type associated with the point. For example, one might consider a point in a dataset where positions represent atomic positions, and the value is the measured atomic mass. Ions are grouped together by different filters, and each group may be represented with a unique colour and size.
-<br/>
-<h5>Range</h5>
-This is a special datatype which propagates information through the filter tree. The data represents non-overlapping regions of the value space which are to be tagged as belonging to a certain group. This data type has now actual output into the 3D scene, but can alter the manner in which "downstream" filters process incoming information. For example, if a profile filter is used after a range, it will split up its measurements into a per-tag "range" section.
-<br/>
-<h4>Filters</h4>
-<b>Pos load</b><br/>
-The pos load filter injects ion data into the analysis tree. Ions are loaded from a file by one of several different methods. By default, random data is selected from the file. This filter can be created using the "load"  function from the file menu.
-<br/>
-<i>Properties</i>
-<ul>
-<li> "Ion colour": Colour of the ions from the 3D view.</li>
-<li> "ion size"  : Default size of points in 3D view.</li>
-<li> "filename"  : name of the file to load the data form.</li>
-<li> "Load limit": The maximum quantity of data to load from the file. If set to 0, then the entire file is loaded. Otherwise a random sub-selection of the file is loaded. Note that random selection reduces memory cost, but if it is more than a few percent of the file size, may be slower to load.</li>
-<li>"Enable" :  Disable/enable the filter.</li>
-</ul>
-<br/>
-<b> Downsampling</b><br/>
-<br/>
-<i>Properties</i>
-<ul>
-<li> "Fraction" : Approximate random fraction of the data to load. Must be between [0,1].</li>
-<li> "Max count": The approximate number of ions to load.</li>
-</ul>
-<br/>
-<b> Ranging</b>
-<br/>
-<i>Properties</i>
-<ul>
-<li>Filename: This is the name of the file to use as the range source. So-called ORNL "rng" files, Cameca "env" files and Imago/Cameca "RRNG" files are accepted. For information on the accepted file formats, see the Appendix.</li>
-<br/>
-<li>Each range loaded from the file may be enabled, either at the ion level (groups of ranges) or at the range level. The range values may be altered; however these may not overlap at any time.</li>
-</ul>
-<br/>
-<b> Clipping</b>
-<br/>
-This filter allows for the rejection of data that does not lie within some given boundary. Possible boundaries are plane, sphere and cylinder. For example, if the sphere mode is set, ions within the sphere will be kept and propagated. Ions outside the sphere boundary will be dropped. 
-<i>Properties</i>
-<ul>
-<li> Mode: Select the fundamental primative used to divide the incoming ions into two groups. Sphere, Cylinder and Plane modes are available.</li>
-<br/>
-<li> Invert clip: Reverse the action of the filter, i.e.  swap the definition of "inside" and "outside".</li>
-<br/>
-<li> Various positioning parameters; These can be typed in manually, or set by manipulating the clipping object in the 3D view.</li>
-</ul>
-<b> Spectrum</b><br/>
-This will generate a histogram of the "value" of ions passing through the filter. Note that no output other than the histogram is generated. Plots can be assigned a colour, set to logarithmic or non-logarithmic mode, or restricted to only cover a specific region. The plot title is taken from the filter name, some limited LaTeX is supported (note that the "\" symbol is a special LaTeX command; you may need to use "\\" to represent a single "\" in the title), for example to type "My Spectr [...]
-<br/>
-<br/>
-<b>Profile</b><br/>
-The profile filter conducts a density or "compositional" analysis of a given sub-region of 3D space. The action of the profile filter depends upon whether the incoming ions have been "ranged". If not, then the profile filter generates a density profile of the ions inside a cylindrical volume by count, which is visible in the 3D view.  If the ions have been ranged, then the frequencies are on a per-species basis.
-<i>Properties</i>
-<ul>
-<li> Normalise : The action of this option converts the density into a fractional one. For ranged ions, this is the local composition. For unranged ions this is the relative density.</li>
-</ul>
-<br/>
-<b> Spatial analysis</b>
-<br/>
-This filter conducts spatially oriented data analysis of incoming ions, and reassigns the 'value' component of the ion data. The nature of the reassignment depends upon the selected algorithm and the incoming data itself. Note that the exact values computed by the spatial algorithms may be affected by subsampling; however trends are usually unaffected, provided the number of incoming data elements is sufficiently large.
-<i> Properties</i>
-<ul>
-<br/>
-<li>	Algorithms <ul><li>Local Density: This computes the local density of the ions on either a nearest neighbour, or a fixed distance metric. The density is then assigned as the point value. Note that the number of points to be examined increases rapidly in the fixed distance metric, and may rapidly become untenable. Clipping the volume of data to reduce the time is an option, however surface effects can occur.</li></ul></li>
-</ul>
-<br/>
-<b> External program</b>
-<br/>
-This allows the program to run external commands on the system in order to link into other programs. 
-<br/>
-Note: Saving this will result in the user being prompted to the existance of potentially hazardous elements in the filter tree, and will give the user the option of removing them. If you are presented with this warning you are highly recommended to discard these elments unless you know better.
-<h4>Cameras</h4>
-<p>
-3Depict uses a full perspective camera system, which allows for multiple views to be set up and switched recalled. In fact in the 3Depict program is entirely camera based. Thus don't try to move the object but rather to realise the truth -- it is not the object that moves, but it is rather yourself (your camera). The data itself is in effect, always in the same orientation, and your are placing cameras around the object at different positions.
-<br/>
-To fully understand the camera model, it is necessary to understand the parameters in the camera property tab. One can select the position of the camera, a position that the camera is always looking at (look-at mode), the camera "up" direction, and the field of view.
-<br/>
-With the exception of the field of view, these parameters are dynamically modified when interacting with the 3D scene (see section X). The camera field of view, however requires special mention. The field of view of the camera is the angle that the camera look at. Human vision is around 120*, and is much narrower for suffers of tunnel vision (say, 30*). A bird has a full 360 degree field of view (it can see in all directions without needing to turn its head). By default the camera is set [...]
-</p>
-<h4>Tools panel</h4>
-<p>
-The tools panel offers several options on changes to the way the program operates internally.
-<ul>
-<li> Smooth and Translucent objects: This enables so-called "alpha blending" in the 3D scene, where appropriate which allows for non-opaque objects, and anti-aliased objects. This mode alters the way in which objects are rendered in the 3D scene and is in effect a quality-apperance tradeoff. Most of the time you will probably want it set to ON.  The program may render the 3D scene slightly faster if this is disabled.</li>
-<br/>
-<li> Enable lighting : 3D objects do not look very 3D if you are only seeing them on a 2D screen. Computer graphics works around this by simulating the effect of having a 3D lighting source. This might provide minor performance improvements if disabled, at the cost of clarity of rendering.</li>
-<br/>
-<li>Enable filter caching: This alters the way in which the program processes the filter tree. Normally, the program performs what is known as a depth-first search, and propagates data generated by the program from one filter to the other. Intermediate copies are kept by the filters themeselves to speed up recomputation. However, this strategy has a large downside, which is memory consumption. Disabling this will reduce memory consumption by filters, but will mean that any change to the  [...]
-</ul>
-</p>
-<h4>Stashes</h4>
-<p>Instead of enabling or disabling sections of the tree, the program supports "stashes" as a place to put sections of the analysis tree for later use without using them in the analysis section. To create a "stash", select a section of the filter tree to "stash", then in the "stashed filters" dropdown on the data tab, type the name of the stash you wish to create (this is up to you), and press enter. Once done, a duplication of the subtree specified (ie all the filters below the selected [...]
-<br/>
-To use a stash, select a filter in the tree and then click the dropdown button on the stash combo box, and then select the stash you wish to use. This will place the stash as a child of the selected filter. Note that the stash can be used multiple times. 
-<br/>
-</p>
-<h4>Plots</h4>
-Several plots may be drawn at once by holding down the CTRL key when selecting the plot to draw from the plot list box. 
-<br/>
- 
-<h3>Miscellaneous features</h3>
-<h4>Save</h4>
-The current programs state can be saved to an "XML" state file for later analysis. Note that this file cannot be copied freely between computers, as the data is not kept inside this file, only a reference to the file on your drive. Fortunately, this file can be easily edited by hand using programs such as "wordpad" (windows), "textpad" (mac), or any reasonable text editor (all platforms).
-<br/>
-The program state may be restored by opening an external file. Note that opening an existing program state file will erase your current state. If you wish to merge the two states together into a single analysis, use the "merge" option.
-<br/>
-<h4>Undo</h4>
-The program has an undo feature which can be used to abort the last changes to the filter tree. Note that for memory reasons, the results of the computation are not stored, and will need to be recomputed.
-<br/>
-<h4>Raw Data</h4>
-The raw data pane may be used to obtain the raw XY data used to generate the plots. This can either by copied and pasted, or alternately 
-saved to file.
-<br/>
-<h4>Export Menu</h4>
-Plots, images and ion data  may be exported from the program. The output formats for 3D images is "Portable Network Graphic (PNG)" images; these are supported by almost all image viewers. For plots, you may save in either (Scalable Vector Graphic (SVG)) or "PNG" forms. Note that due to the nature of the SVG files, no resolution is needed, and the image can be reproduced at any scale. Furthermore the SVG can be used later to generate PNG images at the required size for output (I recommend [...]
- 
-Exporting Ion Data can be done in several ways; you may export only the visible ions, or alternately, you may export only a subset (for example one or two ranges) of the data, depending upon the filter that the data originated from. The output format will be in Big-endian "POS" format.
-<br/>
-<h4>Autosave</h4>
-The program will generate an autosave file periodically. If the program crashes, it will look for an autosave file and prompt you to restore it. Note that only the program settings are saved, not the data, so recomputation will be neccesary.
-<br/>
-</div>
-</div>
-</body>
-</html>
-
diff --git a/docs/web/news.html b/docs/web/news.html
deleted file mode 100644
index 7791976..0000000
--- a/docs/web/news.html
+++ /dev/null
@@ -1,98 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>3Depict Home</title> 
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-</head>
-
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-
-
-<div id="content">
-<div id="right">
-<h2> News Archive </h2>
-<p>
-<h3> 2013</h3>
-<ul>
-	<li><i>25th Jan. 2014: </i> <a href="http://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=38">Bugs</a> were introduced in the uploaded versions of 0.0.15, which caused the filter system to function incorrectly. This has been fixed, and the installers updated. If you downloaded 0.0.15 previously, please <a href="download.html">re-download</a> the latest installer (windows: 0.0.15-1).</li>
-	<li><i>1st Dec. 2013: </i> 3Depict 0.0.15 installers for windows 32 and 64 bit <a href="download.html">uploaded</a>. The current windows installers now use multiple CPUs for many filters - go faster!</li>
-	<li><i>1st Dec. 2013: </i> 3Depict 0.0.15 released! New features include a range editor, binomial randomness testing, and enhanced support for ENV and RRNG files. More details in the <a href="changelog.txt">Changelog</a>. Platform builds available soon.</li>
-
-	<li><i>29th Aug. 2013: </i> Some reports of windows versions of 0.0.14 not working for some systems, whilst 0.0.13 works. If this affects you, please <a href="https://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=33">post on the forum</a>.</li>
-	<li><i>21st Jul. 2013: </i> Windows installers <a href="download.html">uploaded</a>.</li>
-	<li><i>20th Jul. 2013: </i> 3Depict 0.0.14 source code uploaded. Various platform releases to follow shortly. </li>
-
-	<li><i>6th Jul. 2013: </i> 3Depict 0.0.14's <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/">source code</a> is now ready for testing. New <a href="http://sourceforge.net/p/threedepict/code/ci/default/tree/ChangeLog">features</a> include slice voxel visualisation, improved rangefile support and performance improvements. Advanced users can <a href="compiling.html">build a copy</a>. We plan to finalise the release in ~3 weeks.</li>
-
-	<li><i>15th Apr. 2013: </i> 3Depict 0.0.13 windows installers <a href="download.html">uploaded</a>.</li>
-	<li><i>13th Apr. 2013: </i> 3Depict 0.0.13 mac OSX 10.7 ("Lion") <a href="download.html">uploaded</a>.</li>
-
-	<li><i>12th Apr. 2013: </i> 3Depict 0.0.13 source code uploaded</li>
-</ul>
-
-<h3> 2012</h3>
-<ul>
-	
-	<li><i>24 Nov. 2012:</i> 0.0.12 released - the source code has been uploaded, with various platform builds to follow shortly. This release focussed on a new feature - filter animation, as well as improved stability and decreased resource consumption. As usual, check the <a href="changelog.txt">Changelog</a> for more info!</i></li>
-	<li><i>29th Oct. 2012:</i> We are finalising the current code for release of 0.0.12 in ~6 weeks. We need people to test the program, do translation and provide feedback - if you can help out, please <a href="contact.html">contact us</a>!</i></li>
-	<li><i>20th Jul. 2012:</i> We have received unconfirmed reports that some users on Windows XP may experience startup problems with 3Depict. If this affects you, please <a href="https://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=13">report it</a>.</i></li>
-	<li><i>17th Jul. 2012:</i> Windows and Mac 10.7 installers uploaded for 3Depict 0.0.11.</i></li>
-	<li><i>16th Jul. 2012:</i> 3Depict 0.0.11 source code uploaded. Installers for various platforms (linux,mac,windows) to follow soon.</i></li>
-	<li><i>28th Jun. 2012:</i> 3Depict 0.0.10 mac 10.6 (snow leopard) installer uploaded.</i></li>
-	<li><i>10th Apr. 2012:</i> 3Depict 0.0.10 mac 10.7 (lion) installer uploaded.</i></li>
-	<li><i>1st Apr. 2012:</i> 3Depict 0.0.10 windows installer uploaded.</li>
-	<li><i>31st Mar. 2012:</i> 3Depict 0.0.10 source code uploaded. Builds for various platforms to follow. This version mostly is directed at bug fixes and improved program structure.</li>
-	<li><i>25th Mar. 2012:</i> We are close to releasing 0.0.10, and we would like to have better native language support - we have added French and Spanish using <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Translation_memory">translation memory</a>, but it still needs human review. If you can check translations in these languages - please consider doing so <a href="https://www.transifex.net/projects/p/3depict/">here</a>.</li>
-	<li><i>22nd Jan. 2012: </i> 0.0.9 Mac OS X 10.7 (Lion) installer <a href="download.html">uploaded</a>. </li>
-</ul>
-<h3> 2011</h3>
-<ul>
-	<li><i>21st Dec. 2011: </i> 0.0.9 Mac OS X 10.6 (Snow Leopard) installer <a href="download.html">uploaded</a>. For Ubuntu users, a <a href="https://help.ubuntu.com/community/PPA">PPA</a> is now <a href="https://launchpad.net/~tehuser/+archive/ppa">available</a>. </li>
-	<li><i>18th Dec. 2011: </i> 0.0.9 windows installer <a href="download.html">uploaded</a>.</li>
-	<li><i>18th Dec. 2011: </i> 3Depict 0.0.9 source <a href="download.html">uploaded</a> -- installers to follow shortly.</li>
-	<li><i>23th Oct. 2011: </i> Windows installer for 0.0.8 <a href="download.html">uploaded.</a></li>
-
-	<li><i>13th Oct. 2011: </i> Mac OS X 10.7 installer and source code for 0.0.8 <a href="download.html">uploaded.</a></li>
-	
-	<li><i>31st Jul. 2011: </i> Windows installer for 0.0.7 <a href="download.html">uploaded.</a></li>
-
-	<li><i>30th Jul. 2011: </i> 3Depict 0.0.7 source uploaded -- installers to be distributed shortly. We have added foreign translation support, so if you would like to <a href="https://www.transifex.net/projects/p/3depict/">translate the program</a> to your local language, please <a href="contact.html">contact us</a>.</li>
-	<li><i>17th Jun. 2011: </i> Problem with win32 installer fixed. If you use windows regularly, and would like to help out, we would appreciate testers.</li>
-	<li><i>21st May. 2011: </i> 3Depict 0.0.6 <a href="download.html">released</a>.</li>
-
-	<li><i>12th May. 2011: </i> Please note that the clustering filter in 0.0.5 (latest) should not be used for quantitative purposes, due to errors in implementation. These errors have been resolved in the source, but will only appear in 0.0.6. <a href="http://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=6">Read more</a> </a></li>
-	<li><i>11th Apr. 2011: </i> 3Depict 0.0.5 Mac OSX build has been re-uploaded to fix <a href="http://sourceforge.net/apps/phpbb/threedepict/viewtopic.php?f=1&t=5&p=13">performance and installer bugs</a></li>
-	<li><i>27th Mar. 2011: </i> 3Depict 0.0.5 <a href="download.html">released</a>.</li>	
-	<li><i>20th Mar. 2011: </i> 3Depict 0.0.5 preview has been uploaded to the <a href="http://sourceforge.net/projects/threedepict/develop">mercurial repository</a> .</li>	
-	<li><i>2nd Feb. 2011: </i> 3Depict 0.0.4 for Mac OSX 10.6 (Snow leopard) uploaded.</li>
-	<li><i>24th Jan. 2011: </i> 3Depict 0.0.4 installers uploaded to the files section. Hop over to the <a href="download.html">downloads page</a> to grab a copy.</li>
-
-	<li><i>16th Jan. 2011: </i> 3Depict 0.0.4 preview has been uploaded to the <a href="http://sourceforge.net/projects/threedepict/develop">sourceforge mercurial repository</a>. If you are keen, you can <a href="compiling.html">compile</a> the new version from source to give it a test drive. If you find any bugs, please <a href="contact.html">report them</a></li>
-</ul>
-<h3> 2010</h3>
-<ul>
-	<li><i>29th Nov. 2010: </i> 3Depict 0.0.3 has been released, so why not <a href="download.html">download it</a>.  Note that state files are not compatible between the 0.0.x series.</li>
-</ul>
-</p>
-</div>
-</div>
-</body>
-</html>
diff --git a/docs/web/questions.html b/docs/web/questions.html
deleted file mode 100644
index 94c3e73..0000000
--- a/docs/web/questions.html
+++ /dev/null
@@ -1,174 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>3Depict Frequent Questions</title> 
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-
-</head>
-
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-  <li><a href="index.html">Home</a></li>
-  <li><a href="download.html">Download</a></li>
-  <li><a href="questions.html">Questions</a></li>
-  <li><a href="documentation.html">Documentation</a></li>
-  <li><a href="contact.html">Contact</a></li>
-  <li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box"><h2>Want to help out?</h2><br/>
-You can help, even without programming; Alternately, if you know how to program <a href="compiling.html">download the source</a>
-<br/>
- 
-</div>
-<center>
-<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=341003&type=2" alt="SourceForge.net Logo" border="0" height="37"/></a>
-</center>
-</div>
-
-<div id="content">
-<div id="right">
-<h1>Questions (and answers)
-</h1>
-<ul>
-  <li>Are there any instruction manuals?</li>
-  <ul>
-    <li>Yes, you can download one from the <a href="documentation.html">documentation
-page.</a> If you are new to 3Depict, but not atom probe, you might want to read the "quick start" section in the manual.
-      </li>
-  </ul>
-</ul>
-<ul>
-  <li>Are there any screencasts?</li>
-  <ul>
-    <li>Yes, you can also find them on the documentation page.</li>
-  </ul>
-</ul>
-<ul>
-  <li>How do I pronounce the name, and where does it come from?</li>
-  <ul>
-	<li>We get this one more than we expected. It's pronounced separately as two words, "three", then the word "depict" (to draw or display), strung together. The name originated from "3D PoInt Cloud Tool". If you have a US accent, it sounds like <a href="questions/En-us-threedepict.ogg">this</a>.</li>
-	</ul>
-  </ul>
-</ul>
-
-<ul>
-  <li>How do I cite this program?</li>
-  <ul>
-    <li>We get this one more than we thought too. Just make sure the URL and the version number of the program you were using is included. Here is a bibtex entry you can modify : <br/><code> @Misc{,<br/>
-  Title                    = {3Depict - Valued point cloud visualisation and analysis},<br/>
-  Note                     = {[Online; Accessed : D-Month-YEAR ]},<br/>
-  Comment                  = {Please use \usepackageurl with latex, to display URL in citation},<br/>
-  Url                      = {http://threedepict.sourceforge.net}<br/>
-} </code> </li>
-  </ul>
-</ul>
-
-<ul>
-  <li>This project is terrible, it doesn't work! What can I do?</li>
-  <ul>
-    <li>Try <a href="contact.html">asking for support</a>; it might be fixable.</li>
-    <li>Report a bug (complain). These will be prioritised to be fixed, if I can reproduce them (get the same problem as you by trying to follow your report). Remember that to solve the problem, I need to make the same problem happen on my computer, and if I can't then I might not be able to fix it.</li>
-  </ul>
-</ul>
-
-<ul>
-  <li>This project is great, can I donate?</li>
-  <ul>
-    <li>Not really. Small donations are not particularly useful, and
-small donations are not going to make me work on this any more than I
-currently do. However, there are several things you <span style="font-style: italic;">can</span> do.</li>
-    <ul>
-      <li><a href="download.html">Use it</a> -- Knowing that people are using the program is good
-motivation to keep working on it.</li>
-    </ul>
-    <ul>
-      <li><a href="contact.html">Email the author</a> to say how this has helped you. If you are
-from an academic background, doing this in a formal manner is helpful
-too.</li>
-
-    </ul>
-    <ul>
-      <li>Upload a nice screenshot online, then send us a link. </li>
-      <li>Report bugs, or </li>
-    </ul>
-    <ul>
-      <li>Work on something that the program needs, such as documentation, artwork,
-<a href="https://www.transifex.net/projects/p/3depict/">language translation</a>, web design or programming.</li>
-      <li>If you need a particular feature, you could fund a project to
-develop it, either independantly, or by contacting the author. This
-could be quite expensive though, and would require some level of
-computational skill and more importantly, familiarity with the code.</li>
-    </ul>
-  </ul>
-</ul>
-<ul>
-  <li>This program doesn't do what I need -- can you still help me?</li>
-  <ul>
-    <li>Maybe.</li>
-    <ul>
-      <li>You can request a feature through the forum or contact form. I do not promise to get these things done, but you might find that it is quite easy to create a change. Worst case scenario is that you lose a few minutes asking.</li>
-    </ul>
-    <ul>
-      <li>I might be able to propose a work around, or suggest alternate, or combinations of tools that can help. </li>
-      <li>If there are modules that might benefit the community, or you are using this for a specific academic need, it might be possible to construct a problem-specific solution</li>
-    </ul>
-    <li>Just remember that there are many, many analysis tools out there. <br/>
-    </li>
-  </ul>
-</ul>
-<ul>
-  <li>How much does this program cost? </li>
-  <ul>
-    <li>Nothing. If you paid for it, ask for your money back, and contact us.</li>
-  </ul>
-</ul>
-<ul>
-  <li>Is the program any good?</li>
-  <ul>
-    <li>I don't know. I'd like to think so. That's not for me to decide.</li>
-  </ul>
-</ul>
-<ul>
-  <li>Why did you write this program?</li>
-  <ul>
-    <li>I needed it to fill some needs that I could not find existing software to suit. I couldn't find a flexible point cloud + value analysis program that fitted my needs, so I built one. Seeing as I needed to do it quickly, free components were the order of the day.</li>
-  </ul>
-</ul>
-
-<ul>
-  <li>I'm keen on doing programming and all that sort of stuff. How can I get started?</li>
-  <ul>
-	<li>Firstly, make sure you are familiar with C++ and compiling projects with multiple source files. Then, the best thing to do is to <a href="compiling.html">compile</a> the latest source code (.tar.gz file). This is by far the easiest under Linux, then Mac, and most difficult under windows, due to dependencies. If needed, you can run a <a href="http://www.virtualbox.org">virtual machine</a>. Now, once done, you can make your changes as you like - there is a section in the manual descri [...]
-  </ul>
-
-</ul>
-
-<ul>
-  <li>My question is not listed here.</li>
-  <ul>
-    <li>Can you put that in the form of a question? </li>
-  </ul>
-</ul>
-<ul>
-  <li>What if my question is not listed here?</li>
-  <ul>
-    <li>Well,
-try to <a href="contact.html">contact the authors</a>. These questions were written in a
-speculative fashion, and are not technically "frequently asked". There are some older questions written up <a href="questions/oldquestions.html">here</a>, for which the answers are now a bit obsolete.</li>
-  </ul>
-</ul>
-</div>
-</div>
-
-</body></html>
diff --git a/docs/web/rss.xml b/docs/web/rss.xml
deleted file mode 100644
index 9c275d8..0000000
--- a/docs/web/rss.xml
+++ /dev/null
@@ -1,144 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<rss  version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
-
-<channel>
-<atom:link  href="http://threedepict.sourceforge.net/rss.xml" rel="self" type="application/rss+xml" />
-	<title>3Depict releases</title>
-	<description>3Depict release data</description>
-	<link>http://threedepict.sourceforge.net/</link>
-	<lastBuildDate>Sun, 01 Dec 2013 00:00:00 +0000 </lastBuildDate>
-	<pubDate>Sun, 01 Dec 2013 00:00:00 +0000 </pubDate>
-
-
-	<item>
-		<title>0.0.16</title>
-		<description>source,win32,win64</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.16/3Depict-0.0.16.tar.gz/download</guid>
-		
-		<pubDate>Sun, 17 Apr 2014 00:00:00 +0000 </pubDate>
-	</item>
-
-
-	<item>
-		<title>0.0.15</title>
-		<description>source,win32,win64,macosx10.7</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.15/3Depict-0.0.15.tar.gz/download</guid>
-		
-		<pubDate>Sun, 01 Dec 2013 00:00:00 +0000 </pubDate>
-	</item>
-
-	<item>
-		<title>0.0.14</title>
-		<description>win32,win64,source</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.14/3Depict-0.0.14.tar.gz/download</guid>
-		
-		<pubDate>Sun, 21 Jul 2013 00:00:00 +0000 </pubDate>
-	</item>
-
-
-	<item>
-		<title>0.0.13</title>
-		<description>win32,win64,macosx10.7,source</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.13/3Depict-0.0.13.tar.gz/download</guid>
-		
-		<pubDate>Fri, 12 Apr 2013 00:00:00 +0000 </pubDate>
-	</item>
-
-
-	<item>
-		<title>0.0.12</title>
-		<description>macosx10.7,source</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.12/3Depict-0.0.12.tar.gz/download</guid>
-		
-		<pubDate>Sat, 24 Nov 2012 00:00:00 +0000 </pubDate>
-	</item>
-
-	<item>
-		<title>0.0.11</title>
-		<description>win32,macosx10.7,source</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.11/3Depict-0.0.11.tar.gz/download</guid>
-		
-		<pubDate>Wed, 18 Jul 2012 00:00:00 +0000 </pubDate>
-	</item>
-
-
-	<item>
-		<title>0.0.10</title>
-		<description>win32,source,macosx10.7,macosx10.6</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.10/3Depict-0.0.10.tar.gz/download</guid>
-		
-		<pubDate>Thu, 28 Jun 2012 00:00:00 +0000 </pubDate>
-	</item>
-
-
-	<item>
-		<title>0.0.9</title>
-		<description>win32,source,macosx10.7,macosx10.6</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.9/3Depict-0.0.9.tar.gz/download</guid>
-		
-		<pubDate>Sun, 18 Dec 2011 00:00:00 +0000 </pubDate>
-	</item>
-
-
-	<item>
-		<title>0.0.8</title>
-		<description>win32,macosx10.7,macosx10.6,source</description>
-		
-		
-		<link>http://threedepict.sourceforge.net/changelog.txt</link>
-		<guid>http://sourceforge.net/projects/threedepict/files/0.0.8/3Depict-0.0.8.tar.gz/download</guid>
-		
-		<pubDate>Thu, 13 Oct 2011 00:00:00 +0000 </pubDate>
-	</item>
-
-	<item>
-		<title>0.0.7</title>
-			<description>win32,macosx10.6,source</description>
-
-
-			<link>http://threedepict.sourceforge.net/changelog.txt</link>
-			<guid>http://sourceforge.net/projects/threedepict/files/0.0.7/3Depict-0.0.7.tar.gz/download</guid>
-
-			<pubDate>Sat, 21 May 2011 00:00:00 +0000 </pubDate>
-	</item>
-
-	<item>
-		<title>0.0.6</title>
-			<description>win32,macosx10.6,source</description>
-
-
-			<link>http://threedepict.sourceforge.net/changelog.txt</link>
-			<guid>http://sourceforge.net/projects/threedepict/files/0.0.6/3Depict-0.0.6.tar.gz/download</guid>
-
-			<pubDate>Sat, 21 May 2011 00:00:00 +0000 </pubDate>
-	</item>
-
-</channel>
-</rss>
-
- 
-
diff --git a/docs/web/screenshots.html b/docs/web/screenshots.html
deleted file mode 100644
index 8f55f8e..0000000
--- a/docs/web/screenshots.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
-	<meta name="description" content="Point cloud visualisation and analysis. Useful for atom probe, lidar, and more sciencey/data-ey things"/>
-
-	<title>3Depict Screenshots </title> 
-	<link rel="stylesheet" href="style.css" type="text/css"/>
-	<link rel="icon" type="image/png" href="favicon.png"/>
-</head>
-<body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-	<li><a href="index.html">Home</a></li>
-	<li><a href="download.html">Download</a></li>
-	<li><a href="questions.html">Questions</a></li>
-	<li><a href="documentation.html">Documentation</a></li>
-	<li><a href="contact.html">Contact</a></li>
-	<li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="left">
-<div class="box">
-<h2>Have a neat image?</h2>
-<p>You should <a href="contact.html">let us know</a>, so we can add it!</p>
-</div>
-</div>
-
-
-<div id="content">
-<div id="right">
-<p>
-<center>
-<h3>Cluster analysis of Cu Precipitates in Steel</h3>
-<img src="images/cu-ppt-cluster-analysis.png"/><br/>
-<small>Courtesy, Dr. P. Styman, <a href="http://www-fim.materials.ox.ac.uk">University of Oxford, Dept. Materials</a></small>
-<br/>
-<br/>
-
-<h3>"Ladybug" robotics project data visualisation</h3>
-<img src="images/laser-data1.png"/>
-<br/>
-<br/>
-<img src="images/laser-data2.png"/>
-<br/>
-<br/>
-<hr width="35%"/>
-<h3>Atom probe data analysis</h3>
-<img src="images/resolution-example.png"/>
-</div>
-</div>
-
-
-
-</center>
-</p>
-</body></html>
diff --git a/docs/web/style.css b/docs/web/style.css
deleted file mode 100644
index 4291e12..0000000
--- a/docs/web/style.css
+++ /dev/null
@@ -1,60 +0,0 @@
-html,body{margin:0;padding:0}
-body{font: 85%/1.3 TrebuchetMS,Arial,sans-serif;
-    text-align: left;background: #fff;padding-bottom:20px}
-a { color: #0066B3; background: inherit; text-decoration: none;}
-h1 { font: bold 2.1em Arial, Arial, Sans-Serif; color: #036DA7 }
-h2 { font: bold 1.5em Arial, Arial, Sans-Serif; }
-h3 { font: bold 1.0em Arial, Arial, Sans-Serif; }
-
-div#header{width:100%;overflow:hidden;background: #BBD9EE; }
-div#header h1,div#menu{width:770px;margin:0 auto;text-align:left}
-div#header h1{padding: 30px 0 20px;color: #fff}
-
-ul#nav,ul#nav li{list-style-type:none;margin:0;padding:0}
-ul#nav{float:right;font-size: 90%}
-ul#nav li{float:left;margin-left: 3px;text-align: center}
-ul#nav a{float:left;width: 110px;padding: 5px 0;background: #E7F1F8;text-decoration:none;color: #666; border-top : 1px solid #fff; border-left : 1px solid #fff; border-right : 1px solid #fff;}
-ul#nav a:hover{background: #fff;color: #000}
-ul#nav li.activelink a,ul#nav li.activelink a:hover{background: #FFF;color: #003}
-
-#right {
-	float: left;
-	width: 76%;
-	padding: 1em;
-	margin-bottom: 1.2em;
-	background: #eee;
-	text-align: justify;
-}
-
-#left {
-	float: right;
-	width: 20%;
-	margin: 0 0 10px 0;
-	
-}
-
-#left .box {
-	
-	padding: 10px;
-	margin: 1em 0 1em 0;
-	background : #FFF6BF;
-}
-
-
-#content {
-font: 100%/1.3 TrebuchetMS,Arial,sans-serif;
-	margin: 0 auto;
-    padding: 15px;
-	background: #fff;
-
-}
-
-#footer {
-   position:absolute;
-   bottom:0;
-   width:100%;
-   height:60px;   /* Height of the footer */
-   background:#fff;
-   padding: 15px;
-}
-	
diff --git a/docs/web/videos.html b/docs/web/videos.html
deleted file mode 100644
index bfe8732..0000000
--- a/docs/web/videos.html
+++ /dev/null
@@ -1,70 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html><head>
-
-  
-  <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"><title>Documentation</title>
-  <meta name="description" content="Documentation for 3Depict" />
-
-  <link rel="icon" type="image/png" href="favicon.png"/>  
-  <link rel="stylesheet" href="style.css" type="text/css" meda="screen"></head><body>
-<div id="header">
-<h1>3Depict - Valued point cloud visualisation and analysis</h1>
-<ul id="nav">
-  <li><a href="index.html">Home</a></li>
-  <li><a href="download.html">Download</a></li>
-  <li><a href="questions.html">Questions</a></li>
-  <li><a href="documentation.html">Documentation</a></li>
-  <li><a href="contact.html">Contact</a></li>
-  <li><a href="about.html">About</a></li>
-</ul>
-</div>
-
-<div id="content">
-<div id="right"><br>
-
-
-A short demo of 3Depict 0.0.8 (Oct, 2011):<br>
-<center>
-<video src="3Depict-0.0.8-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br></small>
-<small><a href="3Depict-0.0.8-screencast.ogg">download video</a></small>
-</center>
-
-<p>
-New in 0.0.4 (Jan, 2011) :<br>
-<center>
-<video src="3Depict-0.0.4-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br>
-<small><a href="3Depict-0.0.4-screencast.ogg">download video</a></small>
-</center>
-
-
-
-New in 0.0.3 (Nov, 2010) :<br>
-<center>
-<video src="3Depict-0.0.3-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br>
-<small><a href="3Depict-0.0.3-screencast.ogg">download video</a></small>
-</center>
-
-
-New in 0.0.2 (Sep, 2010) :<br>
-<center>
-<video src="3Depict-0.0.2-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br>
-<small><a href="3Depict-0.0.2-screencast.ogg">download video</a></small>
-</center>
-<p>
-
-A short demo of 3Depict 0.0.1 (Aug, 2010):<br>
-<center>
-<video src="3Depict-0.0.1-screencast.ogg" controls="controls" type='video/ogg; codecs="theora, vorbis"'></video><br><small>
-<a href="3Depict-0.0.1-screencast.ogg">download video</a>
-</center>
-
-<center>
-<br>
-<br>
-Can't see the videos above? You can download the video using the links above, we recommend the <a href="http://www.videolan.org/vlc/">VideoLan Client</a> player for playing the downloaded files. <br> If you want in-browser playback, you might need to install <a href="http://xiph.org/quicktime/">XiphQT</a> (mac, safari) or <a href="http://xiph.org/dshow/">Xiph Directshow</a> (windows, IE). Alternately, you should be able to view this without downloading any plugins  under the <a href="htt [...]
-</center>
-
-</div>
-</div>
-</body>
-</html>
diff --git a/locales/de_DE/LC_MESSAGES/3Depict.mo b/locales/de_DE/LC_MESSAGES/3Depict.mo
index 36291f1..c31b1ce 100644
Binary files a/locales/de_DE/LC_MESSAGES/3Depict.mo and b/locales/de_DE/LC_MESSAGES/3Depict.mo differ
diff --git a/m4/freetype2.m4 b/m4/freetype2.m4
old mode 100755
new mode 100644
diff --git a/m4/ftgl.m4 b/m4/ftgl.m4
old mode 100755
new mode 100644
diff --git a/m4/gl.m4 b/m4/gl.m4
old mode 100755
new mode 100644
diff --git a/m4/gsl.m4 b/m4/gsl.m4
old mode 100755
new mode 100644
diff --git a/m4/libxml2.m4 b/m4/libxml2.m4
old mode 100755
new mode 100644
diff --git a/m4/opengl.m4 b/m4/opengl.m4
old mode 100755
new mode 100644
diff --git a/m4/wxwin.m4 b/m4/wxwin.m4
old mode 100755
new mode 100644
diff --git a/missing b/missing
old mode 100644
new mode 100755
diff --git a/packaging/3Depict.desktop b/packaging/3Depict.desktop
old mode 100755
new mode 100644
diff --git a/packaging/RPM/3Depict.spec b/packaging/RPM/3Depict.spec
index 8d2780b..53080f2 100644
--- a/packaging/RPM/3Depict.spec
+++ b/packaging/RPM/3Depict.spec
@@ -1,5 +1,5 @@
 Name:		3Depict
-Version:	0.0.17
+Version:	0.0.18
 Release:	1%{?dist}
 Summary:	Valued 3D point cloud visualization and analysis
 Group:		Applications/Engineering
@@ -26,6 +26,9 @@ BuildRequires: libpng-devel
 BuildRequires: desktop-file-utils
 #WX widgets
 BuildRequires: wxGTK-devel
+#Vigra, for voxelisation
+BuildRequires: vigra-devel
+
 #PDF latex build
 #BuildRequires: tex(latex)
 
@@ -110,7 +113,13 @@ rm -rf %{buildroot}
 
 
 %changelog
-* Sat Aug 02 2014 D Haley <mycae(a!t)gmx.com> - 0.0.17-1
+* Tue Apr 21 2015 D Haley <mycae(a!t)gmx.com> - 0.0.18-1
+- Update to 0.0.18
+
+* Sat Oct 11 2014 D Haley <mycae(a!t)gmx.com> - 0.0.17-2
+- Rebuild for mathgl 2.3
+
+* Sun Sep 28 2014 D Haley <mycae(a!t)gmx.com> - 0.0.17-1
 - Update to 0.0.17
 
 * Fri Jun 06 2014 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 0.0.16-2
diff --git a/packaging/debian/3depict.1 b/packaging/debian/3depict.1
old mode 100644
new mode 100755
diff --git a/packaging/debian/changelog b/packaging/debian/changelog
index b44a057..83cb3c2 100644
--- a/packaging/debian/changelog
+++ b/packaging/debian/changelog
@@ -1,8 +1,22 @@
-3depict (0.0.17-1) UNRELEASED; urgency=medium
+3depict (0.0.18-1) unstable; urgency=medium
 
   * Update to upstream 0.0.17
 
- -- D Haley <mycae at gmx.com>  Sun, 03 Aug 2014 23:11:45 +0100
+ -- D Haley <mycae at gmx.com>  Sun, 26 Apr 2014 12:00:00 +0100
+
+3depict (0.0.17-1) unstable; urgency=medium
+
+  * Update to upstream 0.0.17
+
+ -- D Haley <mycae at gmx.com>  Sun, 28 Sep 2014 16:07:00 +0100
+
+3depict (0.0.16-2.1) unstable; urgency=medium
+
+  * Non-maintainer upload at maintainer's request.
+  * Update BD on libwxgtk2.8-dev to libwxgtk3.0-dev | libwxgtk2.8-dev.
+    (Really Closes: #746609)
+
+ -- Olly Betts <olly at survex.com>  Fri, 25 Jul 2014 10:55:36 +1200
 
 3depict (0.0.16-2) unstable; urgency=medium
 
diff --git a/packaging/debian/compat b/packaging/debian/compat
old mode 100644
new mode 100755
diff --git a/packaging/debian/docs b/packaging/debian/docs
old mode 100644
new mode 100755
diff --git a/packaging/debian/manpages b/packaging/debian/manpages
old mode 100644
new mode 100755
diff --git a/packaging/debian/menu b/packaging/debian/menu
old mode 100644
new mode 100755
diff --git a/packaging/debian/source/format b/packaging/debian/source/format
old mode 100644
new mode 100755
diff --git a/packaging/debian/watch b/packaging/debian/watch
old mode 100644
new mode 100755
diff --git a/packaging/deps/getDeps b/packaging/deps/getDeps
index aa6866a..87428f3 100755
--- a/packaging/deps/getDeps
+++ b/packaging/deps/getDeps
@@ -45,14 +45,8 @@ function setFullDeps {
 }
 
 function setMacDeps {
+	# All deps are available via macports
 	OS_VERSION=`sw_vers -productVersion |sed 's/\.[0-9]*$//'`
-	#Dependency ID, URL and name
-	DEPIDS=( 0)
-	DEPNAMES=( mathgl)
-	DEPURLS=( http://sourceforge.net/projects/mathgl/files/mathgl/mathgl%202.2.2/mathgl-2.2.2.1.tar.gz/download)
-	DEPFILENAMES=( mathgl-2.2.2.1.tar.gz)
-	DEPMDSUM=( cdee2784ce2f18ab9014bbd204b8589f)
-	
 }
 
 function installMacPorts {
@@ -71,7 +65,7 @@ function installMacPorts {
 			curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.0.3-10.7-Lion.dmg
 			hdiutil attach MacPorts-2.0.3-10.7-Lion.dmg
 		else
-			echo "Mac OS X $OS_VERSION  not supported."
+			echo "Mac OS X $OS_VERSION  not supported  by this script. You'll need to install dependencies manually."
 			exit 1
 		fi
 		if [ $? -ne 0 ] ; then
@@ -196,8 +190,8 @@ function handleMacDistro()
 
 
 	# install deps except mathgl
-	echo "installing 3Depict dependencies..."
-	sudo port install wget libpng freetype ftgl wxWidgets-3.0 dylibbundler qhull gsl  cmake libtool pkgconfig
+	echo "installing some 3Depict dependencies..."
+	sudo port install wget libpng freetype ftgl wxWidgets-3.0 dylibbundler qhull gsl  cmake libtool pkgconfig vigra mathgl
 	if [ $? -ne 0 ] ; then
 		echo "macports was not successful in getting the dependencies"
 		exit 1
@@ -257,7 +251,9 @@ function handleLinuxDistro()
 			echo "System appears to be Debian-esque (debian/ubuntu/mint...)."
 			echo "Running apt-get:"
 
-			sudo apt-get build-dep 3depict && sudo apt-get install make g++
+
+			#TODO: Remove libvigra dep. This will eventually roll into build-dep for 3depict
+			sudo apt-get build-dep 3depict && sudo apt-get install make g++ && sudo apt-get install libvigraimpex-dev
 			if [ $? -ne 0 ] ; then
 				echo "Failed to install build-dependencies"
 				exit 1
@@ -371,6 +367,8 @@ function handleLinuxDistro()
 			echo " An RPM \"Spec\" file has been left in ~/rpmbuild/SPECS/"
 			echo " go there, tweak that file and use \"rpmbuild -ba mathgl.spec\" to build it."
 			echo " This is not straightforwards - work is needed to make centos and mathgl play nice"
+
+			echo " libvigra has not been intalled on your system. Please install it before compiling 3depict"
 			exit 0;
 		;;
 		SuseLike)
@@ -389,7 +387,7 @@ function handleLinuxDistro()
 			echo "Are you sure you need to run this?? "
 			echo "You are running some unrecognised version of linux "
 			echo "you should use your package manager to install these "
-			echo "libraries : ${dep_names[*]}"
+			echo "libraries : ${DEPNAMES[*]}"
 			echo
 			echo "Refusing to continue, otherwise script could break your system..."
 			echo "--------"
diff --git a/packaging/deps/sources/mgl_data_png.patch b/packaging/deps/sources/mgl_data_png.patch
old mode 100755
new mode 100644
diff --git a/packaging/deps/sources/mgl_eps.patch b/packaging/deps/sources/mgl_eps.patch
old mode 100755
new mode 100644
diff --git a/packaging/deps/sources/mgl_export.patch b/packaging/deps/sources/mgl_export.patch
old mode 100755
new mode 100644
diff --git a/packaging/howToRelease.txt b/packaging/howToRelease.txt
new file mode 100644
index 0000000..4eb081a
--- /dev/null
+++ b/packaging/howToRelease.txt
@@ -0,0 +1,47 @@
+== HOW TO PREP A RELEASE - A ROUGH GUIDE ==
+
+-- Pre-tarball tasks --
+* Ensure basic compilation works.
+* Ensure basic unit tests work, using ubsan/valgrind.
+* Run cppcheck, fixing any major issues (extras/cppcheck.sh)
+* Run coverity, fixing any major issues (extras/coverity.sh)
+* Review any outstanding bug reports
+* Update Changelog
+* Run "makeTranslations update" from translations dir to rebuild translation database
+	- remove any dead translations
+* Merge code into "threedepict" sf repository, then work from that repo
+* Copy, or link, makeTarball.sh into base 3depict dir
+* Run makeTarball.sh, fixing any error messages - this may take a few runs
+	- may need to build PDFs
+* Tarball will now be in tarball/3Depict-version.tar.gz
+* Update RPMs
+* Update DEBs
+* Optionally, ensure mac compilation works
+* Ensure windows compilation works
+	- suggest using mingw cross comp. dir
+* Merge updated RPM/ DEB and windows installer files and any source changes
+------------------------ 
+
+-- Tarball generation --
+* Re-run makeTarball.sh, fixing any new errors
+------------------------
+
+-- Installer uploading --
+* Convert Changelog to dos format, and rename Changelog.txt
+* Upload Changelog
+* Upload PDF manual
+* Rebuild doxygen documentation & upload
+* Upload new tarball -> sourceforge website
+* Update news, download pages to point to new tarball 
+	- maybe other pages? 
+* Build windows installers (64 and 32 bit)
+	- test windows installers under wine (quick but inaccurate), then under real windows
+* Upload windows installers
+	- update RSS file
+* Update DEB in debian-science repo
+	- ensure lintian clean, and request upload
+* Update RPM in fedora-updates
+	- ensure rpmlint clean, then upload to testing.
+* Update PPAs in Ubuntu-linux, where possible
+* Update download page to point to new installer
+-------------------------
diff --git a/packaging/mac/2compile.sh b/packaging/mac/2compile.sh
old mode 100644
new mode 100755
diff --git a/packaging/makeTarball.sh b/packaging/makeTarball.sh
index 6d58c6f..cca0bff 100755
--- a/packaging/makeTarball.sh
+++ b/packaging/makeTarball.sh
@@ -1,7 +1,14 @@
 #!/bin/bash
 MSG_FILE=tmp-messages
 
-NUM_PROCS=4
+if [ x`uname | grep Linux` != x"" ] ; then
+	NUM_PROCS=`cat /proc/cpuinfo | grep cores | uniq | sed 's/.*:\s*//'`
+else
+	#For other platforms, guess!
+	NUM_PROCS=4
+fi
+
+
 
 
 if [ ! -f configure ] ; then
@@ -19,43 +26,49 @@ done
 #	- enable/disable parallel 
 #	- debug checking
 #	- C++11
+#	- ubsan
 #-------
-CONF_ARGS=" --enable-openmp-parallel | --disable-debug-checks | --enable-debug-checks | --enable-openmp-parallel --disable-debug-checks | --enable-c++11 --enable-openmp-parallel | --enable-c++11 --disable-debug-checks"
-
-OLD_IFS=$IFS
-IFS="|"
-for i in ${CONF_ARGS[*]}
-do
-	echo "$i"
-
-	make distclean
-
-	./configure
-	if [ $? -ne 0 ] ; then
-		echo "test-configuration failed to configure: arguments are $i"
-	fi
-
-	make -j $NUM_PROCS
-	if [ $? -ne 0 ] ; then
-		echo "failed to build: argumens are $i"
-	fi
-
-	#Check for unit test availability, and run them
-	# where possible
-	pushd src
-	TEST_FLAG=`./3Depict --help  2>&1 | grep "\-\-test"`
-	if [ x"$TEST_FLAG" != x"" ] ; then
-		./3Depict -t
-		if [ $? -ne 0 ] ; then
-			echo "Unit tests failed for configure flag : $i" 
-			exit 1
-		fi
-	fi
-	popd	
-      
-	make distclean
-done
-IFS=$OLD_IFS
+#CONF_ARGS=("--enable-openmp-parallel" "--disable-debug-checks " " --enable-debug-checks " " --enable-openmp-parallel --disable-debug-checks " " --enable-ubsan --enable-openmp-parallel " " --enable-ubsan "  )
+#
+#for i in ${CONF_ARGS[*]}
+#do
+#	if [ -f Makefile ] ; then
+#		make distclean
+#	fi
+#
+#	./configure "$i"
+#
+#	if [ $? -ne 0 ] ; then
+#		echo "test-configuration failed to configure: arguments are $i"
+#		exit 1
+#	fi
+#
+#	if [ ! -f Makefile ] ; then
+#		echo "Configure claimed everything was OK, but did not create a Makefile"
+#		exit 1
+#	fi
+#
+#	make -j $NUM_PROCS
+#	if [ $? -ne 0 ] ; then
+#		echo "failed to build: arguments are $i"
+#		exit 1
+#	fi
+#
+#	#Check for unit test availability, and run them
+#	# where possible
+#	pushd src
+#	TEST_FLAG=`./3Depict --help  2>&1 | grep "\-\-test"`
+#	if [ x"$TEST_FLAG" != x"" ] ; then
+#		./3Depict -t
+##		if [ $? -ne 0 ] ; then
+##			echo "Unit tests failed for configure flag : $i" 
+###			exit 1
+##		fi
+#	fi
+#	popd	
+#      
+#	make distclean
+#done
 #-------
 
 ./configure
@@ -181,7 +194,7 @@ fi
 
 #Run licensecheck over files, if available
 if [ x`which licensecheck` != x"" ] ; then
-	LIC_CHECK_OUT=`licensecheck -r  --ignore=".*\.(sh|sci|py|tex)$" . | grep -v "GPL (v[23] or later)" | grep -v "GENERATED FILE" | grep -v "MPL"  | grep -v "tarball/"`
+	LIC_CHECK_OUT=`licensecheck -r  --ignore=".*\.(sh|sci|py|tex|js)$" . | grep -v "GPL (v[23] or later)" | grep -v "GENERATED FILE" | grep -v "MPL"  | grep -v "tarball/"`
 
 	if [ x"$LIC_CHECK_OUT" != x"" ] ; then
 		echo "WARNING:" $LIC_CHECK_OUT >> $MSG_FILE
diff --git a/packaging/manpage/3Depict.1 b/packaging/manpage/3Depict.1
old mode 100755
new mode 100644
diff --git a/packaging/mingw-debian-cross/bootstrap.sh b/packaging/mingw-debian-cross/bootstrap.sh
index 1529e17..bfbe5c9 100755
--- a/packaging/mingw-debian-cross/bootstrap.sh
+++ b/packaging/mingw-debian-cross/bootstrap.sh
@@ -64,7 +64,7 @@ BASE=`pwd`
 PREFIX=/
 NUM_PROCS=4
 
-IS_RELEASE=1
+IS_RELEASE=0
 
 if [ `id -u` -eq 0 ]; then
 	echo "This script should not be run as root."
@@ -72,7 +72,7 @@ if [ `id -u` -eq 0 ]; then
 	exit 1;
 fi
 #2) own patch for fixing wx-config's lack of sysroot support
-PATCHES_WXWIDGETS_PRE=""
+PATCHES_WXWIDGETS_PRE="wx_changeset_76890.diff"
 PATCHES_WXWIDGETS_POST="wx-config-sysroot.patch"
 #1) Zlib no longer needs to explicitly link libc, and will fail if it tries
 PATCHES_ZLIB="zlib-no-lc.patch"
@@ -83,12 +83,11 @@ PATCHES_FTGL_POSTCONF="ftgl-override-configure-2"
 #2) gettext fails to correctly determine windows function call prefix.
 #   should be fixed for gettext > 0.18.1.1 ?
 #   https://lists.gnu.org/archive/html/bug-gettext/2012-12/msg00071.html
-PATCHES_GETTEXT="gettext-disable-tools"    #gettext-win32-prefix
+PATCHES_GETTEXT="gettext-disable-tools gettext-fix-configure-versions"    #gettext-win32-prefix
 
 PATCHES_GLEW="glew-makefile.base"
 
-PATCHES_MATHGL=""
-
+PATCHES_MATHGL="mathgl-openmp-linker-flag"
 PATCH_LIST="$PATCHES_WXWIDGETS_PRE $PATCHES_WXWIDGETS_POST $PATCHES_GSL $PATCHES_ZLIB $PATCHES_LIBPNG $PATCHES_GETTEXT $PATCHES_FTGL $PATCHES_GLEW $PATCHES_MATHGL $PATCHES_FTGL_POSTCONF"
 
 BUILD_STATUS_FILE="$BASE/build-status"
@@ -150,12 +149,13 @@ function grabDeps()
 {
 	pushd deps 2>/dev/null
 
-	DEB_PACKAGES="expat freetype ftgl gettext gsl libpng libxml2 mathgl qhull tiff wxwidgets3.0 zlib glew"
-	if [ x$DIST_NAME == x"Ubuntu" ] || [ x$DIST_NAME == x"LinuxMint" ] ; then 
-		LIBJPEGNAME="libjpeg6b"
+	DEB_PACKAGES="expat freetype ftgl gettext gsl libpng libxml2 mathgl qhull tiff wxwidgets3.0 zlib glew libvigraimpex"
+	if [ x$DIST_NAME == x"Ubuntu" ] || [ x$DIST_NAME == x"LinuxMint" ]  ; then 
+       		LIBJPEGNAME="libjpeg6b"
 	else
-		LIBJPEGNAME="libjpeg8"
-	
+		#Libjpeg seems to be forked/renamed very frequently in debian
+		# Likely a new libjpeg will need to be picked each time this script is run
+		LIBJPEGNAME="libjpeg-turbo"
 	fi
 	DEB_PACKAGES="$DEB_PACKAGES $LIBJPEGNAME"
 
@@ -181,6 +181,8 @@ function grabDeps()
 			exit 1
 		fi
 
+		#Strip patches from the build and patch status files, 
+		# if we are retriving new packages
 		for i in $GET_PACKAGES
 		do
 			grep -v $i ../build-status > tmp
@@ -648,11 +650,12 @@ function build_expat()
 		echo "expat dir missing, or duplicated?"
 		exit 1
 	fi
-	./configure --host=$HOST_VAL --enable-shared --disable-static --prefix=/ || { echo "Libtiff configure failed"; exit 1; } 
 
-	make -j $NUM_PROCS || { echo "expat build failed"; exit 1; } 
+	./configure --host=$HOST_VAL --enable-shared --disable-static --prefix=/ || { echo "$NAME configure failed"; exit 1; } 
+
+	make -j $NUM_PROCS || { echo "$NAME build failed"; exit 1; } 
 	
-	make install DESTDIR="$BASE"|| { echo "expat install failed"; exit 1; } 
+	make install DESTDIR="$BASE"|| { echo "$NAME install failed"; exit 1; } 
 
 	#DLL needs to be copied into lib manually
 	cp -p .libs/${NAME}-[0-9]*.dll $BASE/lib/ 
@@ -731,7 +734,7 @@ function build_wx()
 
 	APPLY_PATCH_ARG=$PATCHES_WXWIDGETS_PRE
 	applyPatches
-#WX_DISABLE="--disable-compat26 --disable-ole --disable-dataobj --disable-ipc --disable-apple_ieee --disable-zipstream --disable-protocol_ftp --disable-mshtmlhelp --disable-aui --disable-mdi --disable-postscript --disable-datepick --disable-splash --disable-wizarddlg --disable-joystick --disable-loggui --disable-debug --disable-logwin --disable-logdlg --disable-tarstream --disable-fs_archive --disable-fs_inet --disable-fs_zip --disable-snglinst --disable-sound --disable-variant --without-regex"
+	WX_DISABLE="--disable-compat26 --disable-compat28 --disable-ole --disable-dataobj --disable-ipc --disable-apple_ieee --disable-zipstream --disable-protocol_ftp --disable-mshtmlhelp --disable-aui --disable-mdi --disable-postscript --disable-datepick --disable-splash --disable-wizarddlg --disable-joystick --disable-loggui --disable-debug --disable-logwin --disable-logdlg --disable-tarstream --disable-fs_archive --disable-fs_inet --disable-fs_zip --disable-snglinst --disable-sound --disabl [...]
 
 	./configure --host=$HOST_VAL --enable-shared --disable-static --with-opengl --enable-unicode --without-regex --prefix=/ || { echo "wxwidgets configure failed"; exit 1; } 
 
@@ -871,7 +874,7 @@ function build_gettext()
 	pushd gettext-* >/dev/null
 		
 	if [ $? -ne 0 ] ; then
-		echo "gettext dir missing, or duplicated?"
+		echo "$NAME dir missing, or duplicated?"
 		exit 1
 	fi
 
@@ -881,12 +884,16 @@ function build_gettext()
 	applyPatches
 	automake
 
-	./configure --host=$HOST_VAL --disable-threads --enable-shared --disable-static --prefix=/ || { echo "gettext configure failed"; exit 1; } 
+	./configure --host=$HOST_VAL --disable-threads --enable-shared --disable-static --prefix=/ || { echo "$NAME configure failed"; exit 1; } 
 
-	make -j $NUM_PROCS || { echo "gettext build failed"; exit 1; } 
+	make -j $NUM_PROCS || { echo "$NAME build failed"; exit 1; } 
 	
-	make install DESTDIR="$BASE"|| { echo "gettext install failed"; exit 1; } 
+	make install DESTDIR="$BASE"|| { echo "$NAME install failed"; exit 1; } 
 
+	#FIXME: I had to copy the .lib, .la and .a files manually
+	# I don't know why the makefile does not do this.
+	cp gettext-runtime/intl/.libs/libintl.{la,lib,a} ${BASE}/lib/ || {  echo "semi-manual copy of libintl failed"; exit 1; } 
+	
 	popd >/dev/null
 	popd >/dev/null
 	
@@ -918,16 +925,12 @@ function build_mathgl()
 	APPLY_PATCH_ARG=$PATCHES_MATHGL
 	applyPatches
 
-	#Strip invalid linker flags from cmake's link instructions
-	rm CMakeCache.txt
-	#Strip invalid linker flags from cmake's link instructions
-	find ./ -name link.txt -exec sed -i 's/-Wl,-z,relro//'  {} \;
-	cmake -DCMAKE_TOOLCHAIN_FILE=../../patches/cmake-toolchain$BITS_VAL
-	#Strip invalid linker flags from cmake's link instructions
-	find ./ -name link.txt -exec sed -i 's/-Wl,-z,relro//'  {} \;
-
+	if [ -d $BASEDIR/include/mgl2 ] ; then
+		echo "there are mgl2 headers already installed. Abort abort!"\
+		exit 1
+	fi
 
-	cmake -DCMAKE_TOOLCHAIN_FILE=../../patches/cmake-toolchain$BITS_VAL
+	LIBS=-lpng cmake -Denable-gsl="yes" -Denable-mpi="no"  -DCMAKE_INSTALL_PREFIX="$BASE" -DCMAKE_TOOLCHAIN_FILE=../../patches/cmake-toolchain$BITS_VAL -DPNG_PNG_INCLUDE_DIR=${BASEDIR}/include/
 
 	make -j $NUM_PROCS
 
@@ -936,9 +939,8 @@ function build_mathgl()
 		exit 1
 	fi
 		
-	cp -p .libs/${NAME}-[0-9]*.dll $BASE/lib/ 
+	make install
 	
-	cp -R include/mgl2 ${BASE}/include
 	ln -s ${BASE}/include/mgl2 ${BASE}/include/mgl
 
 	popd >/dev/null
@@ -949,6 +951,41 @@ function build_mathgl()
 	echo ${NAME} >> $BUILD_STATUS_FILE
 }
 
+function build_libvigra()
+{
+	NAME="libvigra"
+	ISBUILT_ARG=${NAME}
+	isBuilt
+	if [ $ISBUILT -eq 1 ] ; then
+		return;
+	fi
+	pushd deps >/dev/null
+	pushd libvigraimpex-* >/dev/null
+	
+	if [ $? -ne 0 ] ; then
+		echo "$NAME dir missing, or duplicated?"
+		exit 1
+	fi
+	make clean
+
+	APPLY_PATCH_ARG=$PATCHES_MATHGL
+	applyPatches
+
+	cmake -DCMAKE_INSTALL_PREFIX="$BASE" -DCMAKE_TOOLCHAIN_FILE=../../patches/cmake-toolchain$BITS_VAL -DPNG_PNG_INCLUDE_DIR=${BASEDIR}/include/
+
+	make -j $NUM_PROCS
+	
+	make install || { echo "$NAME install failed"; exit 1; } 
+
+
+	popd >/dev/null
+	popd >/dev/null
+	FIX_LA_FILE_ARG=libmgl
+	fix_la_file
+	
+	echo ${NAME} >> $BUILD_STATUS_FILE
+}
+
 function build_ftgl()
 {
 	NAME="libftgl"
@@ -1002,7 +1039,7 @@ function build_ftgl()
 
 	make -j $NUM_PROCS || { echo "ftgl build failed"; exit 1; } 
 	
-	make install DESTDIR="$BASE"|| { echo "ftgl install failed"; exit 1; } 
+	DESTDIR="$BASE" make install | { echo "ftgl install failed"; exit 1; } 
 
 	popd >/dev/null
 	popd >/dev/null
@@ -1099,7 +1136,7 @@ function build_3Depict()
 	make distclean
 
 
-	CONF_FLAG="--host=$HOST_VAL"
+	CONF_FLAG="--host=$HOST_VAL --with-libqhull-link=-lqhull_p"
 	if [ $IS_RELEASE -ne 0 ] ; then
 		CONF_FLAG="$CONF_FLAG --disable-debug-checks --enable-openmp-parallel"
 	fi
@@ -1161,8 +1198,6 @@ function build_3Depict()
 		exit 1
 	fi
 
-
-
 	#if the locales are missing, try to rebuild them
 	if [ x`find locales/ -name \*.mo` = x""  ] ; then
 		
@@ -1191,6 +1226,20 @@ function make_package()
 {
 	pushd ./code/3Depict 2> /dev/null
 
+	#Check that the PDF manual has been built
+	if [ ! -f docs/manual-latex/manual.pdf ] ; then
+		echo "PDF Manual not built. Building"
+	
+		pushd docs/manual-latex
+		pdflatex manual.tex && bibtex manual && pdflatex manual.tex  || { echo " Manual not pre-built, and failed to build. Aborting" ; exit 1; }
+		popd
+		if [ ! -f docs/manual-latex/manual.pdf ] ; then 
+			echo "Failed to build manual, even though latex completed with no errors. Aborting " 
+			exit 1;
+		fi
+	fi
+
+
 	NSI_FILE=./windows-installer.nsi
 
 	#copy as needed
@@ -1399,9 +1448,10 @@ build_libiconv
 build_gettext 
 build_ftgl 
 build_glew
+build_libvigra
 
 build_mathgl 
-build_wx	
+build_wx
 
 build_3Depict
 
diff --git a/packaging/mingw-debian-cross/patches/gettext-fix-configure-versions b/packaging/mingw-debian-cross/patches/gettext-fix-configure-versions
new file mode 100644
index 0000000..7ee9220
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/gettext-fix-configure-versions
@@ -0,0 +1,36 @@
+diff -r 60a714234035 configure.ac
+--- a/configure.ac	Sat Apr 25 18:57:00 2015 +0100
++++ b/configure.ac	Sat Apr 25 19:17:15 2015 +0100
+@@ -17,7 +17,7 @@
+ dnl Process this file with autoconf to produce a configure script.
+ 
+ AC_PREREQ([2.62])
+-AC_INIT([gettext],
++AC_INIT([gettext], [0.19.3],
+ 	m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+ 	[bug-gnu-gettext at gnu.org])
+ AC_CONFIG_SRCDIR([gettext-tools/src/msgfmt.c])
+diff -r 60a714234035 gettext-runtime/configure.ac
+--- a/gettext-runtime/configure.ac	Sat Apr 25 18:57:00 2015 +0100
++++ b/gettext-runtime/configure.ac	Sat Apr 25 19:17:15 2015 +0100
+@@ -17,7 +17,7 @@
+ dnl Process this file with autoconf to produce a configure script.
+ 
+ AC_PREREQ([2.62])
+-AC_INIT([gettext-runtime],
++AC_INIT([gettext-runtime], [0.19.3],
+ 	m4_esyscmd([../build-aux/git-version-gen ../.tarball-version]),
+ 	[bug-gnu-gettext at gnu.org])
+ AC_CONFIG_SRCDIR([intl/dcigettext.c])
+diff -r 60a714234035 gettext-tools/configure.ac
+--- a/gettext-tools/configure.ac	Sat Apr 25 18:57:00 2015 +0100
++++ b/gettext-tools/configure.ac	Sat Apr 25 19:17:15 2015 +0100
+@@ -17,7 +17,7 @@
+ dnl Process this file with autoconf to produce a configure script.
+ 
+ AC_PREREQ([2.62])
+-AC_INIT([gettext-tools],
++AC_INIT([gettext-tools],[0.19.3],
+ 	m4_esyscmd([../build-aux/git-version-gen ../.tarball-version]),
+ 	[bug-gnu-gettext at gnu.org])
+ AC_CONFIG_SRCDIR([src/msgfmt.c])
diff --git a/packaging/mingw-debian-cross/patches/glew-makefile b/packaging/mingw-debian-cross/patches/glew-makefile
index 2d4985b..30e6b8c 100644
--- a/packaging/mingw-debian-cross/patches/glew-makefile
+++ b/packaging/mingw-debian-cross/patches/glew-makefile
@@ -11,7 +11,7 @@ diff -r 9bbbd8b43e5b Makefile
  SHELL = /bin/sh
 -SYSTEM ?= $(shell config/config.guess | cut -d - -f 3 | sed -e 's/[0-9\.]//g;')
 -SYSTEM.SUPPORTED = $(shell test -f config/Makefile.$(SYSTEM) && echo 1)
-+SYSTEM = x86_64-w64-mingw32
++SYSTEM = i686-w64-mingw32
  
 -ifeq ($(SYSTEM.SUPPORTED), 1)
 -include config/Makefile.$(SYSTEM)
@@ -20,7 +20,7 @@ diff -r 9bbbd8b43e5b Makefile
 -endif
  
 -GLEW_DEST ?= /usr
-+GLEW_DEST ?= /home/pcuser/mingw64
++GLEW_DEST ?= /home/pcuser/mingw32
  BINDIR    ?= $(GLEW_DEST)/bin
  LIBDIR    ?= $(GLEW_DEST)/lib
  INCDIR    ?= $(GLEW_DEST)/include/GL
diff --git a/packaging/mingw-debian-cross/patches/mathgl-openmp-linker-flag b/packaging/mingw-debian-cross/patches/mathgl-openmp-linker-flag
new file mode 100644
index 0000000..35dbece
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/mathgl-openmp-linker-flag
@@ -0,0 +1,13 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index b369da1..0bc7547 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -219,6 +219,8 @@ if(enable-openmp)
+ 		set(MGL_HAVE_OMP 1)
+ 		set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
+ 		set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
++		set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fopenmp ")
++
+ 	else(OPENMP_FOUND)
+ 		message(SEND_ERROR "Couldn't find OpenMP. You can enable POSIX threads instead.")
+ 		set(MGL_HAVE_OMP 0)
diff --git a/packaging/mingw-debian-cross/patches/wx_changeset_76890.diff b/packaging/mingw-debian-cross/patches/wx_changeset_76890.diff
new file mode 100644
index 0000000..91ef82a
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/wx_changeset_76890.diff
@@ -0,0 +1,46 @@
+--- a/src/propgrid/propgridpagestate.cpp	(revision 76889)
++++ b/src/propgrid/propgridpagestate.cpp	(revision 76890)
+@@ -287,16 +287,31 @@
+     }
+ 
+-    m_regularArray.Empty();
+-    if ( m_abcArray )
+-        m_abcArray->Empty();
+-
+-    m_dictName.clear();
+-
+-    m_currentCategory = NULL;
+-    m_lastCaptionBottomnest = 1;
+-    m_itemsAdded = 0;
+-
+-    m_virtualHeight = 0;
+-    m_vhCalcPending = 0;
++    // If handling wxPG event then every property item must be
++    // deleted individually (and with deferral).
++    if ( m_pPropGrid && m_pPropGrid->m_processedEvent )
++    {
++        wxPropertyGridIterator it;
++        for ( it = m_pPropGrid->GetIterator(wxPG_ITERATE_ALL);
++              !it.AtEnd();
++              it++ )
++        {
++            DoDelete(*it, true);
++        }
++    }
++    else
++    {
++        m_regularArray.Empty();
++        if ( m_abcArray )
++            m_abcArray->Empty();
++
++        m_dictName.clear();
++
++        m_currentCategory = NULL;
++        m_lastCaptionBottomnest = 1;
++        m_itemsAdded = 0;
++
++        m_virtualHeight = 0;
++        m_vhCalcPending = 0;
++    }
+ }
+ 
diff --git a/packaging/mingw-debian-cross/windows-installer.nsi b/packaging/mingw-debian-cross/windows-installer.nsi
index 77b2fb8..1f1a358 100755
--- a/packaging/mingw-debian-cross/windows-installer.nsi
+++ b/packaging/mingw-debian-cross/windows-installer.nsi
@@ -2,7 +2,7 @@
 
 ; HM NIS Edit Wizard helper defines
 !define PRODUCT_NAME "3Depict"
-!define PRODUCT_VERSION "0.0.17"
+!define PRODUCT_VERSION "0.0.18"
 !define PRODUCT_PUBLISHER "D. Haley, A. Ceguerra"
 !define PRODUCT_WEB_SITE "http://threedepict.sourceforge.net"
 !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\3Depict.exe"
diff --git a/src/3Depict.cpp b/src/3Depict.cpp
index a1df700..ad1b583 100644
--- a/src/3Depict.cpp
+++ b/src/3Depict.cpp
@@ -1,6 +1,6 @@
 /*
  *	threeDepict.cpp - main program implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -39,6 +39,12 @@ enum
 
 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
+
 	MainWindowFrame* MainFrame ;
 	wxArrayString commandLineFiles;
 	wxLocale* usrLocale;
@@ -62,9 +68,6 @@ public:
     void MacReopenFile(const wxString & fileName);
 #endif
 
-#ifdef DEBUG
-    void setEventloggerFile(const char *file);
-#endif
 };
 
 //Check version is in place because wxT is deprecated for wx 2.9
@@ -91,15 +94,6 @@ winconsole winC;
 #endif
 #endif
 
-//DEBUG NaN and INF
-#ifdef DEBUG
-#ifdef __linux__
-#include <fenv.h>
-void trapfpe () {
-//  feenableexcept(FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
-}
-#endif
-#endif
 
 IMPLEMENT_APP(threeDepictApp)
 
@@ -168,7 +162,7 @@ void threeDepictApp::initLanguageSupport()
 #ifdef __WXMAC__
 			bindtextdomain( PROGRAM_NAME, paths->GetResourcesDir().mb_str(wxConvUTF8) );
 #elif defined(__WIN32) || defined(__WIN64)
-			cerr << paths->GetResourcesDir().mb_str(wxConvUTF8) << endl;
+			cerr << paths->GetResourcesDir().mb_str(wxConvUTF8) << std::endl;
 			std::string s;
 			s =  paths->GetResourcesDir().mb_str(wxConvUTF8);
 			s+="/locales/";
@@ -183,15 +177,15 @@ void threeDepictApp::initLanguageSupport()
 			switch(curPage)
 			{
 				case 1252:
-					cerr << "Bound cp1252" << endl;
+					std::cerr << "Bound cp1252" << std::endl;
 					bind_textdomain_codeset(PROGRAM_NAME, "CP1252");
 					break;
 				case 65001:
-					cerr << "Bound utf8"<< endl;
+					std::cerr << "Bound utf8"<< std::endl;
 					bind_textdomain_codeset(PROGRAM_NAME, "UTF-8");
 					break;
 				default:
-					cerr << "Unknown codepage " << curPage << endl;
+					std::cerr << "Unknown codepage " << curPage << std::endl;
 					break;
 			}			
 #else
@@ -202,7 +196,7 @@ void threeDepictApp::initLanguageSupport()
 	}
 	else
 	{
-		std::cout << "Language not supported, falling back to English" << endl;
+		std::cout << "Language not supported, falling back to English" << std::endl;
 		usrLocale = new wxLocale( wxLANGUAGE_ENGLISH );
 		language = wxLANGUAGE_ENGLISH;
 	}
@@ -285,7 +279,7 @@ void threeDepictApp::OnInitCmdLine(wxCmdLineParser& parser)
 	version=(PROGRAM_VERSION);
 	version+=wxT("\n");
 
-	preamble=wxT("Copyright (C) 2013  3Depict team\n");
+	preamble=wxT("Copyright (C) 2015  3Depict team\n");
 	preamble+=wxT("This program comes with ABSOLUTELY NO WARRANTY; for details see LICENCE file.\n");
 	preamble+=wxT("This is free software, and you are welcome to redistribute it under certain conditions.\n");
 	preamble+=wxT("Source code is available under the terms of the GNU GPL v3.0 or any later version (http://www.gnu.org/licenses/gpl.txt)\n");
@@ -314,42 +308,42 @@ bool threeDepictApp::OnCmdLineParsed(wxCmdLineParser& parser)
 				strFile=stlStr(f.GetFullPath());
 				if( !f.FileExists() )
 				{
-					cerr << "Unable to locate file:" << strFile << endl;
+					std::cerr << "Unable to locate file:" << strFile << std::endl;
 					return false;
 				}
 
-				cerr << "Loading :" << strFile << endl ;
+				std::cerr << "Loading :" << strFile << std::endl ;
 
 				{
 				VisController visControl;
-				if(!visControl.loadState(strFile.c_str(),cerr,false,true))
+				if(!visControl.state.load(strFile.c_str(),true,std::cerr))
 				{
-					cerr << "Error loading state file:" << endl;
+					std::cerr << "Error loading state file:" << std::endl;
 					return false;
 				}
 
 				//Run a refresh over the filter tree as a test
 				FilterTree f;
-				visControl.cloneFilterTree(f);
+				visControl.state.treeState.cloneFilterTree(f);
 				if(f.hasHazardousContents())
 				{
 					f.stripHazardousContents();
-					cerr << "For security reasons, the tree was pruned prior to execution." << endl;
+					std::cerr << "For security reasons, the tree was pruned prior to execution." << std::endl;
 				}
 				
 				if(!testFilterTree(f))
 				{
-					cerr << "Failed loading :" << strFile << " , aborting" << endl;
+					std::cerr << "Failed loading :" << strFile << " , aborting" << std::endl;
 					return false;
 				}
 				}
 
-				cerr << "OK" << endl; 
+				std::cerr << "OK" << std::endl; 
 
 			}
 			
 			 
-			cerr << "Test XML File(s) Loaded OK" << endl;
+			std::cerr << "Test XML File(s) Loaded OK" << std::endl;
 			dontLoad=true;	
 		}
 		else
@@ -357,12 +351,12 @@ bool threeDepictApp::OnCmdLineParsed(wxCmdLineParser& parser)
 			//Unit tests failed
 			if(!runUnitTests()) 
 			{
-				cerr << "Unit tests failed" <<endl;
+				std::cerr << "Unit tests failed" <<std::endl;
 				return false;
 			}
 			else
 			{
-				cerr << "Unit tests succeeded!" <<endl;
+				std::cerr << "Unit tests succeeded!" <<std::endl;
 				dontLoad=true;
 			}
 		}
@@ -411,6 +405,10 @@ bool threeDepictApp::OnInit()
 
     initLanguageSupport();
 	
+#if defined(DEBUG) && defined(__linux__)
+	//Virtualbox has a bug, where the video driver generates FPEs
+//   trapfpe(); //Under Linux, enable  segfault on invalid floating point operations
+#endif
 
     //Set the gettext language
     //Register signal handler for backtraces
@@ -441,9 +439,6 @@ bool threeDepictApp::OnInit()
 
     SetTopWindow(MainFrame);
 
-#if defined(DEBUG) && defined(__linux__)
-   trapfpe(); //Under Linux, enable  segfault on invalid floating point operations
-#endif
 
 #ifdef __APPLE__    
    	//Switch the working directory into the .app bundle's resources
diff --git a/src/Makefile.am b/src/Makefile.am
index 68011aa..363a44f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,10 +21,11 @@ bin_PROGRAMS= 3Depict
 
 #------- Common header files for all sub-modules
 COMMON_SOURCE_FILES = common/pngread.c common/stringFuncs.cpp common/constants.cpp common/xmlHelper.cpp\
-			 common/colourmap.cpp common/voxels.cpp common/mathfuncs.cpp common/basics.cpp common/assertion.cpp
+			 common/colourmap.cpp common/voxels.cpp common/mathfuncs.cpp common/basics.cpp common/assertion.cpp \
+			common/mesh.cpp common/gsl_helper.cpp
 COMMON_HEADER_FILES = common/pngread.h common/stringFuncs.h  common/constants.h  common/xmlHelper.h common/colourmap.h \
 		      	common/mathfuncs.h common/basics.h common/translation.h common/endianTest.h common/assertion.h common/voxels.h \
-			common/array2D.h
+			common/array2D.h common/mesh.h common/gsl_helper.h
 
 #-----------
 
@@ -38,7 +39,7 @@ FILTER_FILES = backend/filters/allFilter.cpp backend/filters/filterCommon.cpp \
 		backend/filters/compositionProfile.cpp backend/filters/spatialAnalysis.cpp \
 		backend/filters/clusterAnalysis.cpp backend/filters/ionInfo.cpp \
 		backend/filters/annotation.cpp backend/filters/geometryHelpers.cpp \
-		backend/filters/algorithms/binomial.cpp  
+		backend/filters/algorithms/binomial.cpp  backend/filters/algorithms/mass.cpp  
 
 FILTER_HEADER_FILES = backend/filters/allFilter.h backend/filters/filterCommon.h \
 		backend/filters/dataLoad.h backend/filters/ionDownsample.h \
@@ -48,7 +49,7 @@ FILTER_HEADER_FILES = backend/filters/allFilter.h backend/filters/filterCommon.h
 		backend/filters/compositionProfile.h backend/filters/spatialAnalysis.h \
 		backend/filters/clusterAnalysis.h backend/filters/ionInfo.h \
 		backend/filters/annotation.h backend/filters/geometryHelpers.h \
-		backend/filters/algorithms/binomial.h 
+		backend/filters/algorithms/binomial.h backend/filters/algorithms/mass.h
 
 BACKEND_SOURCE_FILES = backend/animator.cpp backend/filtertreeAnalyse.cpp backend/filtertree.cpp \
 		     	backend/APT/ionhit.cpp backend/APT/APTFileIO.cpp backend/APT/APTRanges.cpp backend/APT/abundanceParser.cpp \
@@ -66,8 +67,8 @@ BACKEND_HEADER_FILES =  backend/animator.h backend/filtertreeAnalyse.h backend/f
 #------------
 
 #------------ OpenGL interface files
-OPENGL_HEADER_FILES = gl/scene.h gl/drawables.h gl/effect.h gl/textures.h gl/select.h gl/cameras.h gl/isoSurface.h gl/tr.h gl/glDebug.h
-OPENGL_SOURCE_FILES =  gl/scene.cpp gl/drawables.cpp gl/effect.cpp gl/textures.cpp gl/select.cpp gl/cameras.cpp gl/isoSurface.cpp gl/tr.cpp
+OPENGL_HEADER_FILES = gl/scene.h gl/drawables.h gl/effect.h gl/textures.h gl/select.h gl/cameras.h gl/isoSurface.h gl/tr.h gl/glDebug.h 
+OPENGL_SOURCE_FILES =  gl/scene.cpp gl/drawables.cpp gl/effect.cpp gl/textures.cpp gl/select.cpp gl/cameras.cpp gl/isoSurface.cpp gl/tr.cpp 
 #------------
 
 
diff --git a/src/Makefile.in b/src/Makefile.in
index 54a2a60..9fcf0d4 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -137,6 +137,7 @@ am__3Depict_SOURCES_DIST = 3Depict.cpp testing/testing.cpp \
 	backend/filters/ionInfo.cpp backend/filters/annotation.cpp \
 	backend/filters/geometryHelpers.cpp \
 	backend/filters/algorithms/binomial.cpp \
+	backend/filters/algorithms/mass.cpp \
 	backend/filters/allFilter.h backend/filters/filterCommon.h \
 	backend/filters/dataLoad.h backend/filters/ionDownsample.h \
 	backend/filters/rangeFile.h backend/filters/voxelise.h \
@@ -147,7 +148,8 @@ am__3Depict_SOURCES_DIST = 3Depict.cpp testing/testing.cpp \
 	backend/filters/spatialAnalysis.h \
 	backend/filters/clusterAnalysis.h backend/filters/ionInfo.h \
 	backend/filters/annotation.h backend/filters/geometryHelpers.h \
-	backend/filters/algorithms/binomial.h backend/animator.cpp \
+	backend/filters/algorithms/binomial.h \
+	backend/filters/algorithms/mass.h backend/animator.cpp \
 	backend/filtertreeAnalyse.cpp backend/filtertree.cpp \
 	backend/APT/ionhit.cpp backend/APT/APTFileIO.cpp \
 	backend/APT/APTRanges.cpp backend/APT/abundanceParser.cpp \
@@ -170,11 +172,13 @@ am__3Depict_SOURCES_DIST = 3Depict.cpp testing/testing.cpp \
 	common/pngread.c common/stringFuncs.cpp common/constants.cpp \
 	common/xmlHelper.cpp common/colourmap.cpp common/voxels.cpp \
 	common/mathfuncs.cpp common/basics.cpp common/assertion.cpp \
-	common/pngread.h common/stringFuncs.h common/constants.h \
-	common/xmlHelper.h common/colourmap.h common/mathfuncs.h \
-	common/basics.h common/translation.h common/endianTest.h \
-	common/assertion.h common/voxels.h common/array2D.h \
-	testing/mglTesting.cpp testing/mglTesting.h 3Depict.rc
+	common/mesh.cpp common/gsl_helper.cpp common/pngread.h \
+	common/stringFuncs.h common/constants.h common/xmlHelper.h \
+	common/colourmap.h common/mathfuncs.h common/basics.h \
+	common/translation.h common/endianTest.h common/assertion.h \
+	common/voxels.h common/array2D.h common/mesh.h \
+	common/gsl_helper.h testing/mglTesting.cpp \
+	testing/mglTesting.h 3Depict.rc
 am__dirstamp = $(am__leading_dot)dirstamp
 am__objects_1 = 3Depict-3Depict.$(OBJEXT) \
 	testing/3Depict-testing.$(OBJEXT) \
@@ -217,7 +221,8 @@ am__objects_6 = backend/filters/3Depict-allFilter.$(OBJEXT) \
 	backend/filters/3Depict-ionInfo.$(OBJEXT) \
 	backend/filters/3Depict-annotation.$(OBJEXT) \
 	backend/filters/3Depict-geometryHelpers.$(OBJEXT) \
-	backend/filters/algorithms/3Depict-binomial.$(OBJEXT)
+	backend/filters/algorithms/3Depict-binomial.$(OBJEXT) \
+	backend/filters/algorithms/3Depict-mass.$(OBJEXT)
 am__objects_7 = backend/3Depict-animator.$(OBJEXT) \
 	backend/3Depict-filtertreeAnalyse.$(OBJEXT) \
 	backend/3Depict-filtertree.$(OBJEXT) \
@@ -245,7 +250,9 @@ am__objects_9 = common/3Depict-pngread.$(OBJEXT) \
 	common/3Depict-voxels.$(OBJEXT) \
 	common/3Depict-mathfuncs.$(OBJEXT) \
 	common/3Depict-basics.$(OBJEXT) \
-	common/3Depict-assertion.$(OBJEXT)
+	common/3Depict-assertion.$(OBJEXT) \
+	common/3Depict-mesh.$(OBJEXT) \
+	common/3Depict-gsl_helper.$(OBJEXT)
 am__objects_10 = testing/3Depict-mglTesting.$(OBJEXT)
 am__objects_11 = $(am__objects_1) $(am__objects_2) $(am__objects_4) \
 	$(am__objects_5) $(am__objects_6) $(am__objects_2) \
@@ -379,6 +386,7 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LDFLAGS = @LDFLAGS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
+LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MGL_CFLAGS = @MGL_CFLAGS@
@@ -401,6 +409,7 @@ PNG_CFLAGS = @PNG_CFLAGS@
 PNG_LIBS = @PNG_LIBS@
 QHULL_CFLAGS = @QHULL_CFLAGS@
 QHULL_LIBS = @QHULL_LIBS@
+RANLIB = @RANLIB@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
@@ -485,11 +494,12 @@ MSYS_PATH = /c/msys/1.0/local/include/
 
 #------- Common header files for all sub-modules
 COMMON_SOURCE_FILES = common/pngread.c common/stringFuncs.cpp common/constants.cpp common/xmlHelper.cpp\
-			 common/colourmap.cpp common/voxels.cpp common/mathfuncs.cpp common/basics.cpp common/assertion.cpp
+			 common/colourmap.cpp common/voxels.cpp common/mathfuncs.cpp common/basics.cpp common/assertion.cpp \
+			common/mesh.cpp common/gsl_helper.cpp
 
 COMMON_HEADER_FILES = common/pngread.h common/stringFuncs.h  common/constants.h  common/xmlHelper.h common/colourmap.h \
 		      	common/mathfuncs.h common/basics.h common/translation.h common/endianTest.h common/assertion.h common/voxels.h \
-			common/array2D.h
+			common/array2D.h common/mesh.h common/gsl_helper.h
 
 
 #-----------
@@ -504,7 +514,7 @@ FILTER_FILES = backend/filters/allFilter.cpp backend/filters/filterCommon.cpp \
 		backend/filters/compositionProfile.cpp backend/filters/spatialAnalysis.cpp \
 		backend/filters/clusterAnalysis.cpp backend/filters/ionInfo.cpp \
 		backend/filters/annotation.cpp backend/filters/geometryHelpers.cpp \
-		backend/filters/algorithms/binomial.cpp  
+		backend/filters/algorithms/binomial.cpp  backend/filters/algorithms/mass.cpp  
 
 FILTER_HEADER_FILES = backend/filters/allFilter.h backend/filters/filterCommon.h \
 		backend/filters/dataLoad.h backend/filters/ionDownsample.h \
@@ -514,7 +524,7 @@ FILTER_HEADER_FILES = backend/filters/allFilter.h backend/filters/filterCommon.h
 		backend/filters/compositionProfile.h backend/filters/spatialAnalysis.h \
 		backend/filters/clusterAnalysis.h backend/filters/ionInfo.h \
 		backend/filters/annotation.h backend/filters/geometryHelpers.h \
-		backend/filters/algorithms/binomial.h 
+		backend/filters/algorithms/binomial.h backend/filters/algorithms/mass.h
 
 BACKEND_SOURCE_FILES = backend/animator.cpp backend/filtertreeAnalyse.cpp backend/filtertree.cpp \
 		     	backend/APT/ionhit.cpp backend/APT/APTFileIO.cpp backend/APT/APTRanges.cpp backend/APT/abundanceParser.cpp \
@@ -533,8 +543,8 @@ BACKEND_HEADER_FILES = backend/animator.h backend/filtertreeAnalyse.h backend/fi
 #------------
 
 #------------ OpenGL interface files
-OPENGL_HEADER_FILES = gl/scene.h gl/drawables.h gl/effect.h gl/textures.h gl/select.h gl/cameras.h gl/isoSurface.h gl/tr.h gl/glDebug.h
-OPENGL_SOURCE_FILES = gl/scene.cpp gl/drawables.cpp gl/effect.cpp gl/textures.cpp gl/select.cpp gl/cameras.cpp gl/isoSurface.cpp gl/tr.cpp
+OPENGL_HEADER_FILES = gl/scene.h gl/drawables.h gl/effect.h gl/textures.h gl/select.h gl/cameras.h gl/isoSurface.h gl/tr.h gl/glDebug.h 
+OPENGL_SOURCE_FILES = gl/scene.cpp gl/drawables.cpp gl/effect.cpp gl/textures.cpp gl/select.cpp gl/cameras.cpp gl/isoSurface.cpp gl/tr.cpp 
 #------------
 
 #------------ Frontend (linked to UI in some way) files ---
@@ -798,6 +808,9 @@ backend/filters/algorithms/$(DEPDIR)/$(am__dirstamp):
 backend/filters/algorithms/3Depict-binomial.$(OBJEXT):  \
 	backend/filters/algorithms/$(am__dirstamp) \
 	backend/filters/algorithms/$(DEPDIR)/$(am__dirstamp)
+backend/filters/algorithms/3Depict-mass.$(OBJEXT):  \
+	backend/filters/algorithms/$(am__dirstamp) \
+	backend/filters/algorithms/$(DEPDIR)/$(am__dirstamp)
 backend/$(am__dirstamp):
 	@$(MKDIR_P) backend
 	@: > backend/$(am__dirstamp)
@@ -890,6 +903,10 @@ common/3Depict-basics.$(OBJEXT): common/$(am__dirstamp) \
 	common/$(DEPDIR)/$(am__dirstamp)
 common/3Depict-assertion.$(OBJEXT): common/$(am__dirstamp) \
 	common/$(DEPDIR)/$(am__dirstamp)
+common/3Depict-mesh.$(OBJEXT): common/$(am__dirstamp) \
+	common/$(DEPDIR)/$(am__dirstamp)
+common/3Depict-gsl_helper.$(OBJEXT): common/$(am__dirstamp) \
+	common/$(DEPDIR)/$(am__dirstamp)
 testing/3Depict-mglTesting.$(OBJEXT): testing/$(am__dirstamp) \
 	testing/$(DEPDIR)/$(am__dirstamp)
 
@@ -949,12 +966,15 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at backend/filters/algorithms/$(DEPDIR)/3Depict-K3DTree-mk2.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at backend/filters/algorithms/$(DEPDIR)/3Depict-K3DTree.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at backend/filters/algorithms/$(DEPDIR)/3Depict-binomial.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at backend/filters/algorithms/$(DEPDIR)/3Depict-rdf.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-assertion.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-basics.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-colourmap.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-constants.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-gsl_helper.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-mathfuncs.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-mesh.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-pngread.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-stringFuncs.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/3Depict-voxels.Po at am__quote@
@@ -1609,6 +1629,20 @@ backend/filters/algorithms/3Depict-binomial.obj: backend/filters/algorithms/bino
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o backend/filters/algorithms/3Depict-binomial.obj `if test -f 'backend/filters/algorithms/binomial.cpp'; then $(CYGPATH_W) 'backend/filters/algorithms/binomial.cpp'; else $(CYGPATH_W) '$(srcdir)/backend/filters/algorithms/binomial.cpp'; fi`
 
+backend/filters/algorithms/3Depict-mass.o: backend/filters/algorithms/mass.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT backend/filters/algorithms/3Depict-mass.o -MD -MP -MF backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Tpo -c -o backend/filters/algorithms/3Depict-mass.o `test -f 'backend/filters/algorithms/mass.cpp' || echo '$(srcdir)/'`backend/filters/algorithms/mass.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Tpo backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='backend/filters/algorithms/mass.cpp' object='backend/filters/algorithms/3Depict-mass.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o backend/filters/algorithms/3Depict-mass.o `test -f 'backend/filters/algorithms/mass.cpp' || echo '$(srcdir)/'`backend/filters/algorithms/mass.cpp
+
+backend/filters/algorithms/3Depict-mass.obj: backend/filters/algorithms/mass.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT backend/filters/algorithms/3Depict-mass.obj -MD -MP -MF backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Tpo -c -o backend/filters/algorithms/3Depict-mass.obj `if test -f 'backend/filters/algorithms/mass.cpp'; then $(CYGPATH_W) 'backend/filters/algorithms/mass.cpp'; else $(CYGPATH_W) '$(srcdir)/backend/filters/algorithms/mass.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Tpo backend/filters/algorithms/$(DEPDIR)/3Depict-mass.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='backend/filters/algorithms/mass.cpp' object='backend/filters/algorithms/3Depict-mass.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o backend/filters/algorithms/3Depict-mass.obj `if test -f 'backend/filters/algorithms/mass.cpp'; then $(CYGPATH_W) 'backend/filters/algorithms/mass.cpp'; else $(CYGPATH_W) '$(srcdir)/backend/filters/algorithms/mass.cpp'; fi`
+
 backend/3Depict-animator.o: backend/animator.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT backend/3Depict-animator.o -MD -MP -MF backend/$(DEPDIR)/3Depict-animator.Tpo -c -o backend/3Depict-animator.o `test -f 'backend/animator.cpp' || echo '$(srcdir)/'`backend/animator.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) backend/$(DEPDIR)/3Depict-animator.Tpo backend/$(DEPDIR)/3Depict-animator.Po
@@ -2043,6 +2077,34 @@ common/3Depict-assertion.obj: common/assertion.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o common/3Depict-assertion.obj `if test -f 'common/assertion.cpp'; then $(CYGPATH_W) 'common/assertion.cpp'; else $(CYGPATH_W) '$(srcdir)/common/assertion.cpp'; fi`
 
+common/3Depict-mesh.o: common/mesh.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT common/3Depict-mesh.o -MD -MP -MF common/$(DEPDIR)/3Depict-mesh.Tpo -c -o common/3Depict-mesh.o `test -f 'common/mesh.cpp' || echo '$(srcdir)/'`common/mesh.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/3Depict-mesh.Tpo common/$(DEPDIR)/3Depict-mesh.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='common/mesh.cpp' object='common/3Depict-mesh.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o common/3Depict-mesh.o `test -f 'common/mesh.cpp' || echo '$(srcdir)/'`common/mesh.cpp
+
+common/3Depict-mesh.obj: common/mesh.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT common/3Depict-mesh.obj -MD -MP -MF common/$(DEPDIR)/3Depict-mesh.Tpo -c -o common/3Depict-mesh.obj `if test -f 'common/mesh.cpp'; then $(CYGPATH_W) 'common/mesh.cpp'; else $(CYGPATH_W) '$(srcdir)/common/mesh.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/3Depict-mesh.Tpo common/$(DEPDIR)/3Depict-mesh.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='common/mesh.cpp' object='common/3Depict-mesh.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o common/3Depict-mesh.obj `if test -f 'common/mesh.cpp'; then $(CYGPATH_W) 'common/mesh.cpp'; else $(CYGPATH_W) '$(srcdir)/common/mesh.cpp'; fi`
+
+common/3Depict-gsl_helper.o: common/gsl_helper.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT common/3Depict-gsl_helper.o -MD -MP -MF common/$(DEPDIR)/3Depict-gsl_helper.Tpo -c -o common/3Depict-gsl_helper.o `test -f 'common/gsl_helper.cpp' || echo '$(srcdir)/'`common/gsl_helper.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/3Depict-gsl_helper.Tpo common/$(DEPDIR)/3Depict-gsl_helper.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='common/gsl_helper.cpp' object='common/3Depict-gsl_helper.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o common/3Depict-gsl_helper.o `test -f 'common/gsl_helper.cpp' || echo '$(srcdir)/'`common/gsl_helper.cpp
+
+common/3Depict-gsl_helper.obj: common/gsl_helper.cpp
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT common/3Depict-gsl_helper.obj -MD -MP -MF common/$(DEPDIR)/3Depict-gsl_helper.Tpo -c -o common/3Depict-gsl_helper.obj `if test -f 'common/gsl_helper.cpp'; then $(CYGPATH_W) 'common/gsl_helper.cpp'; else $(CYGPATH_W) '$(srcdir)/common/gsl_helper.cpp'; fi`
+ at am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/3Depict-gsl_helper.Tpo common/$(DEPDIR)/3Depict-gsl_helper.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='common/gsl_helper.cpp' object='common/3Depict-gsl_helper.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -c -o common/3Depict-gsl_helper.obj `if test -f 'common/gsl_helper.cpp'; then $(CYGPATH_W) 'common/gsl_helper.cpp'; else $(CYGPATH_W) '$(srcdir)/common/gsl_helper.cpp'; fi`
+
 testing/3Depict-mglTesting.o: testing/mglTesting.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(3Depict_CXXFLAGS) $(CXXFLAGS) -MT testing/3Depict-mglTesting.o -MD -MP -MF testing/$(DEPDIR)/3Depict-mglTesting.Tpo -c -o testing/3Depict-mglTesting.o `test -f 'testing/mglTesting.cpp' || echo '$(srcdir)/'`testing/mglTesting.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) testing/$(DEPDIR)/3Depict-mglTesting.Tpo testing/$(DEPDIR)/3Depict-mglTesting.Po
diff --git a/src/backend/APT/APTFileIO.cpp b/src/backend/APT/APTFileIO.cpp
index c3b7cad..62ae9a6 100644
--- a/src/backend/APT/APTFileIO.cpp
+++ b/src/backend/APT/APTFileIO.cpp
@@ -1,6 +1,6 @@
 /* 
  * APTClasses.h - Generic APT components code
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 #include "ionhit.h"
 
 #include "../../common/stringFuncs.h"
+#include "../../common/basics.h"
 #include "../../common/translation.h"
 
 
@@ -85,7 +86,7 @@ const char *ION_TEXT_ERR_STRINGS[] = { "",
 					};
 //---------
 
-//ATO formatted files error codes and asscoiated strings
+//ATO formatted files error codes and associated strings
 //---------
 enum
 {
@@ -109,7 +110,7 @@ const char *LAWATAP_ATO_ERR_STRINGS[] = { "",
 //---------
 
 unsigned int LimitLoadPosFile(unsigned int inputnumcols, unsigned int outputnumcols, const unsigned int index[], vector<IonHit> &posIons,const char *posFile, size_t limitCount,
-	       	unsigned int &progress, bool (*callback)(bool),bool strongSampling)
+	       	unsigned int &progress, ATOMIC_BOOL &wantAbort,bool strongSampling)
 {
 
 
@@ -178,7 +179,7 @@ unsigned int LimitLoadPosFile(unsigned int inputnumcols, unsigned int outputnumc
 		delete[] buffer;
 		delete[] buffer2;
 		//Try opening it using the normal functions
-		return GenericLoadFloatFile(inputnumcols, outputnumcols, index, posIons,posFile,progress, callback);
+		return GenericLoadFloatFile(inputnumcols, outputnumcols, index, posIons,posFile,progress, wantAbort);
 	}
 
 	//Use a sampling method to load the pos file
@@ -191,7 +192,7 @@ unsigned int LimitLoadPosFile(unsigned int inputnumcols, unsigned int outputnumc
 		rng.initTimer();
 		unsigned int dummy;
 		randomDigitSelection(ionsToLoad,maxIons,rng,
-				limitCount,dummy,callback,strongSampling);
+				limitCount,dummy,strongSampling);
 	}
 	catch(std::bad_alloc)
 	{
@@ -257,7 +258,7 @@ unsigned int LimitLoadPosFile(unsigned int inputnumcols, unsigned int outputnumc
 
 			progress= (unsigned int)((float)(CFile.tellg())/((float)fileSize)*100.0f);
 			curProg=PROGRESS_REDUCE;
-			if(!(*callback)(false))
+			if(wantAbort)
 			{
 				delete[] buffer;
 				delete[] buffer2;
@@ -276,7 +277,7 @@ unsigned int LimitLoadPosFile(unsigned int inputnumcols, unsigned int outputnumc
 
 unsigned int GenericLoadFloatFile(unsigned int inputnumcols, unsigned int outputnumcols, 
 		const unsigned int index[], vector<IonHit> &posIons,const char *posFile, 
-			unsigned int &progress, bool (*callback)(bool))
+			unsigned int &progress, ATOMIC_BOOL &wantAbort)
 {
 	ASSERT(outputnumcols==4); //Due to ionHit.setHit
 	//buffersize must be a power of two and at least sizeof(float)*outputnumCols
@@ -409,7 +410,7 @@ unsigned int GenericLoadFloatFile(unsigned int inputnumcols, unsigned int output
 			{
 				progress= (unsigned int)((float)(CFile.tellg())/((float)fileSize)*100.0f);
 				curProg=PROGRESS_REDUCE;
-				if(!(*callback)(false))
+				if(wantAbort)
 				{
 					delete[] buffer;
 					delete[] buffer2;
@@ -436,7 +437,7 @@ unsigned int GenericLoadFloatFile(unsigned int inputnumcols, unsigned int output
 //TODO: Add progress
 unsigned int limitLoadTextFile(unsigned int maxCols, 
 			vector<vector<float> > &data,const char *textFile, const char *delim, const size_t limitCount,
-				unsigned int &progress, bool (*callback)(bool),bool strongRandom)
+				unsigned int &progress, ATOMIC_BOOL &wantAbort,bool strongRandom)
 {
 	ASSERT(maxCols);
 	ASSERT(textFile);
@@ -596,7 +597,7 @@ unsigned int limitLoadTextFile(unsigned int maxCols,
 		rng.initTimer();
 		unsigned int dummy;
 		randomDigitSelection(dataToLoad,newLinePositions.size(),rng,
-				limitCount,dummy,callback,strongRandom);
+				limitCount,dummy,strongRandom);
 	}
 	catch(std::bad_alloc)
 	{
@@ -604,11 +605,23 @@ unsigned int limitLoadTextFile(unsigned int maxCols,
 		return TEXT_ERR_ALLOC_FAIL;
 	}
 
-	(*callback)(true);
-
+	//check for abort before/after sort, as this is a long process that we cannot
+	// safely abort
+	if(wantAbort)
+	{
+		delete[] buffer;
+		return POS_ABORT_FAIL;
+	}
 	//Sort the data such that we are going to
 	//always jump forwards in the file; better disk access and whatnot.
 	std::sort(dataToLoad.begin(),dataToLoad.end());
+	
+	//check again for abort
+	if(wantAbort)
+	{
+		delete[] buffer;
+		return POS_ABORT_FAIL;
+	}
 
 	//OK, so we have  a list of newlines
 	//that we can use as entry points for random seek.
@@ -677,7 +690,7 @@ unsigned int limitLoadTextFile(unsigned int maxCols,
 
 
 
-unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned int &progress, bool (*callback)(bool),unsigned int forceEndian)
+unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned int &progress, ATOMIC_BOOL &wantAbort,unsigned int forceEndian)
 {
 
 
@@ -756,8 +769,8 @@ unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned in
 	}
 
 
-	//Heuristic fo detect endianness.
-	// - Randomly sample 100 pts from file, and check to see if, when interpreted either wa
+	//Heuristic to detect endianness.
+	// - Randomly sample 100 pts from file, and check to see if, when interpreted either way,
 	// there are any NaN
 	//   
 
@@ -784,9 +797,9 @@ unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned in
 		RandNumGen rng;
 		rng.initTimer();
 		randomDigitSelection(randomNumbers,pointCount,rng, 
-					numToCheck,dummy,dummyCallback);
+					numToCheck,dummy,wantAbort);
 
-		//Make the travese in ascending order
+		//Make the traverse in ascending order
 		std::sort(randomNumbers.begin(),randomNumbers.end());
 
 		//One for no endian-flip, one for flip
@@ -877,7 +890,7 @@ unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned in
 
 	//File records consist of 14 fields, some of which may not be initialised.
 	// each being 4-byte IEEE little-endian float
-	// It is unknown how to detect initalisated state.
+	// It is unknown how to detect initialised state.
 	// Field 	Data	
 	// 0-3		x,y,z,m/c in Angstrom (x,yz) or Da (m/c)
 	// 4		clusterID, if set
@@ -890,7 +903,7 @@ unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned in
 	// 11		"Virtual voltage" for reconstruction.
 	//			- Ignore this field, as this information is redundant
 	// 12,13	Fourier intensity
-	//			- Ignore theese fields, as this information is redundant
+	//			- Ignore these fields, as this information is redundant
 	//Attempt to detect
 	CFile.seekg(8);
 
@@ -914,7 +927,7 @@ unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned in
 	}
 	else
 	{
-		//read wthout swapping
+		//read without swapping
 		while((size_t)CFile.tellg() < fileSize)
 		{	
 			CFile.read((char*)buffer,LAWATAP_ATO_RECORD_SIZE);
@@ -1004,27 +1017,26 @@ bool testATOFormat()
 	unsigned int dummyProgress;
 
 
+	ATOMIC_BOOL wantAbort;
+	wantAbort=false;
 	vector<IonHit> ions;
 	//Load using auto-detection of endinanness
-	TEST(!LoadATOFile(filename.c_str(),ions,dummyProgress,dummyCallback),"ATO load test  (auto endianness)");
+	TEST(!LoadATOFile(filename.c_str(),ions,dummyProgress,wantAbort),"ATO load test  (auto endianness)");
 
 	TEST(ions.size() == 100,"ion size check");
 
-	TEST((ions[0].getPos().sqrDist(Point3D(1,1,0)) < sqrt(std::numeric_limits<float>::epsilon())),"Checking read/write OK");
+	TEST((ions[0].getPos().sqrDist(Point3D(1,1,0)) < sqrtf(std::numeric_limits<float>::epsilon())),"Checking read/write OK");
 	//Load using auto-detection of endinanness
 
 	//Load, forcing assuming cont4ents are little endianness as requried
-	TEST(!LoadATOFile(filename.c_str(),ions,dummyProgress,dummyCallback,1),"ATO load test (forced endianness)");
+	TEST(!LoadATOFile(filename.c_str(),ions,dummyProgress,wantAbort,1),"ATO load test (forced endianness)");
 	TEST(ions.size() == 100,"ion size check");
-	TEST((ions[0].getPos().sqrDist(Point3D(1,1,0)) < sqrt(std::numeric_limits<float>::epsilon())),"checking read/write OK");
+	TEST((ions[0].getPos().sqrDist(Point3D(1,1,0)) < sqrtf(std::numeric_limits<float>::epsilon())),"checking read/write OK");
 
 
 	
 
-	rmFile(filename.c_str());
-
-
-
+	rmFile(filename);
 
 	return true;
 
diff --git a/src/backend/APT/APTFileIO.h b/src/backend/APT/APTFileIO.h
index e743b9c..7677e9f 100644
--- a/src/backend/APT/APTFileIO.h
+++ b/src/backend/APT/APTFileIO.h
@@ -1,6 +1,6 @@
 /*
  * APTClasses.h - Generic APT components header 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -66,25 +66,25 @@ enum posErrors
 //!Load a pos file into a T of IonHits
 unsigned int GenericLoadFloatFile(unsigned int inputnumcols, unsigned int outputnumcols, 
 		const unsigned int index[], vector<IonHit> &posIons,const char *posFile, 
-				unsigned int &progress, bool (*callback)(bool));
+				unsigned int &progress, ATOMIC_BOOL &wantAbort);
 
 
 unsigned int LimitLoadPosFile(unsigned int inputnumcols, unsigned int outputnumcols, const unsigned int index[], 
 			vector<IonHit> &posIons,const char *posFile, size_t limitCount,
-					       	unsigned int &progress, bool (*callback)(bool),bool strongRandom);
+					       	unsigned int &progress, ATOMIC_BOOL &wantAbort,bool strongRandom);
 
 
 
 unsigned int limitLoadTextFile(unsigned int numColsTotal, 
 			vector<vector<float> > &data,const char *posFile, const char *deliminator, const size_t limitCount,
-					       	unsigned int &progress, bool (*callback)(bool),bool strongRandom);
+					       	unsigned int &progress, ATOMIC_BOOL &wantAbort,bool strongRandom);
 
 
 //Load a CAMECA LAWATAP "ATO" formatted file.
 //	- This is a totally different format to the "FlexTAP" ato format
 //Supported versions are "version 3"
 //	Force endian : 0 - do not force, autodetect, 1 - force little, 2- force big
-unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned int &progressm, bool (*callback)(bool), unsigned int forceEndian=0);
+unsigned int LoadATOFile(const char *fileName, vector<IonHit> &ions, unsigned int &progressm, ATOMIC_BOOL &wantAbort, unsigned int forceEndian=0);
 
 
 #ifdef DEBUG
diff --git a/src/backend/APT/APTRanges.cpp b/src/backend/APT/APTRanges.cpp
index c8b06eb..e851d20 100644
--- a/src/backend/APT/APTRanges.cpp
+++ b/src/backend/APT/APTRanges.cpp
@@ -1,6 +1,6 @@
 /*
  * APTRanges.cpp - Atom probe rangefile class 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -96,6 +96,7 @@ const char *elementList[] = {
 };	
 
 
+//Known issues - will not decompose brackets, eg Fe(OH)2
 bool RangeFile::decomposeIonNames(const std::string &name,
 		std::vector<pair<string,size_t> > &fragments)
 {
@@ -199,17 +200,17 @@ bool RangeFile::decomposeIonNames(const std::string &name,
 	vector<bool> toKill(fragments.size(),false);
 	for(size_t ui=0;ui<fragments.size();ui++)
 	{
-		//skip empty framgnets
+		//skip empty fragments
 		if(fragments[ui].first.empty())
-			ui++;
+			continue;	
 
 		for(size_t uj=ui+1;uj<fragments.size();uj++)
 		{
-			//skip empty gragments
+			//skip empty fragments
 			if(fragments[uj].first.empty())
-				uj++;
+				continue;	
 
-			//Collect fragment multiplcities if they have the same name
+			//Collect fragment multiplicities if they have the same name
 			if(fragments[uj].first == fragments[ui].first)
 			{
 				fragments[ui].second+=fragments[uj].second;
@@ -667,7 +668,7 @@ unsigned int RangeFile::openDoubleRNG(FILE *fpRange)
 	if(errCode)
 		return errCode;
 
-	//Spin forwards ot the "polyatomic extension" line
+	//Spin forwards to the "polyatomic extension" line
 
 		
 	char *inBuffer = new char[MAX_LINE_SIZE];
@@ -697,7 +698,7 @@ unsigned int RangeFile::openDoubleRNG(FILE *fpRange)
 	//Now merge the two range files by using the mass pair data as a key
 
 	//Find the matching ranges
-	//range IDs from first and second file who have matching rnage values
+	//range IDs from first and second file who have matching range values
 	vector< pair<size_t,size_t> > rangeMatches;
 	//IonID from the first dataset that we will need to replace
 	vector<size_t> overrideIonID;
@@ -913,7 +914,7 @@ unsigned int RangeFile::openRNG( FILE *fpRange)
 			if(composeMap.empty())
 			{
 				//We have a multiple, but no way of composing it!
-				// we will need ot build our own table entry.
+				// we will need to build our own table entry.
 				// For now, just store the freq tableentry
 				unassignedMultiples.push_back(make_pair(ui,freqEntries));
 				ionIDs.push_back(-2);
@@ -2870,3 +2871,80 @@ void RangeFile::eraseIon(size_t ionId)
 
 
 }
+
+bool RangeFile::decompose(RangeFile &rng) const
+{
+	//find the list of decomposables
+	rng=*this;	
+	for(size_t ui=0; ui<ionNames.size(); ui++)
+	{	
+		vector<pair<string,size_t > > fragments;
+		if(!decomposeIonNames(ionNames[ui].first,fragments))
+			return false;
+
+		for(size_t uj=0; uj<fragments.size();uj++)
+		{
+			if(rng.getIonID(fragments[uj].first) == -1)
+			{
+				//make a new random colour
+				RGBf col;
+				col.red=rand()/(float)std::numeric_limits<int>::max();
+				col.green=rand()/(float)std::numeric_limits<int>::max();
+				col.blue=rand()/(float)std::numeric_limits<int>::max();
+				//Create a new ion to support this fragment
+				rng.addIon(fragments[uj].first,fragments[uj].first,col);
+			}
+		}	
+	}	
+	ASSERT(rng.isSelfDecomposable());
+
+	return true;
+}
+
+bool RangeFile::isSelfDecomposable() const
+{
+	for(size_t ui=0; ui<ionNames.size(); ui++)
+	{	
+		vector<pair<string,size_t > > fragments;
+		if(!decomposeIonNames(ionNames[ui].first,fragments))
+			return false;
+		//ion cannot be decomposed into smaller fragments. 
+		//This is not a decomposable rangefile
+		if(getIonID(ionNames[ui].first) == -1)
+			return false;
+	}	
+
+	return true;
+}
+
+bool RangeFile::getDecomposition(std::map<unsigned int, vector< std::pair<unsigned int, unsigned int> > > &decomposition) const
+{
+	decomposition.clear();
+	//TODO: Convert to cached, statified map?
+	for(size_t ui=0;ui<ionNames.size(); ui++)
+	{
+		vector<pair<string,size_t> > thisFragment;
+		if(!decomposeIonNames(ionNames[ui].first,thisFragment))
+			return false;
+
+		//convert the name,count pairs to  ionID, count pairs
+		vector<pair<unsigned int, unsigned int> > fragmentAsRanges;
+		fragmentAsRanges.resize(thisFragment.size());
+		
+		for(size_t uj=0;uj<fragmentAsRanges.size();uj++)
+		{	
+			unsigned int ionID;
+			ionID = getIonID(thisFragment[uj].first);
+			
+			if(ionID ==(unsigned int)-1)
+				return false;
+	
+			fragmentAsRanges[uj] = make_pair(ionID,thisFragment[uj].second);
+		}
+		decomposition[ui] = fragmentAsRanges;
+	
+		fragmentAsRanges.clear();	
+	}
+
+	return true;
+}
diff --git a/src/backend/APT/APTRanges.h b/src/backend/APT/APTRanges.h
index a08ba92..f8dc33d 100644
--- a/src/backend/APT/APTRanges.h
+++ b/src/backend/APT/APTRanges.h
@@ -1,6 +1,6 @@
 /*
  * APTRanges.h - Atom probe rangefile class 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 
 #include <vector>
 #include <string>
+#include <map>
 
 
 #include "backend/APT/ionhit.h"
@@ -279,6 +280,18 @@ class RangeFile
 		//erase given ions and associated rnagefes)
 		void eraseIon(size_t ionId);
 
+		//can we decompose all composed ranges in this file into
+		// other components that already exist within this range?
+		bool isSelfDecomposable() const;
+		
+		//Generate a secondary rangefile with decomposable ranges as needed.	
+		bool decompose(RangeFile &rng) const;
+
+		//Obtain the decompsiition for this range. This maps composed ranges to their decomposed ones.
+		//  - This will perform a decompsition if needed, and return false if not self decomposable.
+		bool getDecomposition(
+			std::map<unsigned int, std::vector< std::pair< unsigned int, unsigned int > > > &decomposition) const;
+	
 		//Break a given string down into a series of substring-count pairs depicting basic ionic components
 		static bool decomposeIonNames(const std::string &name,
 
diff --git a/src/backend/APT/abundanceParser.cpp b/src/backend/APT/abundanceParser.cpp
index fba3917..6f095df 100644
--- a/src/backend/APT/abundanceParser.cpp
+++ b/src/backend/APT/abundanceParser.cpp
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/backend/APT/abundanceParser.h b/src/backend/APT/abundanceParser.h
index 8111c15..a8abaf1 100644
--- a/src/backend/APT/abundanceParser.h
+++ b/src/backend/APT/abundanceParser.h
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/backend/APT/ionhit.cpp b/src/backend/APT/ionhit.cpp
index f45f890..9641d73 100644
--- a/src/backend/APT/ionhit.cpp
+++ b/src/backend/APT/ionhit.cpp
@@ -1,6 +1,6 @@
 /*
  * ionhit.cpp - Ion event data class
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/backend/APT/ionhit.h b/src/backend/APT/ionhit.h
index e8cc552..1b73111 100644
--- a/src/backend/APT/ionhit.h
+++ b/src/backend/APT/ionhit.h
@@ -1,6 +1,6 @@
 /*
  * ionhit.h - Ion event data class
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/backend/animator.cpp b/src/backend/animator.cpp
index cde1f99..c0f10a2 100644
--- a/src/backend/animator.cpp
+++ b/src/backend/animator.cpp
@@ -1,6 +1,6 @@
 /*
  *	animatior.cpp - animation interopolator implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -19,7 +19,17 @@
 #include "animator.h"
 #include "common/stringFuncs.h"
 #include "common/basics.h"
+
 #include <map>
+#include <set>
+
+using std::string;
+using std::map;
+using std::vector;
+using std::pair;
+using std::make_pair;
+using std::endl;
+using std::set;
 
 const char *INTERP_NAME[] ={ "Step",
 	"Linear",
@@ -560,7 +570,7 @@ std::string InterpData::getInterpolatedData(const vector<pair<size_t,
 			//interpolate the colour value
 			ColourRGBAf interpCol;
 			float delta;
-			delta = (frame - startF )/ (endF - startF);
+			delta = (float)(frame - startF )/ (float)(endF - startF);
 			interpCol=tmpCol[0].toRGBAf().interpolate(delta,tmpCol[1].toRGBAf());
 			return interpCol.toColourRGBA().rgbaString();
 		}
diff --git a/src/backend/animator.h b/src/backend/animator.h
index 47d80d9..0eb9f1a 100644
--- a/src/backend/animator.h
+++ b/src/backend/animator.h
@@ -1,6 +1,6 @@
 /*
  *	animator.h - animation classes for 3Depict
- *	Copyright (C) 2013, D Haley
+ *	Copyright (C) 2015, D Haley
  
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ class InterpData
 		// for the properties given as a parameter.
 		// should only be called for frames that lie within the interpolated
 		// range
-		std::string getInterpolatedData(const vector<pair<size_t,
+		std::string getInterpolatedData(const std::vector<std::pair<size_t,
  					std::string>  > &keyData,size_t frame) const;
 
 		float interpLinearRamp(size_t startFrame, size_t endFrame, size_t curFrame,
@@ -67,7 +67,7 @@ class FrameProperties
 		//Property Key for filter
 		size_t propertyKey;
 		//!First in pair is frame offset, second is property at that frame
-		vector<pair<size_t,std::string> > frameData;
+		std::vector<std::pair<size_t,std::string> > frameData;
 
 		//!Interpolation information
 		InterpData interpData;
@@ -118,7 +118,7 @@ class PropertyAnimator
 	private:
 		//Vector containing each properties new
 		// value/key pairing
-		vector<FrameProperties> keyFrames;
+		std::vector<FrameProperties> keyFrames;
 	public:
 		PropertyAnimator();
 
@@ -132,8 +132,8 @@ class PropertyAnimator
 
 		//!Get all the properties that intersect or precede 
 		// a particular keyframe.
-		void getPropertiesAtFrame(size_t keyframe, vector<size_t> &propIds,
-			vector<FrameProperties> &props) const;
+		void getPropertiesAtFrame(size_t keyframe, std::vector<size_t> &propIds,
+			std::vector<FrameProperties> &props) const;
 
 		//Obtain the as-animated version of a specific filter for a particular frame.
 		// returns empty string if the filter ID/key is not known.
@@ -163,7 +163,7 @@ class PropertyAnimator
 		void removeNthKeyFrame(size_t frameNum);
 
 		//Remove the specified key frames. Input vector contents will be sorted.
-		void removeKeyFrames(vector<size_t> &vec);
+		void removeKeyFrames(std::vector<size_t> &vec);
 
 		//!Dump state to output stream, using specified format
 		/* Current supported formats are STATE_FORMAT_XML.
@@ -177,7 +177,7 @@ class PropertyAnimator
 
 
 		//!Obtain the complete listing of IDs used internally
-		void getIdList(vector<unsigned int> &ids) const;
+		void getIdList(std::vector<unsigned int> &ids) const;
 
 		//!Force the internal IDs for filters to a new value
 		void updateMappings(const std::map<size_t,size_t> &newMap);
diff --git a/src/backend/configFile.cpp b/src/backend/configFile.cpp
index b962cdc..ec87b9a 100644
--- a/src/backend/configFile.cpp
+++ b/src/backend/configFile.cpp
@@ -1,6 +1,6 @@
 /*
  *	configFile.cpp  - User configuration loading/saving
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@ const unsigned int MAX_MOUSE_PERCENT= 400;
 
 using std::endl;
 using std::string;
+using std::deque;
 
 
 ConfigFile::ConfigFile() : configLoadOK(false), panelMode(CONFIG_PANELMODE_REMEMBER),
diff --git a/src/backend/configFile.h b/src/backend/configFile.h
index ba13e67..9154517 100644
--- a/src/backend/configFile.h
+++ b/src/backend/configFile.h
@@ -1,6 +1,6 @@
 /*
  * configFile.h - Configuration file management header 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 #ifndef CONFIGFILE_H
 #define CONFIGFILE_H
 
+#include <deque>
 
 #include "backend/filter.h"
 
@@ -51,13 +52,13 @@ class ConfigFile
 {
 	private:
 		std::deque<std::string> recentFiles;
-		vector<Filter *> filterDefaults;
+		std::vector<Filter *> filterDefaults;
 		
 		//!Did the configuration load from file OK?
 		bool configLoadOK;
 		
 		//!Panel 
-		vector<bool> startupPanelView;
+		std::vector<bool> startupPanelView;
 
 		//!Any errors that occur during file IO. Set by class members during read()/write()
 		std::string errMessage;
@@ -117,9 +118,9 @@ class ConfigFile
 		static unsigned int getMaxHistory();
 
 		//Get a vector of the default filter pointers
-		void getFilterDefaults(vector<Filter* > &defs);
+		void getFilterDefaults(std::vector<Filter* > &defs);
 		//Set the default filter pointers (note this will take ownership of the pointer)
-		void setFilterDefaults(const vector<Filter* > &defs);
+		void setFilterDefaults(const std::vector<Filter* > &defs);
 
 		//Get a clone of the default filter for a given type,
 		//even if it is not in the array (use hardcoded)
diff --git a/src/backend/filter.cpp b/src/backend/filter.cpp
index 8f0f15b..52dddd7 100644
--- a/src/backend/filter.cpp
+++ b/src/backend/filter.cpp
@@ -1,6 +1,6 @@
 /*
- *	filter.h - modular data filter implementation 
- *	Copyright (C) 2013, D Haley 
+ *	filter.cpp - modular data filter implementation 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -24,7 +24,10 @@
 
 #include "wx/wxcomponents.h"
 
+#include "common/voxels.h"
+
 #include <set>
+#include <deque>
 
 #ifdef _OPENMP
 #include <omp.h>
@@ -35,11 +38,14 @@ using std::vector;
 using std::string;
 using std::pair;
 using std::make_pair;
+using std::endl;
+using std::deque;
 
 
 
 
 bool Filter::strongRandom= false;
+ATOMIC_BOOL *Filter::wantAbort= 0;
 
 const char *STREAM_NAMES[] = { NTRANS("Ion"),
 				NTRANS("Plot"),
@@ -148,6 +154,25 @@ void Filter::cacheAsNeeded(FilterStreamData *stream)
 	}
 }
 
+std::string Filter::getErrString(unsigned int errCode) const
+{
+	//First see if we have a generic error code, before attempting to
+	// switch to a specific error code
+	std::string errString;
+	errString = getBaseErrString(errCode);
+	if(!errString.empty())
+		return errString;
+	return this->getSpecificErrString(errCode);
+}
+
+//If we recognise a base error string, check that
+std::string Filter::getBaseErrString(unsigned int errCode)
+{
+	if(errCode == FILTER_ERR_ABORT)
+		return TRANS("Aborted");
+
+	return string("");
+}
 
 #ifdef DEBUG
 bool FilterProperty::checkSelfConsistent() const
@@ -324,7 +349,7 @@ void FilterPropGroup::checkConsistent() const
 	ASSERT(groupNames.size() ==groupCount);
 
 
-	//check that each group ahas a name
+	//check that each group has a name
 	for(size_t ui=0;ui<groupNames.size(); ui++)
 	{
 		ASSERT(!groupNames[ui].empty())
@@ -333,10 +358,6 @@ void FilterPropGroup::checkConsistent() const
 #endif
 
 
-void VoxelStreamData::clear()
-{
-	data.clear();
-}
 
 void DrawStreamData::clear()
 {
@@ -483,6 +504,10 @@ void PlotStreamData::checkSelfConsistent() const
 Plot2DStreamData::Plot2DStreamData()
 {
 	streamType=STREAM_TYPE_PLOT2D;
+	r=g=0.0f;
+	b=a=1.0f;
+
+	scatterIntensityLog=false;
 }
 
 size_t Plot2DStreamData::getNumBasicObjects() const
@@ -502,12 +527,13 @@ size_t Plot2DStreamData::getNumBasicObjects() const
 void Plot2DStreamData::checkSelfConsistent() const
 {
 	//only using scatter or xy, not both
-	ASSERT(!(xyData.empty() && scatterData.empty()));
+	ASSERT(xorFunc(xyData.empty(), scatterData.empty()));
 
 	//no intensity without data
 	if(scatterData.empty())
 		ASSERT(scatterIntensity.empty());
 
+
 	ASSERT(plotType < PLOT_TYPE_ENUM_END);
 }
 void RangeStreamData::checkSelfConsistent() const
@@ -530,6 +556,47 @@ FilterStreamData::FilterStreamData(const Filter  *theParent) : parent(theParent)
 {
 }
 
+
+unsigned int IonStreamData::exportStreams(const std::vector<const FilterStreamData * > &selectedStreams,
+		const std::string &outFile, unsigned int format)
+{
+
+	//test file open, and truncate file to zero bytes
+	std::ofstream f(outFile.c_str(),std::ios::trunc);
+	
+	if(!f)
+		return 1;
+
+	f.close();
+
+	for(unsigned int ui=0; ui<selectedStreams.size(); ui++)
+	{
+		switch(selectedStreams[ui]->getStreamType())
+		{
+			case STREAM_TYPE_IONS:
+			{
+				const IonStreamData *ionData;
+				ionData=((const IonStreamData *)(selectedStreams[ui]));
+				switch(format)
+				{
+					case IONFORMAT_POS:
+					{
+						//Append this ion stream to the posfile
+						IonHit::appendPos(ionData->data,outFile.c_str());
+
+						break;
+					}
+					default:
+						ASSERT(false);
+						break;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
 IonStreamData::IonStreamData() : representationType(ION_REPRESENT_POINTS), 
 	r(1.0f), g(0.0f), b(0.0f), a(1.0f), 
 	ionSize(2.0f), valueType("Mass-to-Charge (amu/e)")
@@ -591,12 +658,30 @@ VoxelStreamData::VoxelStreamData() : representationType(VOXEL_REPRESENT_POINTCLO
 	r(1.0f),g(0.0f),b(0.0f),a(0.3f), splatSize(2.0f),isoLevel(0.5f)
 {
 	streamType=STREAM_TYPE_VOXEL;
+	data = new Voxels<float>;
 }
 
 VoxelStreamData::VoxelStreamData(const Filter *f) : FilterStreamData(f), representationType(VOXEL_REPRESENT_POINTCLOUD),
 	r(1.0f),g(0.0f),b(0.0f),a(0.3f), splatSize(2.0f),isoLevel(0.5f)
 {
 	streamType=STREAM_TYPE_VOXEL;
+	data = new Voxels<float>;
+}
+
+VoxelStreamData::~VoxelStreamData()
+{
+	if(data)
+		delete data;
+}
+
+size_t VoxelStreamData::getNumBasicObjects() const
+{
+	return data->getSize(); 
+}
+
+void VoxelStreamData::clear()
+{
+	data->clear();
 }
 
 RangeStreamData::RangeStreamData() : rangeFile(0)
@@ -711,7 +796,7 @@ void Filter::propagateStreams(const vector<const FilterStreamData *> &dataIn,
 
 unsigned int Filter::collateIons(const vector<const FilterStreamData *> &dataIn,
 				vector<IonHit> &outVector, ProgressData &prog,
-				bool (*callback)(bool),size_t totalDataSize)
+				size_t totalDataSize)
 {
 	if(totalDataSize==(size_t)-1)
 		totalDataSize=numElements(dataIn,STREAM_TYPE_IONS);
@@ -739,7 +824,7 @@ unsigned int Filter::collateIons(const vector<const FilterStreamData *> &dataIn,
 				for(size_t ui=0;ui<dataSize; ui++)
 					outVector[offset+ui]=d->data[ui];
 
-				if(!(*callback)(false))
+				if(Filter::wantAbort)
 					return FILTER_ERR_ABORT;
 				offset+=d->data.size();
 
@@ -755,7 +840,7 @@ unsigned int Filter::collateIons(const vector<const FilterStreamData *> &dataIn,
 
 void Filter::updateOutputInfo(const std::vector<const FilterStreamData *> &dataOut)
 {
-	//Reset the number ouf output streams to zero
+	//Reset the number of output streams to zero
 	for(unsigned int ui=0;ui<NUM_STREAM_TYPES;ui++)
 		numStreamsLastRefresh[ui]=0;
 	
@@ -788,6 +873,17 @@ void Filter::initFilter(const std::vector<const FilterStreamData *> &dataIn,
 	std::copy(dataIn.begin(),dataIn.end(),dataOut.begin());
 }
 
+ProgressData::ProgressData()
+{
+	step=0;
+	maxStep=0;
+	curFilter=0;
+	filterProgress=0;
+	totalProgress=0;
+	totalNumFilters=0;
+}
+
+
 bool ProgressData::operator==( const ProgressData &oth) const
 {
 	if(filterProgress!=oth.filterProgress ||
diff --git a/src/backend/filter.h b/src/backend/filter.h
index 81eb9cc..16a4a0c 100644
--- a/src/backend/filter.h
+++ b/src/backend/filter.h
@@ -1,6 +1,6 @@
 /*
  *	filter.h - Data filter header file. 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@ class ProgressData;
 class RangeFileFilter;
 
 #include "APT/ionhit.h"
+#include "APT/APTFileIO.h"
 
 #include "APT/APTRanges.h"
 #include "common/constants.h"
@@ -32,10 +33,12 @@ class RangeFileFilter;
 #include "gl/select.h"
 #include "gl/drawables.h"
 
-#include "common/voxels.h"
 #include "common/stringFuncs.h"
 #include "common/array2D.h"
 
+template<typename T>
+class Voxels;
+
 //ifdef inclusion as there is some kind of symbol clash...
 #ifdef ATTRIBUTE_PRINTF
 	#pragma push_macro("ATTRIBUTE_PRINTF")
@@ -47,6 +50,7 @@ class RangeFileFilter;
 #endif
 
 
+
 #include <wx/propgrid/propgrid.h>
 
 const unsigned int NUM_CALLBACK=50000;
@@ -74,6 +78,7 @@ enum
 	FILTER_TYPE_ENUM_END // not a filter. just end of enum
 };
 
+
 extern const char *FILTER_NAMES[];
 
 
@@ -142,17 +147,17 @@ enum
 };
 
 
+//!Generic filter error codes
 enum
 {
-	FILTER_ERR_ABORT=1, //User abort
-	FILTER_ERR_ENUM_END
+	FILTER_ERR_ABORT = 1000000,
 };
 
 //---
 //
 
 //!Return the number of elements in a vector of filter data - i.e. the sum of the number of objects within each stream. Only masked streams (STREAM_TYPE_*) will be counted
-size_t numElements(const vector<const FilterStreamData *> &vm, unsigned int mask=STREAMTYPE_MASK_ALL);
+size_t numElements(const std::vector<const FilterStreamData *> &vm, unsigned int mask=STREAMTYPE_MASK_ALL);
 
 
 
@@ -246,7 +251,7 @@ class FilterPropGroup
 				groupCount=0;}
 
 		//!Grab all properties from the specified group
-		void getGroup(size_t group, vector<FilterProperty> &groupVec) const;
+		void getGroup(size_t group, std::vector<FilterProperty> &groupVec) const;
 		
 		//Confirm a particular group exists
 		bool hasGroup(size_t group) const;
@@ -286,6 +291,10 @@ public:
 	
 	//!Apply filter to input data stream	
 	std::vector<IonHit> data;
+
+	//!export given filterstream data pointers as ion data
+	static unsigned int exportStreams(const std::vector<const FilterStreamData *> &selected, 
+							const std::string &outFile, unsigned int format=IONFORMAT_POS);
 };
 
 //!Point with m-t-c value data
@@ -293,8 +302,9 @@ class VoxelStreamData : public FilterStreamData
 {
 public:
 	VoxelStreamData();
+	~VoxelStreamData();
 	VoxelStreamData( const Filter *f);
-	size_t getNumBasicObjects() const { return data.getSize();};
+	size_t getNumBasicObjects() const ;
 	void clear();
 	
 	unsigned int representationType;
@@ -302,7 +312,7 @@ public:
 	float splatSize;
 	float isoLevel;
 	//!Apply filter to input data stream	
-	Voxels<float> data;
+	Voxels<float> *data;
 		
 };
 
@@ -343,13 +353,13 @@ class PlotStreamData : public FilterStreamData
 		//!XY data pairs for plotting curve
 		std::vector<std::pair<float,float> > xyData;
 		//!Rectangular marked regions
-		vector<std::pair<float,float> > regions;
-		vector<string> regionTitle;
+		std::vector<std::pair<float,float> > regions;
+		std::vector<std::string> regionTitle;
 		//!Region colours
-		vector<float> regionR,regionB,regionG;
+		std::vector<float> regionR,regionB,regionG;
 
 		//!Region indices from parent region
-		vector<unsigned int> regionID;
+		std::vector<unsigned int> regionID;
 
 		//!Region parent filter pointer, used for matching interaction 
 		// with region to parent property
@@ -392,12 +402,17 @@ class Plot2DStreamData : public FilterStreamData
 		Array2D<float> xyData;
 		//Only rqeuired for xy plots
 		float xMin,xMax,yMin,yMax;
-		
+	
+
+		float r,g,b,a;
+	
 		//!Unstructured XY points
-		vector<pair<float,float> > scatterData;
+		std::vector<std::pair<float,float> > scatterData;
 		//optional intensity data for scatter plots
-		vector<float> scatterIntensity;
+		std::vector<float> scatterIntensity;
 	
+		//Do we want to plot the scatter intensity in lgo mode?
+		bool scatterIntensityLog;	
 
 		//!Parent filter index
 		unsigned int index;
@@ -412,7 +427,7 @@ class DrawStreamData: public FilterStreamData
 {
 	public:
 		//!Vector of 3D objects to draw.
-		vector<DrawableObj *> drawables;
+		std::vector<DrawableObj *> drawables;
 		//!constructor
 		DrawStreamData(){ streamType=STREAM_TYPE_DRAW;};
 		DrawStreamData(const Filter *f){ streamType=STREAM_TYPE_DRAW;};
@@ -438,9 +453,9 @@ class RangeStreamData :  public FilterStreamData
 		//it merely provides access to existing data.
 		RangeFile *rangeFile;
 		//Enabled ranges from source filter
-		vector<char> enabledRanges;
+		std::vector<char> enabledRanges;
 		//Enabled ions from source filter 
-		vector<char> enabledIons;
+		std::vector<char> enabledIons;
 
 		
 		//!constructor
@@ -470,13 +485,15 @@ class Filter
 		static bool strongRandom;
 
 
+
 		//!Array of the number of streams propagated on last refresh
 		//This is initialised to -1, which is considered invalid
 		unsigned int numStreamsLastRefresh[NUM_STREAM_TYPES];
 	
 
-		//!temporary console output. Should be only nonzero size immediately after refresh
-		vector<string> consoleOutput;
+		//!Temporary console output. Should be only nonzero size if messages are present
+		//after refresh, until cache is cleared
+		std::vector<std::string> consoleOutput;
 		//!User settable labelling string (human readable ID, etc etc)
 		std::string userString;
 		//Filter output cache
@@ -487,16 +504,15 @@ class Filter
 
 
 		//Collate ions from filterstream data into an ionhit vector
-		static unsigned int collateIons(const vector<const FilterStreamData *> &dataIn,
-				vector<IonHit> &outVector, ProgressData &prog,
-				bool (*callback)(bool),size_t totalDataSize=(size_t)-1);
+		static unsigned int collateIons(const std::vector<const FilterStreamData *> &dataIn,
+				std::vector<IonHit> &outVector, ProgressData &prog, size_t totalDataSize=(size_t)-1);
 
 		//!Propagate the given input data to an output vector
-		static void propagateStreams(const vector<const FilterStreamData *> &dataIn,
-				vector<const FilterStreamData *> &dataOut,size_t mask=STREAMTYPE_MASK_ALL,bool invertMask=false) ;
+		static void propagateStreams(const std::vector<const FilterStreamData *> &dataIn,
+				std::vector<const FilterStreamData *> &dataOut,size_t mask=STREAMTYPE_MASK_ALL,bool invertMask=false) ;
 
 		//!Propagate the cache into output
-		void propagateCache(vector<const FilterStreamData *> &dataOut) const;
+		void propagateCache(std::vector<const FilterStreamData *> &dataOut) const;
 
 		//Set a property, without any checking of the new value 
 		// -clears cache on change
@@ -509,10 +525,20 @@ class Filter
 		//place a stream object into the filter cache, if required
 		// does not place object into filter output - you need to do that yourself
 		void cacheAsNeeded(FilterStreamData *s); 
+
+		//!Get the generic (applies to any filter) error codes
+		static std::string getBaseErrString(unsigned int errCode);
+
+		//!Get the per-filter error codes
+		virtual std::string getSpecificErrString(unsigned int errCode) const=0;
+
+	
 	public:	
 		Filter() ;
 		virtual ~Filter();
-		
+		//Abort pointer . This must be  nonzero during filter refreshes
+		static ATOMIC_BOOL *wantAbort;
+
 		//Pure virtual functions
 		//====
 		//!Duplicate filter contents, excluding cache.
@@ -521,7 +547,7 @@ class Filter
 		//!Apply filter to new data, updating cache as needed. Vector of returned pointers must be deleted manually, first checking ->cached.
 		virtual unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 				std::vector<const FilterStreamData *> &dataOut,
-				ProgressData &progress, bool (*callback)(bool)) =0;
+				ProgressData &progress ) =0;
 		//!Erase cache
 		virtual void clearCache();
 
@@ -548,8 +574,8 @@ class Filter
 		virtual bool setProperty(unsigned int key,
 			const std::string &value, bool &needUpdate) = 0;
 
-		//!Get the human readable error string associated with a particular error code during refresh(...)
-		virtual std::string getErrString(unsigned int code) const =0;
+		//!Get the human readable error string associated with a particular error code during refresh(...). Do *not* override this for specific filter errors. Override getSpecificErrString
+		std::string getErrString(unsigned int code) const;
 
 		//!Dump state to output stream, using specified format
 		/* Current supported formats are STATE_FORMAT_XML
@@ -580,7 +606,7 @@ class Filter
 		//====
 	
 		//!Return the unique name for a given filter -- DO NOT TRANSLATE	
-		string trueName() const { return FILTER_NAMES[getType()];};
+		std::string trueName() const { return FILTER_NAMES[getType()];};
 
 
 
@@ -592,7 +618,7 @@ class Filter
 
 		//!Return the XML elements that refer to external entities (i.e. files) which do not move with the XML file
 		//Each element is to be referred to using "/" as entity separator, for the first pair element, and the attribute name for the second.
-		virtual void getStateOverrides(std::vector<string> &overrides) const {}; 
+		virtual void getStateOverrides(std::vector<std::string> &overrides) const {}; 
 
 		//!Enable/disable caching for this filter
 		void setCaching(bool enableCache) {cache=enableCache;};
@@ -619,7 +645,7 @@ class Filter
 		 * another at this level (for example setting two devices on one primitve,
 		 * with the same mouse/key bindings). So dont do that.
 		 */
-		void getSelectionDevices(vector<SelectionDevice *> &devices) const;
+		void getSelectionDevices(std::vector<SelectionDevice *> &devices) const;
 
 
 		//!Update the output statistics for this filter (num items of streams of each type output)
@@ -652,9 +678,12 @@ class Filter
 
 		//Can we be a useful filter, even if given no input specified by the Use mask?
 		virtual bool isUsefulAsAppend() const { return false;}
+	
+		template<typename T>	
+		static void getStreamsOfType(const std::vector<const FilterStreamData *> &vec, std::vector<const T *> &dataOut);
 #ifdef DEBUG
 		//!Run all the registered unit tests for this filter
-		virtual bool runUnitTests() { cerr << "No test for " << typeString() << endl; return true;} ;
+		virtual bool runUnitTests() { std::cerr << "No test for " << typeString() << std::endl; return true;} ;
 		//!Is the filter caching?
 		bool cacheEnabled() const {return cache;};
 
@@ -665,6 +694,17 @@ class Filter
 
 };
 
+template<typename T>
+void Filter::getStreamsOfType(const std::vector<const FilterStreamData *> &vec, std::vector<const T *> &dataOut)
+{
+	T dummyInstance;
+	for(size_t ui=0;ui<vec.size() ; ui++)
+	{
+		if(vec[ui]->getStreamType() == dummyInstance.getStreamType())
+			dataOut.push_back((const T*)vec[ui]);
+	}
+}
+
 //Template specialisations & def for  applyPropertyNow
 //--
 template<>
@@ -718,12 +758,14 @@ class ProgressData
 		//!Maximum steps
 		unsigned int maxStep;
 		
-		//!Pointer to the current filter that is being updated. Only valid during an update callback
+		//!Pointer to the current filter that is being updated. 
 		const Filter *curFilter;
 
 		//!Name of current operation, if specified
 		std::string stepName;
 
+		ProgressData(); 
+
 		bool operator==(const ProgressData &o) const;
 		const ProgressData &operator=(const ProgressData &o);
 
diff --git a/src/backend/filters/algorithms/K3DTree-mk2.cpp b/src/backend/filters/algorithms/K3DTree-mk2.cpp
index 84ca087..c8bb50d 100644
--- a/src/backend/filters/algorithms/K3DTree-mk2.cpp
+++ b/src/backend/filters/algorithms/K3DTree-mk2.cpp
@@ -1,6 +1,6 @@
 /* 
  * K3DTree-mk2.cpp : 3D Point KD tree - precise implementation 
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,10 @@ using std::stack;
 using std::vector;
 using std::pair;
 
+unsigned int *K3DTreeMk2::progress=0;
+//Pointer for aborting during build process
+ATOMIC_BOOL *K3DTreeMk2::abort=0;
+
 void K3DTreeMk2::resetPts(std::vector<Point3D> &p, bool clear)
 {
 	//Compute bounding box for indexedPoints
@@ -106,10 +110,10 @@ size_t K3DTreeMk2::size() const
 	return indexedPoints.size();
 }
 
-bool K3DTreeMk2::build(bool wantCallback)
+bool K3DTreeMk2::build()
 {
-
-	const size_t PROGRESS_REDUCE=5000;
+	ASSERT(progress); // Check progress pointer is inited
+	ASSERT(abort); //Check abort pointer is initialised
 
 	using std::make_pair;
 
@@ -263,16 +267,20 @@ bool K3DTreeMk2::build(bool wantCallback)
 			}
 		}	
 
-		if(wantCallback && !(numSeen%PROGRESS_REDUCE) && progress)
-		{
-			*progress= (unsigned int)((float)numSeen/(float)nodes.size()*100.0f);
+		*progress= (unsigned int)((float)numSeen/(float)nodes.size()*100.0f);
 
-			if(!(*callback)(false))
-				return false;
-		}
+		if(*abort)
+			return false;
 
 	}while(!limits.empty());
 
+#ifdef DEBUG
+	for(unsigned int ui=0;ui<nodes.size();ui++)
+	{
+		ASSERT(nodes[ui].childLeft != (size_t)-2); 
+		ASSERT(nodes[ui].childRight != (size_t)-2); 
+	}
+#endif
 
 	return true;
 }
@@ -574,8 +582,8 @@ size_t K3DTreeMk2::findNearestUntagged(const Point3D &searchPt,
 			case NODE_THIRD_VISIT:
 			{
 				//Decide if we should promote the current node
-				//to "best" (ie nearest untagged) node.
-				//To promote, it musnt be tagged, and it must
+				//to "best" (i.e. nearest untagged) node.
+				//To promote, it mustn't be tagged, and it must
 				//be closer than cur best estimate.
 				if(!nodes[curNode].tagged)
 				{
@@ -618,7 +626,7 @@ size_t K3DTreeMk2::findNearestUntagged(const Point3D &searchPt,
 		}
 		
 
-	//Keep going until we meet the root nde for the third time (one left, one right, one finish)	
+	//Keep going until we meet the root node for the third time (one left, one right, one finish)	
 	}while(!(curNode== startNode &&  visit== NODE_THIRD_VISIT));
 
 	if(bestPoint != (size_t) -1)
@@ -826,8 +834,9 @@ bool K3DMk2Tests()
 	pts.push_back(Point3D(0,0,0));
 	tree.resetPts(pts,false);
 
-	//build, but do not give progress
-	tree.build(false);
+
+	//build 
+	TEST(tree.build(),"Tree build");
 	
 	Point3D searchPt=Point3D(1,0,0);
 	BoundCube dummyCube;
@@ -852,7 +861,7 @@ bool K3DMk2Tests()
 	pts.push_back(Point3D(1.1,0.9,0.95));
 	
 	tree.resetPts(pts,false);
-	tree.build(false);
+	TEST(tree.build(),"Tree build");
 	
 	testBox.setBounds(Point3D(1.05,0.5,0.5),Point3D(1.5,1.5,1.5));
 	TEST(tree.getBoxInTree(testBox)==2,"subtree test pt2");
diff --git a/src/backend/filters/algorithms/K3DTree-mk2.h b/src/backend/filters/algorithms/K3DTree-mk2.h
index 1379ae7..e570689 100644
--- a/src/backend/filters/algorithms/K3DTree-mk2.h
+++ b/src/backend/filters/algorithms/K3DTree-mk2.h
@@ -1,6 +1,6 @@
 /* 
  * K3DTreeMk2.h  - Precise KD tree implementation
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -83,10 +83,8 @@ class K3DTreeMk2
 
 		BoundCube treeBounds;
 		
-		//Callback for progress reporting
-		bool (*callback)(bool);
-
-		unsigned int *progress; //Progress counter
+		static unsigned int *progress; //Progress counter
+		static ATOMIC_BOOL *abort; //set to true if build should abort. Must be initalised prior to build
 
 	public:
 		//KD Tree constructor
@@ -95,15 +93,25 @@ class K3DTreeMk2
 		//!Cleans up tree, deallocates nodes
 		~K3DTreeMk2(){};
 
+		static void setProgressPtr(unsigned int *ptr){progress=ptr;}
+		static void setAbortFlag(ATOMIC_BOOL *ptr){abort=ptr;}
+
 		//Reset the points
 		void resetPts(std::vector<Point3D> &pts, bool clear=true);
 		void resetPts(std::vector<IonHit> &pts, bool clear=true);
+	
+	
+		//Set a pointer that can be used to indicate that we need to abort build
+		static void setAbortPtr(bool *abortFlag);
+		//Set a pointer that can be used to write the current progress
+		static void setProgressPointer(unsigned int *p) ;
+		
 
 		/*! Builds a balanced KD tree from a list of points
-		 *  previously set by "resetPts". returns false if callback returns
+		 *  previously set by "resetPts". returns false if abort checks return
 		 *  false;
 		 */	
-		bool build(bool wantCallback=true);
+		bool build();
 
 		void getBoundCube(BoundCube &b);
 
@@ -143,11 +151,6 @@ class K3DTreeMk2
 
 		size_t getOrigIndex(size_t treeIndex) const ;
 		
-		//Set the callback routine for progress reporting
-		void setCallback(bool (*cb)(bool)) {callback = cb;}
-		
-		//Set a pointer that can be used to write the current progress
-		void setProgressPointer(unsigned int *p) { progress=p;};
 
 		//Erase tree contents
 		void clear() { nodes.clear(); indexedPoints.clear();};
diff --git a/src/backend/filters/algorithms/K3DTree.cpp b/src/backend/filters/algorithms/K3DTree.cpp
index 4e6cfe0..a409940 100644
--- a/src/backend/filters/algorithms/K3DTree.cpp
+++ b/src/backend/filters/algorithms/K3DTree.cpp
@@ -1,6 +1,6 @@
 /* 
  * K3DTree.cpp : 3D Point KD tree 
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,10 @@
 
 using std::vector;
 
+unsigned int *K3DTree::progress=0;
+//Pointer for aborting during build process
+const ATOMIC_BOOL *K3DTree::abort=0;
+
 //Axis compare
 //==========
 AxisCompare::AxisCompare() : axis(0)
@@ -87,7 +91,7 @@ void K3DNode::dump(std::ostream &strm, unsigned int depth) const
 
 //K3D Tree
 //=============
-K3DTree::K3DTree() : treeSize(0),maxDepth(0),root(0), callback(0),progress(0)
+K3DTree::K3DTree() : treeSize(0),maxDepth(0),root(0)
 {
 }
 
@@ -180,7 +184,7 @@ K3DTree::~K3DTree()
 			}
 		}
 		
-	//Keep going until we meet the root nde for the third time (one left, one right, one finish)	
+	//Keep going until we meet the root node for the third time (one left, one right, one finish)	
 	}while(!(curNode==root &&  visit== 2));
 
 	std::cerr << "===COMPARE===" << std::endl;
@@ -207,6 +211,10 @@ void K3DTree::kill()
 //Build the KD tree
 void K3DTree::build(vector<Point3D> pts)
 {
+
+	ASSERT(progress); // Check progress pointer is inited
+	ASSERT(abort); //Check abort pointer is initialised
+
 	//che. to see if the pts vector is empty
 	if(!pts.size())
 	{
@@ -270,28 +278,29 @@ K3DNode *K3DTree::buildRecurse(vector<Point3D>::iterator pts_start, vector<Point
 	if(median)
 	{
 		
-		//Only do process if callback is OK
-		if((*callback)(false))
+		//Abort recursion if we need to abort
+		if(*abort)
+			node->setLeft(0);
+		else
 		{
+			//process data as per normal
 			node->setLeft(buildRecurse(pts_start,pts_start + median,depth+1));
 			*progress= (unsigned int)((float)curNodeCount/(float)treeSize*100.0f);
 		}
-		else 
-			node->setLeft(0);
 	}
 	else
 		node->setLeft(0);	
 
 	if(median!=ptsSize)
 	{
-		//Only do process if callback is OK
-		if((*callback)(false))
+		//Only do process if not aborting
+		if(*abort)
+			node->setRight(0);
+		else
 		{
 			node->setRight(buildRecurse(pts_start + median + 1, pts_end,depth+1));
 			*progress= (unsigned int)((float)curNodeCount/(float)treeSize*100.0f);
 		}
-		else
-			node->setRight(0);
 
 	}
 	else
@@ -539,13 +548,13 @@ void K3DTree::findKNearest(const Point3D &searchPt, const BoundCube &domainCube,
 				float deadDistSqr) const
 {
 	//find the N nearest points
-	float sqrDist;
 	bestPts.clear();
 	bestPts.reserve(num);
 
-	const Point3D *p;
 	for(unsigned int ui=0; ui<num; ui++)
 	{
+		const Point3D *p;
+		float sqrDist;
 		p= findNearest(searchPt, domainCube,
 						deadDistSqr);
 
diff --git a/src/backend/filters/algorithms/K3DTree.h b/src/backend/filters/algorithms/K3DTree.h
index 562ba49..8285083 100644
--- a/src/backend/filters/algorithms/K3DTree.h
+++ b/src/backend/filters/algorithms/K3DTree.h
@@ -1,6 +1,6 @@
 /* 
  * K3DTree.h - limited precision KD tree implementation
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@ class AxisCompare;
 class K3DTree;
 
 
+
 //!Functor allowing for sorting of points in 3D
 /*! Used by KD Tree to sort points based around which splitting axis is being used
  * once the axis is set, points will be ranked based upon their relative value in
@@ -100,10 +101,10 @@ class K3DTree
 		K3DNode *buildRecurse(std::vector<Point3D>::iterator pts_start,
 			       	std::vector<Point3D>::iterator pts_end, unsigned int depth );
 		
-		bool (*callback)(bool);
-
-		unsigned int *progress; //Progress counter
 		size_t curNodeCount; //Counter for build operations
+
+		static unsigned int *progress; //Progress counter
+		static const ATOMIC_BOOL *abort; //aborting flag
 	public:
 	
 		//KD Tree constructor
@@ -111,13 +112,10 @@ class K3DTree
 
 		//!Cleans up tree, deallocates nodes
 		~K3DTree();
-		
-		//Set the callback routine for progress reporting
-		void setCallbackMethod(bool (*cb)(bool)) {callback = cb;}
-		
-		void setProgressPointer(unsigned int *p) { progress=p;};
-	
 
+		static void setProgressPtr(unsigned int *ptr){progress=ptr;}
+		static void setAbortFlag(const ATOMIC_BOOL *ptr){abort=ptr;}
+	
 		/*! Builds a balanced KD tree from a list of points
 		 * This call is being passed by copy in order to prevent
 		 * re-ordering of the points. It may be worth having two calls
diff --git a/src/backend/filters/algorithms/binomial.cpp b/src/backend/filters/algorithms/binomial.cpp
index 5df28dd..f869c04 100644
--- a/src/backend/filters/algorithms/binomial.cpp
+++ b/src/backend/filters/algorithms/binomial.cpp
@@ -1,6 +1,6 @@
 /*
  *	binomial.cpp - Binomia distribution randomness testing
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -175,10 +175,10 @@ int countBinnedIons(const std::vector<IonHit> &ions, const RangeFile *rng,
 	Point3D lowBound;
 	totalBound.getBound(lowBound,0);
 
-	completedGridEntries.reserve((filteredIons.size()/segmentOptions.nIons)*0.5f);
+	completedGridEntries.reserve(((float)filteredIons.size()/segmentOptions.nIons)*0.5f);
 	for(size_t ui=0;ui<filteredIons.size(); ui++)
 	{	
-		//Find the X y division for hte ion
+		//Find the X y division for the ion
 		unsigned int xPos,yPos;
 		Point3D ionOffset;
 		ionOffset=filteredIons[ui].getPos() - lowBound;
@@ -209,7 +209,7 @@ int countBinnedIons(const std::vector<IonHit> &ions, const RangeFile *rng,
 		//Update grid end
 		gridEntries[binIdx].endPt[extrusionAxis]=ionOffset[extrusionAxis];
 
-		//Check to see if we need to finsh this grid entry
+		//Check to see if we need to finish this grid entry
 		if(gridEntries[binIdx].totalIons ==segmentOptions.nIons)
 		{
 #ifdef DEBUG
@@ -305,7 +305,7 @@ void genBinomialHistogram(const vector<GRID_ENTRY> &completedGridEntries,
 	normFreq.resize(mapIonFrequencies.size());
 	for(size_t ui=0;ui<mapIonFrequencies.size();ui++)
 	{
-		//For each type (vector entry), compute the compositon of each block
+		//For each type (vector entry), compute the composition of each block
 		map<unsigned int,unsigned int>::const_iterator it;
 		size_t total;
 		total=0;
@@ -313,7 +313,7 @@ void genBinomialHistogram(const vector<GRID_ENTRY> &completedGridEntries,
 		{
 			total+=it->second;
 		}
-		//Create the entries for the normalised frequency, watching fout for /= 0
+		//Create the entries for the normalised frequency, watching out for /= 0
 		for(it=mapIonFrequencies[ui].begin(); it!=mapIonFrequencies[ui].end();++it)
 		{
 			if(total)
@@ -655,9 +655,9 @@ bool testBinomialRandomnessTruePositive()
 		TEST(binStats.pValueOK[0],"Pvalue reported as correctly computed");
 		//Note that this next test is quite wide, as the pvalues are for 
 		// *two different observation underlying probabilities*.
-		//In one, the binomial prob is known (PVAL, eg =0.7), in the other we estimate it 
-		//from obsercvation - which has an error associated with it due to the 
-		//finite number of observations (eg pObs = 0.698). Chi-square is quite sensitive to this
+		//In one, the binomial prob is known (PVAL, e.g. =0.7), in the other we estimate it 
+		//from observation - which has an error associated with it due to the 
+		//finite number of observations (e.g. pObs = 0.698). Chi-square is quite sensitive to this
 		//difference.
 		TEST(fabs(binStats.pValue[0]-pValue)/pValue < 2.0,"cross-check pvalue computation");
 	}
diff --git a/src/backend/filters/algorithms/binomial.h b/src/backend/filters/algorithms/binomial.h
index b7eeb5e..c69c212 100644
--- a/src/backend/filters/algorithms/binomial.h
+++ b/src/backend/filters/algorithms/binomial.h
@@ -1,6 +1,6 @@
 /*
  *	binomial.h - Binomia distribution randomness testing
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/backend/filters/algorithms/mass.cpp b/src/backend/filters/algorithms/mass.cpp
new file mode 100644
index 0000000..bf4a32d
--- /dev/null
+++ b/src/backend/filters/algorithms/mass.cpp
@@ -0,0 +1,257 @@
+/*
+ *	mass.h - Algorithms for computing mass backgrounds 
+ *	Copyright (C) 2015, D Haley 
+
+ *	This program is free software: you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation, either version 3 of the License, or
+ *	(at your option) any later version.
+
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "mass.h"
+#include <common/assertion.h>
+
+using std::vector;
+
+//Background modes
+const char *BACKGROUND_MODE_STRING[FIT_MODE_ENUM_END] = {NTRANS("None"), 	
+					NTRANS("Flat TOF")};
+
+std::string getFitError(unsigned int errMsg) 
+{
+	ASSERT(errMsg < BACKGROUND_PARAMS::FIT_FAIL_END); 
+	const char * errorMsgs[BACKGROUND_PARAMS::FIT_FAIL_END] = {
+
+		NTRANS("INsufficient bins to perform fit"),
+		NTRANS("Insufficient counts to perform fit"),
+		NTRANS("Insufficient data to perform fit"),
+		NTRANS("Data did not appear to be random noise - cannot fit noise level")
+	};
+
+	return std::string(TRANS(errorMsgs[errMsg]));
+}
+
+//Make a linearly spaced histogram with the given spacings
+//TODO: Less lazy implementation
+void makeHistogram(const vector<float> &data, float start, 
+			float end, float step, vector<float> &histVals)
+{
+	ASSERT(start < end);
+	ASSERT(step > std::numeric_limits<float>::epsilon());
+
+	gsl_histogram *h = gsl_histogram_alloc((end-start)/step);
+	gsl_histogram_set_ranges_uniform(h,start,end);
+
+	for(size_t ui=0; ui<data.size();ui++)
+		gsl_histogram_increment(h,data[ui]);
+
+	//Copy out data
+	histVals.resize(h->n);
+	for(size_t ui=0;ui<h->n; ui++)
+		histVals[ui]=h->bin[ui];
+
+	gsl_histogram_free(h);
+}
+
+unsigned int doFitBackground(const vector<const FilterStreamData*> &dataIn, 
+	BACKGROUND_PARAMS &backParams)
+{
+	ASSERT(backParams.mode == FIT_MODE_CONST_TOF);
+
+	vector<const IonStreamData *> ionData;
+	Filter::getStreamsOfType(dataIn,ionData);
+
+	vector<float> sqrtFiltMass;
+	for(size_t ui=0;ui<ionData.size();ui++)
+	{
+		for(size_t uj=0;uj<ionData[ui]->data.size(); uj++)
+		{
+			float curMass;
+			curMass=ionData[ui]->data[uj].getMassToCharge();  
+			if( curMass >=backParams.massStart && curMass <= backParams.massEnd) 
+			{
+				sqrtFiltMass.push_back(sqrtf(curMass));
+			}
+		}	
+	}
+
+	//Minimum required counts per bin to have sufficient statistics
+	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; 
+
+	//we cannot perform a test with fewer than this number of bins
+	if ( nBins < MIN_REQUIRED_BINS)
+		return BACKGROUND_PARAMS::FIT_FAIL_MIN_REQ_BINS;
+
+	float averageCounts = sqrtFiltMass.size()/ (float)nBins; 
+	if( averageCounts < MIN_REQUIRED_AVG_COUNTS)
+		return BACKGROUND_PARAMS::FIT_FAIL_AVG_COUNTS; 
+
+	vector<float> histogram;
+	makeHistogram(sqrtFiltMass,sqrt(backParams.massStart),
+			sqrt(backParams.massEnd), filterStep,histogram);	
+
+	float andersonStat,meanVal;
+	size_t undefCount;
+	if(!andersonDarlingStatistic(histogram,meanVal,backParams.stdev,andersonStat, undefCount))
+		//TODO: Error message regarding fit failure
+		return BACKGROUND_PARAMS::FIT_FAIL_INSUFF_DATA;
+
+	//Rejection threshold for Anderson statistic 
+	// - either we didn't have enough samples,
+	// - or we failed the null hypothesis test of Gaussian-ness
+	// Rejection of null hypothesis at 99% confidence occurs at 1.092 [NIST].
+	// we use much more than this, in case batch processing/familywise error is present
+	// two slightly overlapping Gaussians can trigger at the 1.8 level
+	const float STATISTIC_THRESHOLD=3.0;
+	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;
+}
+
+#ifdef DEBUG
+#include "common/mathfuncs.h"
+
+bool testAnderson()
+{
+	//Generate some normal random numbers
+	RandNumGen rng;
+	rng.initialise(12345);
+	//Test to see if they are normal.
+	vector<float> data;
+	data.resize(30);
+
+	for(size_t ui=0;ui<data.size();ui++)
+	{
+		data[ui]=rng.genGaussDev();
+	}
+
+	//Anderson test should pass, or something is probably wrong.
+	float s,meanV,stdV;
+	size_t undefcount;
+	if(!andersonDarlingStatistic(data,meanV,stdV,s,undefcount) || s > 2.0f)
+	{
+		ASSERT(false);
+		return false;
+	}
+
+	//check anderson statistic
+	TEST(s >=0 && s < 1.5,"Anderson gauss test statistic");
+
+	TEST(EQ_TOLV(meanV,0.0f,0.2f),"Gaussian mean");
+	TEST(EQ_TOLV(stdV,1.0f,0.2f),"Gaussian mean");
+
+	return true;
+}
+
+bool testBackgroundFit()
+{
+	RandNumGen rng;
+	rng.initTimer();
+	//make some random data which is flat in TOF space
+	// then convert to m/c space
+	IonStreamData *ionData;
+	
+	ionData = new IonStreamData;
+
+	const unsigned int NUM_IONS =10000;
+	const float SIMULATED_INTENSITY= 100.0f;
+	
+	//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};	
+	
+	vector<float> rawData;
+	ionData->data.resize(NUM_IONS);
+	rawData.resize(NUM_IONS);
+	for(size_t ui=0;ui<NUM_IONS; ui++)
+	{
+		float simTof;
+		simTof = rng.genUniformDev()*(TOF_LIMIT[1]-TOF_LIMIT[0] ) + TOF_LIMIT[0];  
+		ionData->data[ui]= IonHit(Point3D(0,0,0),simTof);
+		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.
+	//---
+
+	//compute the mass histogram numerically
+	vector<float> massData;
+	massData.resize(NUM_IONS);
+	for(size_t ui=0;ui<NUM_IONS;ui++)
+		massData[ui] = sqrt(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);	
+
+	//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());
+
+	//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++)
+	{
+		float midV;
+		midV = massHist[ui] + histogramRes[ui];
+		midV*=0.5f;
+		float errorFraction;
+		errorFraction= fabs((massHist[ui] - histogramRes[ui])/midV);
+		//ASSERT(errorFraction < 0.5f);
+	}	
+	//---
+
+	return true;	
+ }
+
+#endif
diff --git a/src/backend/filters/algorithms/mass.h b/src/backend/filters/algorithms/mass.h
new file mode 100644
index 0000000..41b5828
--- /dev/null
+++ b/src/backend/filters/algorithms/mass.h
@@ -0,0 +1,200 @@
+/*
+ *	mass.h - Algorithms for computing mass backgrounds 
+ *	Copyright (C) 2015, D Haley 
+
+ *	This program is free software: you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation, either version 3 of the License, or
+ *	(at your option) any later version.
+
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef MASS_H
+#define MASS_H
+
+#include <vector>
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include <gsl/gsl_sf_erf.h>
+#include <gsl/gsl_histogram.h>
+
+#include "../../filter.h"
+
+enum
+{
+	FIT_MODE_NONE,
+	FIT_MODE_CONST_TOF,
+	FIT_MODE_ENUM_END,
+};
+
+//Matching strings for FIT_MODE enum. ENUM_END does not have a string
+extern const char *BACKGROUND_MODE_STRING[FIT_MODE_ENUM_END];
+
+
+
+struct BACKGROUND_PARAMS
+{
+	enum
+	{
+		FIT_FAIL_MIN_REQ_BINS=1,
+		FIT_FAIL_AVG_COUNTS,
+		FIT_FAIL_INSUFF_DATA,
+		FIT_FAIL_DATA_NON_GAUSSIAN,
+		FIT_FAIL_END
+	};
+	//background fitting mode to use
+	unsigned int mode;
+	//-- start/end window for const tof background fit
+	float massStart, massEnd;
+	//step size in bins for fitting histogram
+	float binWidth;
+
+	//result parameters
+	// for FIT_MODE_CONST_TOF, this uses 
+	float intensity,stdev;
+};
+
+template<typename T>
+void meanAndStdev(const std::vector<T > &f,float &meanVal, 
+			float &stdevVal,bool normalCorrection=true)
+{
+	meanVal=0;
+	for(size_t ui=0;ui<f.size();ui++)
+		meanVal+=f[ui];
+	meanVal /=f.size();
+	
+	stdevVal=0;
+	for(size_t ui=0;ui<f.size();ui++)
+	{
+		float delta;
+		delta=f[ui]-meanVal;
+		stdevVal+=delta*delta;
+	}
+	stdevVal = sqrtf( stdevVal/float(f.size()-1));
+
+	//Perform bias correction, assuming the input data is normally distributed
+	if(normalCorrection)
+	{
+		float n=f.size();
+		//Approximation to C4 = sqrt(2/(n-1))*gamma(n/2)/gamma((n-1)/2)
+		// multiplier must be applied to 1/(n-1) normalised standard deviation
+		// Citation: 
+		//	http://support.sas.com/documentation/cdl/en/qcug/63922/HTML/default/qcug_functions_sect007.htm
+		//	https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation
+		stdevVal*=(1.0 - 1.0/(4.0*n) - 7.0/(32.0*n*n) - 19.0/(128.0*n*n*n));
+	}
+}
+
+//Perform a background fit
+//	- background params has the input and output data for the fit.
+//	- dataIn requires ion data for a successful fit
+//	- returns zero on success, nonzero on error`
+unsigned int doFitBackground(const std::vector<const FilterStreamData*> &dataIn, BACKGROUND_PARAMS &params) ;
+
+
+//Anderson. test statistic for gaussian-ness. Returns false if input has insufficient points for test (2 items)
+//Implented for unknown (derived from data) mean & variance
+// reject statistic if output has this prob. of non-normality:
+// 15% - 0.576
+// 10% - 0.656
+//  5% - 0.787
+//2.5% - 0.918
+//  1% - 1.092
+//See, eg 
+// http://itl.nist.gov/div898/handbook/eda/section3/eda35e.htm
+template<class T>
+bool andersonDarlingStatistic(std::vector<T> vals, float &meanV, float &stdevVal, 
+		float &statistic, size_t &undefCount, bool computeMeanAndStdev=true)
+{
+	size_t n=vals.size();
+	//we cannot compute this without more data
+	if(n <= 1)
+		return false;
+
+	if(computeMeanAndStdev)
+		meanAndStdev(vals,meanV,stdevVal);
+
+	//Bring assumed gauss data into a normal dist
+	for(size_t ui=0;ui<n;ui++)
+		vals[ui]=(vals[ui]-meanV)/stdevVal;
+
+	//For test, data *must be sorted*
+	std::sort(vals.begin(),vals.end());
+
+	//Compute the Phi distribution from the error function
+	// - also compute log of this for later use
+	//--
+	std::vector<double> normedPhi,lonCdf;
+	std::vector<bool> normedPhiOK;
+
+	normedPhiOK.resize(n,true);
+	normedPhi.resize(n);
+	for(size_t ui=0;ui<n; ui++)
+	{
+		normedPhi[ui] = 0.5*(1.0+gsl_sf_erf(vals[ui]/sqrt(2.0)));
+
+		if(normedPhi[ui] < std::numeric_limits<float>::epsilon())
+			normedPhiOK[ui]=false;
+	}
+
+	lonCdf.resize(n);
+	for(size_t ui=0;ui<n; ui++)
+	{
+		if(normedPhiOK[ui])	
+			lonCdf[ui] = log(normedPhi[ui]);
+		else
+			normedPhi[ui]=2.0f;  //result will imply v 1.0-normedphi[...] < 0 --> Undefined
+	}
+	//--
+
+	//Compute anderson-darling statistic
+	//--
+	undefCount=0;	
+	double sumV=0.0;
+	for(size_t i=0;i<n; i++)
+	{
+		double v;
+		v=1.0-normedPhi[n-(i+1)];
+		if( v > 0.0)
+			sumV+=(2.0*(i+1.0)-1.0)*(lonCdf[i] + log(v));
+		else
+			undefCount++;
+	}
+
+	n=n-undefCount;
+	statistic=-(double)n - sumV/(double)n;
+
+	//Perform correction of Shorak & Wellner
+	statistic*=(1.0 + 4.0/(double)n + 25/(double(n)*double(n)));
+	
+	//--
+
+
+	//my name... is neo
+	return true;
+}
+
+
+void makeHistogram(const std::vector<float> &data, float start, 
+			float end, float step, std::vector<float> &histVals);
+
+
+#ifdef DEBUG
+
+//Unit test function for anderson statistics. Should be able to check that gaussian numbers are in fact gaussian 
+bool testAnderson();
+
+//Check that the background fitting routine can fit
+// a random TOF data histogram
+bool testBackgroundFit();
+
+#endif
+#endif
diff --git a/src/backend/filters/algorithms/rdf.cpp b/src/backend/filters/algorithms/rdf.cpp
index de87559..ec65877 100644
--- a/src/backend/filters/algorithms/rdf.cpp
+++ b/src/backend/filters/algorithms/rdf.cpp
@@ -1,6 +1,6 @@
  /* 
  * rdf.cpp - Radial distribution function implentation
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +20,8 @@
 
 #include "../filterCommon.h"
 
+#include <gsl/gsl_sf_gamma.h>
+
 using std::vector;
 
 #ifdef _OPENMP
@@ -175,7 +177,7 @@ const unsigned int MAX_NN_DISTS = 0x8000000; //96 MB samples at a time
 //it has been shrunk such that the closest distance from the hull to the original data
 //is reductionDim 
 unsigned int GetReducedHullPts(const vector<Point3D> &points, float reductionDim,  
-		unsigned int *progress, bool (*callback)(bool), vector<Point3D> &pointResult)
+		unsigned int *progress, ATOMIC_BOOL &wantAbort, vector<Point3D> &pointResult)
 {
 	//TODO: This could be made to use a fixed amount of ram, by
 	//partitioning the input points, and then
@@ -191,7 +193,7 @@ unsigned int GetReducedHullPts(const vector<Point3D> &points, float reductionDim
 
 	unsigned int dummyProg;
 	vector<Point3D> theHull;
-	if(computeConvexHull(points,progress,callback,theHull,false))
+	if(computeConvexHull(points,progress,wantAbort,theHull,false))
 		return 2;
 
 	Point3D midPoint(0,0,0);
@@ -202,7 +204,7 @@ unsigned int GetReducedHullPts(const vector<Point3D> &points, float reductionDim
 	//Now we will find the mass/volume centroid of the hull
 	//by constructing sets of pyramids.
 	//We cannot use the simple midpoint, as point distribution
-	//around the hull sruface may be uneven
+	//around the hull surface may be uneven
 	
 	//Here the faces of the hull are the bases of 
 	//pyramids, and the midpoint is the apex
@@ -373,7 +375,7 @@ reduced_loop_next:
 unsigned int generateNNHist( const vector<Point3D> &pointList, 
 			const K3DTree &tree,unsigned int nnMax, unsigned int numBins,
 		       	vector<vector<size_t> > &histogram, float *binWidth , unsigned int *progressPtr,
-			bool (*callback)(bool))
+			ATOMIC_BOOL &wantAbort)
 {
 	if(pointList.size() <=nnMax)
 		return RDF_ERR_INSUFFICIENT_INPUT_POINTS;
@@ -429,12 +431,12 @@ unsigned int generateNNHist( const vector<Point3D> &pointList,
 			{
 				numAnalysed+=CALLBACK_REDUCE;
 				*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
-				if(!(*callback)(false))
+				if(wantAbort)
 					spin=true;
 			}			
 #else
 			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*50.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 			{
 				delete[] maxSqrDist;
 				return RDF_ABORT_FAIL;
@@ -526,7 +528,7 @@ unsigned int generateNNHist( const vector<Point3D> &pointList,
 		#pragma omp critical
 		{
 			*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*50.0f + 50.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 				spin=true;
 			numAnalysed+=CALLBACK_REDUCE;
 		}			
@@ -536,7 +538,7 @@ unsigned int generateNNHist( const vector<Point3D> &pointList,
 		if(!(callbackReduce--))
 		{
 			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*50.0f + 50.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 			{
 				delete[] maxSqrDist;
 				return RDF_ABORT_FAIL;
@@ -558,7 +560,7 @@ unsigned int generateNNHist( const vector<Point3D> &pointList,
 
 unsigned int generate1DAxialDistHist(const vector<Point3D> &pointList, const K3DTree &tree,
 		const Point3D &axisDir, unsigned int *histogram, float distMax, unsigned int numBins,
-		unsigned int *progressPtr, bool (*callback)(bool))
+		unsigned int *progressPtr, ATOMIC_BOOL &wantAbort)
 {
 	ASSERT(fabs(axisDir.sqrMag() -1.0f) < sqrt(std::numeric_limits<float>::epsilon()));
 #ifdef DEBUG
@@ -662,7 +664,7 @@ unsigned int generate1DAxialDistHist(const vector<Point3D> &pointList, const K3D
 			{
 			numAnalysed+=CALLBACK_REDUCE_VAL;
 			*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 			{
 #ifdef _OPENMP
 				spin=true;
@@ -686,7 +688,7 @@ unsigned int generate1DAxialDistHist(const vector<Point3D> &pointList, const K3D
 
 unsigned int generate1DAxialNNHist(const vector<Point3D> &pointList, const K3DTree &tree,
 		const Point3D &axisDir, unsigned int *histogram, float &binWidth, unsigned int nnMax, unsigned int numBins,
-		unsigned int *progressPtr, bool (*callback)(bool))
+		unsigned int *progressPtr, ATOMIC_BOOL &wantAbort)
 {
 #ifdef DEBUG
 	for(unsigned int ui=0;ui<numBins;ui++)
@@ -749,12 +751,12 @@ unsigned int generate1DAxialNNHist(const vector<Point3D> &pointList, const K3DTr
 			{
 				numAnalysed+=CALLBACK_REDUCE;
 				*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
-				if(!(*callback)(false))
+				if(wantAbort)
 					spin=true;
 			}			
 #else
 			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*100.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 			{
 				delete[] maxAxialDist;
 				return RDF_ABORT_FAIL;
@@ -827,14 +829,14 @@ unsigned int generate1DAxialNNHist(const vector<Point3D> &pointList, const K3DTr
 		}
 	
 
-		//Callbacks to perform UI updates as needed
+		//Callbacks to check for abort as needed
 #ifdef _OPENMP 
 		if(!(callbackReduce--))
 		{
 		#pragma omp critical
 		{
 			*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 				spin=true;
 			numAnalysed+=CALLBACK_REDUCE;
 		}			
@@ -844,7 +846,7 @@ unsigned int generate1DAxialNNHist(const vector<Point3D> &pointList, const K3DTr
 		if(!(callbackReduce--))
 		{
 			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*100.0f);
-			if(!(*callback)(false))
+			if(wantAbort)
 			{
 				delete[] maxAxialDist;
 				return RDF_ABORT_FAIL;
@@ -868,7 +870,7 @@ unsigned int generate1DAxialNNHist(const vector<Point3D> &pointList, const K3DTr
 unsigned int generateDistHist(const vector<Point3D> &pointList, const K3DTree &tree,
 			unsigned int *histogram, float distMax,
 			unsigned int numBins, unsigned int &warnBiasCount,
-			unsigned int *progressPtr,bool (*callback)(bool))
+			unsigned int *progressPtr,ATOMIC_BOOL &wantAbort)
 {
 
 
@@ -970,7 +972,7 @@ unsigned int generateDistHist(const vector<Point3D> &pointList, const K3DTree &t
 				{
 					numAnalysed+=numAnalysedThread;
 					*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
-					if(!(*callback)(false))
+					if(wantAbort)
 						spin=true;
 				}
 				if(spin)
@@ -978,7 +980,7 @@ unsigned int generateDistHist(const vector<Point3D> &pointList, const K3DTree &t
 				numAnalysedThread=0;
 #else
 				*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*100.0f);
-				if(!(*callback)(false))
+				if(wantAbort)
 					return RDF_ABORT_FAIL;
 #endif
 				callbackReduce=CALLBACK_REDUCE;
@@ -1003,3 +1005,44 @@ unsigned int generateDistHist(const vector<Point3D> &pointList, const K3DTree &t
 	//Calculations complete!
 	return 0;
 }
+
+
+
+void generateKnnTheoreticalDist(const std::vector<float> &radii, float density, unsigned int nn,
+					std::vector<float> &nnDist)
+{
+	ASSERT(density >=0);
+	ASSERT(nn>0);
+	//Reference :Stephenson, 2009, simplified using D=3
+
+	//Distribution requires evaluation of :
+	//- the gamma function gamma(5/2)
+	const float GAMMA_2PT5 = 1.32934038817914;
+	// - power of pi : pi^(D/2)
+	const float PI_POW_D_2 = pow(M_PI,3.0/2.0);
+	
+	const float LAMBDA_FACTOR =  PI_POW_D_2/GAMMA_2PT5;
+
+	double pBase,lambda;
+
+	//radius independent component
+	lambda = density*LAMBDA_FACTOR;
+	pBase = 3.0/gsl_sf_fact(nn-1);
+	pBase*= pow(lambda,nn);
+	
+
+	nnDist.resize(radii.size());
+	for(size_t ui=0;ui<radii.size();ui++)
+	{
+		double r,p;
+		p=pBase;
+		r =radii[ui];	
+		p*= pow(r,3*nn-1);
+		p*=exp(-lambda*r*r*r);
+		nnDist[ui]=p;
+	}
+
+}
+
+
+
diff --git a/src/backend/filters/algorithms/rdf.h b/src/backend/filters/algorithms/rdf.h
index 0d51b3d..2b2f411 100644
--- a/src/backend/filters/algorithms/rdf.h
+++ b/src/backend/filters/algorithms/rdf.h
@@ -1,6 +1,6 @@
 /* 
  * rdf.h - Radial distribution function implementation header
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,14 +35,14 @@ enum
 unsigned int generateNNHist( const std::vector<Point3D> &pointList, 
 			const K3DTree &tree,unsigned int nnMax, unsigned int numBins,
 		       	std::vector<std::vector<size_t> > &histogram, float *binWidth,
-		       	unsigned int *progressPtr,bool (*callback)(bool));
+		       	unsigned int *progressPtr,ATOMIC_BOOL &wantAbort);
 
 //!Generate an NN histogram using distance max cutoffs. Input histogram must be zeroed,
 //if a voxelsname is given, a 3D RDF will be recorded. in this case voxelBins must be nonzero
 unsigned int generateDistHist(const std::vector<Point3D> &pointList, const K3DTree &tree,
 			unsigned int *histogram, float distMax,
 			unsigned int numBins, unsigned int &warnBiasCount,
-			unsigned int *progressPtr,bool (*callback)(bool));
+			unsigned int *progressPtr,ATOMIC_BOOL &wantAbort);
 
 //!Returns a subset of points guaranteed to lie at least reductionDim inside hull of input points
 /*! Calculates the hull of the input ions and then scales the hull such that the 
@@ -50,7 +50,7 @@ unsigned int generateDistHist(const std::vector<Point3D> &pointList, const K3DTr
  * reductionDim
  */
 unsigned int GetReducedHullPts(const std::vector<Point3D> &pts, float reductionDim,
-		unsigned int  *progress, bool (callback)(bool),std::vector<Point3D> &returnIons );
+		unsigned int  *progress, ATOMIC_BOOL &wantAbort, std::vector<Point3D> &returnIons );
 
 
 //Return a 1D histogram of NN frequencies, by projecting the NNs within a given search onto a specified axis, stopping at some fixed sstance
@@ -58,7 +58,7 @@ unsigned int GetReducedHullPts(const std::vector<Point3D> &pts, float reductionD
 //	- axisDir  must be normalised.
 unsigned int generate1DAxialDistHist(const std::vector<Point3D> &pointList, const K3DTree &tree,
 		const Point3D &axisDir, unsigned int *histogram, float distMax, unsigned int numBins,
-		unsigned int *progressPtr, bool (*callback)(bool));
+		unsigned int *progressPtr, ATOMIC_BOOL &wantAbort);
 
 
 //Generate a 1D distribution of NN distances s projected onto a specified axis
@@ -68,5 +68,12 @@ unsigned int generate1DAxialDistHist(const std::vector<Point3D> &pointList, cons
 unsigned int generate1DAxialNNHist(const std::vector<Point3D> &pointList, const K3DTree &tree,
 			const Point3D &axisDir, unsigned int *histogram, 
 			float &binWidth, unsigned int nnMax, unsigned int numBins,
-			unsigned int *progressPtr, bool (*callback)(bool));
+			unsigned int *progressPtr, ATOMIC_BOOL &wantAbort);
+
+
+//generate the Knn probability distribution for a given nn occurring at a radius in 3D space
+// with a fixed density parameter. Radii are the positions to evaluate the distribution
+// nnDist will store the answer.  It is required that both density >=0 and nn >0.
+void generateKnnTheoreticalDist(const std::vector<float> &radii, float density, unsigned int nn,
+					std::vector<float> &nnDist);
 #endif
diff --git a/src/backend/filters/allFilter.cpp b/src/backend/filters/allFilter.cpp
index fd66597..fa83474 100644
--- a/src/backend/filters/allFilter.cpp
+++ b/src/backend/filters/allFilter.cpp
@@ -1,6 +1,6 @@
 /*
  *	allFilter.cpp - factory functions for filter classes
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/backend/filters/allFilter.h b/src/backend/filters/allFilter.h
index 68f356f..b3ae895 100644
--- a/src/backend/filters/allFilter.h
+++ b/src/backend/filters/allFilter.h
@@ -1,6 +1,6 @@
 /*
  *	allFilter.h - Filter class factory header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -37,11 +37,11 @@
 bool isValidFilterName(const std::string &s);
 
 //!Create a "true default" filter from its true name string
-Filter *makeFilter(const string  &s) ;
+Filter *makeFilter(const std::string  &s) ;
 //!Create a true default filter from its enum value FILTER_TYPE_*
 Filter *makeFilter(unsigned int ui) ;
 
 //!Create a true default filter from its type string
-Filter *makeFilterFromDefUserString(const string &s) ;
+Filter *makeFilterFromDefUserString(const std::string &s) ;
 
 #endif
diff --git a/src/backend/filters/annotation.cpp b/src/backend/filters/annotation.cpp
index 6f0d4b6..f3e253f 100644
--- a/src/backend/filters/annotation.cpp
+++ b/src/backend/filters/annotation.cpp
@@ -1,6 +1,6 @@
 /*
  *	annotation.cpp - 3D annotations filter for 3depict
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -19,6 +19,11 @@
 
 #include "filterCommon.h"
 
+using std::string;
+using std::vector;
+using std::pair;
+using std::make_pair;
+
 //grab size when doing convex hull calculations
 const unsigned int HULL_GRAB_SIZE=4096;
 
@@ -151,7 +156,7 @@ Filter *AnnotateFilter::cloneUncached() const
 }
 
 unsigned int AnnotateFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 
 	//Clear selection devices, first deleting any we have
@@ -160,7 +165,7 @@ unsigned int AnnotateFilter::refresh(const std::vector<const FilterStreamData *>
 	//Pipe everything through
 	propagateStreams(dataIn,getOut);
 
-	//If we are not endabled, do not draw anyhting into the output
+	//If we are not enabled, do not draw anything into the output
 	if(!active)
 		return 0;
 
@@ -303,7 +308,7 @@ unsigned int AnnotateFilter::refresh(const std::vector<const FilterStreamData *>
 
 			//Work out the angle if there is a non-degenerate vector.
 			//otherwise set it to the "undefined" value of zero
-			if(fabs(d1.dotProd(d2))  > sqrt(std::numeric_limits<float>::epsilon()))
+			if(fabs(d1.dotProd(d2))  > sqrtf(std::numeric_limits<float>::epsilon()))
 				angleVal =d1.angle(d2);
 
 			if(reflexAngle)
@@ -394,12 +399,12 @@ unsigned int AnnotateFilter::refresh(const std::vector<const FilterStreamData *>
 		vector<float> tickSpacings;
 		if(linearFixedTicks)
 		{
-			tickSpacingsFromFixedNum(0,sqrt(target.sqrDist(position)),
+			tickSpacingsFromFixedNum(0,sqrtf(target.sqrDist(position)),
 					linearMeasureTicks,tickSpacings);
 		}
 		else
 		{
-			tickSpacingsFromInterspace(0,sqrt(target.sqrDist(position)),
+			tickSpacingsFromInterspace(0,sqrtf(target.sqrDist(position)),
 						linearMeasureSpacing,tickSpacings);
 		}
 
@@ -1067,7 +1072,7 @@ bool AnnotateFilter::setProperty(  unsigned int key,
 			float tmp;
 			stream_cast(tmp,value);
 			if(fabs(tmp-textSize) > std::numeric_limits<float>::epsilon() 
-				&& tmp > sqrt(std::numeric_limits<float>::epsilon()))
+				&& tmp > sqrtf(std::numeric_limits<float>::epsilon()))
 			{
 				needUpdate=true;
 				textSize=tmp;
@@ -1173,7 +1178,7 @@ bool AnnotateFilter::setProperty(  unsigned int key,
 	return true;
 }
 
-std::string  AnnotateFilter::getErrString(unsigned int code) const
+std::string  AnnotateFilter::getSpecificErrString(unsigned int code) const
 {
 	ASSERT(false);
 }
@@ -1470,11 +1475,11 @@ bool rulerTest()
 	TEST(f->setProperty(KEY_POSITION,s,needUp),"Set prop");
 	stream_cast(s,Point3D(1,1,1));
 	TEST(f->setProperty(KEY_TARGET,s,needUp),"Set prop");
-	stream_cast(s,sqrt(2)/10);
+	stream_cast(s,sqrtf(2)/10);
 	TEST(f->setProperty(KEY_LINEAR_TICKSPACING,s,needUp),"Set prop");
 	
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 
 	delete f;
 
@@ -1551,7 +1556,7 @@ bool angleTest()
 	TEST(f->setProperty(KEY_ANGLE_POS_TWO,s,needUp),"Set prop");
 	
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 
 	delete f;
 
@@ -1627,7 +1632,7 @@ bool arrowTest()
 	TEST(f->setProperty(KEY_TARGET,s,needUp),"Set target prop");
 	
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 
 	delete f;
 
@@ -1702,7 +1707,7 @@ bool textArrowTest()
 	TEST(f->setProperty(KEY_TARGET,s,needUp),"Set prop");
 	
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 
 	delete f;
 
diff --git a/src/backend/filters/annotation.h b/src/backend/filters/annotation.h
index ce99959..c644824 100644
--- a/src/backend/filters/annotation.h
+++ b/src/backend/filters/annotation.h
@@ -1,6 +1,6 @@
 /*
  *	annotation.h - 3D annotation header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -85,7 +85,7 @@ class AnnotateFilter : public Filter
 		// ->cached.
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 							std::vector<const FilterStreamData *> &dataOut,
-							ProgressData &progress, bool (*callback)(bool));
+							ProgressData &progress);
 		//!Get (approx) number of bytes required for cache
 		size_t numBytesForCache(size_t nObjects) const;
 
@@ -107,7 +107,7 @@ class AnnotateFilter : public Filter
 		void setPropFromBinding( const SelectionBinding &b) ;
 
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 
 		//!Dump state to output stream, using specified format
 		/* Current supported formats are STATE_FORMAT_XML
diff --git a/src/backend/filters/boundingBox.cpp b/src/backend/filters/boundingBox.cpp
index 4180090..005758d 100644
--- a/src/backend/filters/boundingBox.cpp
+++ b/src/backend/filters/boundingBox.cpp
@@ -1,6 +1,6 @@
 /*
  *	boundingBox.cpp - Place a bounding box around point datasets
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -19,6 +19,11 @@
 
 #include "filterCommon.h"
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+
 enum
 {
 	KEY_VISIBLE=1,
@@ -93,7 +98,7 @@ Filter *BoundingBoxFilter::cloneUncached() const
 	p->lineWidth=lineWidth;
 	p->fontSize=fontSize;
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -245,7 +250,7 @@ void BoundingBoxFilter::drawDimension(const BoundCube &bTotal, DrawStreamData *d
 
 
 
-	//Add the arrows from the start ot the end
+	//Add the arrows from the start to the end
 	//Create the position from which to draw the tick origins
 	Point3D tickOrigin,tickEnd;
 	bTotal.getBounds(tickOrigin,tickEnd);
@@ -355,7 +360,7 @@ void BoundingBoxFilter::drawDimension(const BoundCube &bTotal, DrawStreamData *d
 }
 
 unsigned int BoundingBoxFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 
 	if(!isVisible)
@@ -429,7 +434,7 @@ unsigned int BoundingBoxFilter::refresh(const std::vector<const FilterStreamData
 
 						if(thisT == 0)
 						{
-							if(!(*callback)(false))
+							if(*Filter::wantAbort)
 								spin=true;
 						}
 					}
@@ -456,7 +461,7 @@ unsigned int BoundingBoxFilter::refresh(const std::vector<const FilterStreamData
 					{
 						n+=NUM_CALLBACK;
 						progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-						if(!(*callback)(false))
+						if(*Filter::wantAbort)
 						{
 							delete d;
 							return BOUNDINGBOX_ABORT_ERR;
@@ -465,6 +470,7 @@ unsigned int BoundingBoxFilter::refresh(const std::vector<const FilterStreamData
 				}
 #endif
 				bTotal.expand(bThis);
+				progress.filterProgress=100;
 				break;
 			}
 			default:
@@ -766,7 +772,7 @@ bool BoundingBoxFilter::setProperty(  unsigned int key,
 }
 
 
-std::string  BoundingBoxFilter::getErrString(unsigned int code) const
+std::string  BoundingBoxFilter::getSpecificErrString(unsigned int code) const
 {
 
 	//Currently the only error is aborting
@@ -1070,7 +1076,7 @@ bool boxVolumeTest()
 
 
 	ProgressData p;
-	TEST(!b->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!b->refresh(streamIn,streamOut,p),"Refresh error code");
 	//---
 
 	//Run tests 
diff --git a/src/backend/filters/boundingBox.h b/src/backend/filters/boundingBox.h
index d70f13e..db77d65 100644
--- a/src/backend/filters/boundingBox.h
+++ b/src/backend/filters/boundingBox.h
@@ -1,6 +1,6 @@
 /*
  *	boundingBox.h - Bounding boxes for 3D point data
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -66,7 +66,7 @@ class BoundingBoxFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 					std::vector<const FilterStreamData *> &getOut, 
-					ProgressData &progress, bool (*callback)(bool));
+					ProgressData &progress);
 		//!Force a re-read of the rangefile Return value is range file reading error code
 		unsigned int updateRng();
 		virtual std::string typeString() const { return std::string(TRANS("Bound box"));};
@@ -78,7 +78,7 @@ class BoundingBoxFilter : public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format,
diff --git a/src/backend/filters/clusterAnalysis.cpp b/src/backend/filters/clusterAnalysis.cpp
index 476056c..158bde4 100644
--- a/src/backend/filters/clusterAnalysis.cpp
+++ b/src/backend/filters/clusterAnalysis.cpp
@@ -1,6 +1,6 @@
 /*
  *	clusterAnalysis.cpp - Perform clustering data analysis on valued point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -16,18 +16,22 @@
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 #include "clusterAnalysis.h"
-
-
-
 #include "filterCommon.h"
 
-
 #include <queue>
 
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_eigen.h>
 
-#include "algorithms/K3DTree-mk2.h"
+#include "../../common/gsl_helper.h"
 #include "backend/plot.h"
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::map;
+using std::max;
 
 enum
 {
@@ -56,8 +60,7 @@ enum
 
 enum
 {
-	ABORT_ERR=1,
-	NOCORE_ERR,
+	NOCORE_ERR=1,
 	NOBULK_ERR,
 	CLUSTER_ERR_ENUM_END
 };
@@ -173,42 +176,33 @@ void makeCompositionTable(const IonStreamData *i ,const RangeFile *r,
 // provide the transformation vectors, so singular values only provide
 // scalar information separate from the original input basis. The first value
 // does *not* correspond to the "x" direction of your input, for example.
-/*void computeSingularValues(float *data, size_t numRows, size_t numCols,
+void computeEigenValues(gsl_matrix *m, size_t numRows, size_t numCols,
 				vector<float> &resultValues, vector<Point3D> &resultVectors)
 {
 
 	//Although this function *mostly* works on arbitrary datasizes, the vector<Point3D> is of course
 	//fixed to being 3D..
-	ASSERT(numCols == 3);
+	ASSERT(numCols == 3 && numRows == 3);
 
-	gsl_matrix *m=gsl_matrix_alloc(numRows,numCols);
-	for(unsigned int ui=0;ui<numRows;ui++)
-	{
-		for(unsigned int uj=0;uj<numCols; uj++)
-			gsl_matrix_set(m,ui,uj,data[ui*numCols+uj]);
-	}
 
-	//Set up the output vector space, the singular result space and
+	//Set up the output vector space, the eigen result space and
 	//some scratch space for the program
-	gsl_vector *singularVals= gsl_vector_alloc(numCols); //Singular value results
-	gsl_matrix *newSpace = gsl_matrix_alloc(numCols,numCols); // Singular basis vectors
-	gsl_vector *workspace = gsl_vector_alloc(numCols); //scratch space
-
-	//Transformation space set to orthogonal Cartesian
-	gsl_matrix_set_identity(newSpace); //U
+	gsl_vector *eigenVals= gsl_vector_alloc(numCols); //Eigen value results
+	gsl_matrix *eigenVecs= gsl_matrix_alloc(numCols,numCols); // Eigen vectors
+	gsl_eigen_symmv_workspace *workspace = gsl_eigen_symmv_alloc(numCols); //scratch space
 
 	//Decompose matrix. Note that input matrix is overwritten by gsl. 
-	gsl_linalg_SV_decomp(m,newSpace,
-			singularVals,workspace);
+	gsl_eigen_symmv(m,eigenVals,
+			eigenVecs,workspace);
+	gsl_eigen_symmv_sort (eigenVals, eigenVecs, GSL_EIGEN_SORT_VAL_DESC);
 
 	resultValues.clear();
 	for(size_t ui=0;ui<numCols;ui++)
 	{
 		float v;
-		v = gsl_vector_get(singularVals,ui);
-
-		ASSERT(v >=0.0f);
+		v = gsl_vector_get(eigenVals,ui);
 		resultValues.push_back(v);
+
 	}
 
 	//Copy out the decomposed V matrix
@@ -216,18 +210,17 @@ void makeCompositionTable(const IonStreamData *i ,const RangeFile *r,
 	for(size_t ui=0;ui<numCols; ui++)
 	{
 		for(size_t uj=0;uj<numCols; uj++)
-			resultVectors[ui][uj] = gsl_matrix_get(newSpace,ui,uj);
+			resultVectors[ui][uj] = gsl_matrix_get(eigenVecs,ui,uj);
 	}
 
 
 	//Free storage space
-	gsl_vector_free(singularVals); 
-	gsl_vector_free(workspace);
+	gsl_vector_free(eigenVals); 
+	gsl_matrix_free(eigenVecs); 
+	gsl_eigen_symmv_free(workspace);
 	
-	gsl_matrix_free(newSpace);
-	gsl_matrix_free(m);
 }
-*/
+
 
 void ClusterAnalysisFilter::checkIonEnabled(bool &core, bool &bulk) const
 {
@@ -264,7 +257,7 @@ void ClusterAnalysisFilter::buildRangeEnabledMap(const RangeStreamData *r,
 
 ClusterAnalysisFilter::ClusterAnalysisFilter() : algorithm(CLUSTER_LINK_ERODE),
 	enableCoreClassify(false), coreDist(0.0f), coreKNN(1), linkDist(0.5f), 
-	enableBulkLink(false), bulkLink(1), enableErosion(false), dErosion(0.25),
+	enableBulkLink(false), bulkLink(0.25), enableErosion(false), dErosion(0.25),
 	wantClusterID(false), wantCropSize(false), nMin(0),nMax(std::numeric_limits<size_t>::max()),
 	wantClusterSizeDist(false),logClusterSize(false),
 	wantClusterComposition(true),normaliseComposition(true),
@@ -314,7 +307,7 @@ Filter *ClusterAnalysisFilter::cloneUncached() const
 	p->ionBulkEnabled.resize(ionBulkEnabled.size());
 	std::copy(ionBulkEnabled.begin(),ionBulkEnabled.end(),p->ionBulkEnabled.begin());
 	
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -442,7 +435,7 @@ void ClusterAnalysisFilter::initFilter(const std::vector<const FilterStreamData
 }
 
 unsigned int ClusterAnalysisFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	// - cluster ID alters the mass, so we can't use this analysis
 	// at the same time
@@ -468,6 +461,9 @@ unsigned int ClusterAnalysisFilter::refresh(const std::vector<const FilterStream
 
 	//OK, we actually have to do some work.
 	//================
+	//Set K3D tree abort pointer and progress
+	K3DTreeMk2::setAbortFlag(Filter::wantAbort);
+	K3DTreeMk2::setProgressPtr(&progress.filterProgress);
 
 	
 	//Find out how much total size we need in points vector
@@ -540,7 +536,7 @@ unsigned int ClusterAnalysisFilter::refresh(const std::vector<const FilterStream
 		{
 			unsigned int errCode;
 			errCode=refreshLinkClustering(dataIn,clusteredCore,
-						clusteredBulk,progress,callback);
+						clusteredBulk,progress);
 
 			if(errCode)
 				return errCode;
@@ -561,7 +557,7 @@ unsigned int ClusterAnalysisFilter::refresh(const std::vector<const FilterStream
 		paranoidDebugAssert(clusteredCore,clusteredBulk);
 #endif
 	if(wantCropSize)
-		stripClusterBySize(clusteredCore,clusteredBulk,callback,progress);
+		stripClusterBySize(clusteredCore,clusteredBulk,progress);
 
 	bool haveBulk,haveCore;
 	haveBulk=clusteredBulk.size();
@@ -638,63 +634,53 @@ ASSERT(!(haveBulk && !haveCore));
 		}
 	}
 
-/*
 	if(wantClusterMorphology)
 	{
 		//Compute the singular values for each cluster
 		//which describe the cluster morphology, their basis vectors, and 
 		//the mass centre for the clusters
 
-		//The premise of this analysis technique is outlined in Sudbrack,2004 - to some extent.
-		// In that work, they have several methods of doing morphology analysis, including where they 
-		//  use a PCA which wraps up the SVD to do the calculations.
-		// Here we use the SVD, which is faster & more space efficient, as we don't form the covariance 
-		// matrix, but work directly on our data.
-
-		// Aside from a sqrt, we should get the same answer for the ellipsoidal fit. 
-		//
-		// Showing SVD & PCA to be equivalent enough for our use:
-		//  SVD : X=USV', PCA: XX'=WDW' ;
-		//
-		//PCA, sub-ing Xs SVD representation:
-		//  XX' = (USV')(USV')'
-		//  XX' = (USV')(VSU')
-		//  XX' = US(V'V)SU'
-		//  V is orthogonal, thus V'V =I
-		//  as transpose of orthogonal is its own inverse.
-		//  XX' = US^2U'
-		//
-		// In our case, X_i=P_i-P_bar, where P_i is the i-th point in the cluster
-		//
-		//  Thus D=S^2  and, D, being diagonal implies S=D^0.5
 		//Sudbrack, C. : Decomposition behavior in model Ni-Al-Cr-X superalloys: 
 		// temporal evolution and compositional pathways on a nanoscale (Ph.D. Thesis, 2004) 
 		//http://arc.nucapt.northwestern.edu/refbase/files/Sudbrack_Ph.D._thesis_2004_6MB.pdf 
 
-		//Singular values, 
-		//Mass centres of clusters & Singular vectors 
-		vector<vector<float> > singularVals;
+		//Eigen values, 
+		//Mass centres of clusters & Eigen vectors 
 		vector<std::pair<Point3D,std::vector<Point3D> > > singularVectors;
-		getSingularValues(clusteredCore,clusteredBulk,
-					singularVals,singularVectors);
+		vector<vector<float> > singularVals; 
 
-		ASSERT(singularVals.size() == clusteredCore.size());
-		ASSERT(singularVectors.size() == clusteredCore.size());
+		singularVectors.resize(clusteredCore.size());
+		singularVals.resize(clusteredCore.size());
+		for(unsigned int ui=0;ui<clusteredCore.size();ui++)
+		{
+			if(clusteredBulk.size())	
+				getEllipsoidalFit(clusteredCore[ui],clusteredBulk[ui],singularVectors[ui]);
+			else
+			{
+				vector<IonHit> dummy;
+				getEllipsoidalFit(clusteredCore[ui],dummy,singularVectors[ui]);
+			}
+			vector<float> thisSingVals;
+			thisSingVals.resize(3);
+			
+			for(unsigned int uj=0;uj<3;uj++)
+			{
+				thisSingVals[uj] =sqrtf(singularVectors[ui].second[uj].sqrMag());
+			}
+			singularVals[ui].swap(thisSingVals);
+		}	
 
 		//Ok, so we have the singular values, now create a 
 		//plot with the values in it
-		PlotStreamData *p = new PlotStreamData;
+		Plot2DStreamData *p = new Plot2DStreamData;
 		p->parent=this;
 
-		p->plotMode=PLOT_MODE_1D;
-		p->plotStyle=PLOT_LINE_POINTS;
+		p->plotType=PLOT_2D_SCATTER;
 		p->dataLabel=TRANS("Morphology Plot");
 		p->xLabel=TRANS("\\lambda_1:\\lambda_2 ratio");
 		p->yLabel=TRANS("\\lambda_2:\\lambda_3 ratio");
-		p->xyData.reserve(singularVals.size());
-		//the Y label makes more sense than the plot label here.
-		p->useDataLabelAsYDescriptor=false;
-
+		p->scatterData.reserve(singularVals.size());
+		p->scatterIntensityLog=true;
 
 #pragma omp parallel for
 		for(unsigned int ui=0; ui<singularVals.size();ui++)
@@ -702,26 +688,36 @@ ASSERT(!(haveBulk && !haveCore));
 			if(singularVals[ui].size() == 3 && 
 				singularVals[ui].back() > std::numeric_limits<float>::epsilon())
 			{
-				p->xyData.push_back(make_pair(singularVals[ui][0]/singularVals[ui][1],
-						singularVals[ui][1]/singularVals[ui][2]));
+
+				//sort the singular values
+				vector<float> v;
+				v.resize(3);
+				for(size_t uj=0;uj<3;uj++)	
+					v[uj]= singularVals[ui][uj];
+
+				std::sort(v.begin(),v.end());
+
+				if( v[0] < std::numeric_limits<float>::epsilon() )
+					continue;
+
+				//Plot (sec largest/largest) (y-coord)  vs. ( sec. smallest/largest) (x-coord) eigenvalues
+				//Keeping data in [0,1] range (thanks to AJL for pointing out the flip!)
+				pair<float,float> pr(v[0]/v[1],v[1]/v[2]);
+				#pragma omp critical
+				{
+				p->scatterData.push_back(pr);
+				p->scatterIntensity.push_back(clusteredCore[ui].size());
+				}
 
 			}
 		}
 
-		if(p->xyData.size())
+		if(p->scatterData.size())
 		{
 			p->index=curPlotIndex;
 			curPlotIndex++;
-			p->autoSetHardBounds();
-
-			if(cache)
-			{
-				p->cached=1;
-				filterOutputs.push_back(p);
-			}
-			else
-				p->cached=0;
 
+			cacheAsNeeded(p);
 			getOut.push_back(p);
 
 		}
@@ -730,7 +726,10 @@ ASSERT(!(haveBulk && !haveCore));
 			consoleOutput.push_back(TRANS("No clusters had sufficient dimensionality to compute singular values"));
 			delete p;
 		}
-		
+	
+
+		//Draw the singular vectors in 3D, per cluster
+		{	
 		
 		DrawStreamData *singularVectorDraw = new DrawStreamData;
 		singularVectorDraw->parent=this;
@@ -740,8 +739,7 @@ ASSERT(!(haveBulk && !haveCore));
 		//the length of each part of the axis shows the primary directions
 		//for that part of the cluster
 		for(unsigned int ui=0;ui<singularVectors.size();ui++)
-		{	
-
+		{
 			for(unsigned int uj=0;uj<3;uj++)
 			{
 				//Need at least j-rank on the singular vectors.
@@ -752,32 +750,31 @@ ASSERT(!(haveBulk && !haveCore));
 				DrawVector *d;
 				d= new DrawVector;
 				d->setColour(uj ==0, uj == 1, uj ==2,1);
-				Point3D start,singVector;
+				d->setDrawArrow(false);
+				
+				Point3D start,semiAxis;
 
-				//make a little cross in free space
-				singVector=singularVectors[ui].second[uj]*singularVals[ui][uj];
-				start=singularVectors[ui].first-singVector*0.5f;
+				semiAxis=singularVectors[ui].second[uj];
+				start=singularVectors[ui].first;
 
 
 				//First in current singular pair is cluster origin
 				d->setOrigin(start);
 				//Second contains each individual  thing
-				d->setVector(singVector*0.5f);
+				d->setVector(semiAxis);
 				singularVectorDraw->drawables.push_back(d);
 			}
 		}
 
-		if(cache)
-		{
-			WARN(false,"Drawable caching is broken now. Refusing to cache!");
-			singularVectorDraw->cached=0;
+		cacheAsNeeded(singularVectorDraw);
+	
+		getOut.push_back(singularVectorDraw);
+
 		}
-		else
-			singularVectorDraw->cached=0;
+	
 
-		getOut.push_back(singularVectorDraw);
 	}
-*/
+
 	//Construct the output clustered data.
 	IonStreamData *i = new IonStreamData;
 	i->parent =this;	
@@ -1058,7 +1055,6 @@ void ClusterAnalysisFilter::getProperties(FilterPropGroup &propertyList) const
 	}
 
 
-	/*
 	tmpStr=boolStrEnc(wantClusterMorphology);
 	p.name=TRANS("Morphology Dist.");
 	p.data=tmpStr;
@@ -1066,7 +1062,6 @@ void ClusterAnalysisFilter::getProperties(FilterPropGroup &propertyList) const
 	p.helpText=TRANS("Create a plot showing cluster aspect ratio");
 	p.key=KEY_WANT_CLUSTERMORPHOLOGY;
 	propertyList.addProperty(p,curGroup);
-	*/
 
 
 	tmpStr=boolStrEnc(wantClusterID);
@@ -1428,7 +1423,7 @@ bool ClusterAnalysisFilter::setProperty(unsigned int key,
 			}
 
 			//composition analysis is mutually
-			// exclsive with ID
+			// exclusive with ID
 			wantClusterID=false;
 			
 			break;
@@ -1752,7 +1747,13 @@ bool ClusterAnalysisFilter::readState(xmlNodePtr &nodePtr, const std::string &pa
 		if(!XMLGetAttrib(tmpPtr,enabled,"enabled"))
 			return false;
 
+		std::string tmpName;
+		if(!XMLGetAttrib(tmpPtr,tmpName,"name"))
+			return false;
+	
 		ionCoreEnabled.push_back(enabled);
+		
+		ionNames.push_back(tmpName);
 	}
 
 	if(XMLHelpFwdToElem(nodePtr,"bulk"))
@@ -1790,10 +1791,16 @@ unsigned int ClusterAnalysisFilter::getRefreshBlockMask() const
 
 unsigned int ClusterAnalysisFilter::getRefreshEmitMask() const
 {
-	if(wantClusterSizeDist || wantClusterComposition || wantClusterMorphology)
-		return STREAM_TYPE_IONS | STREAM_TYPE_PLOT | STREAM_TYPE_DRAW;
-	else
-		return STREAM_TYPE_IONS;
+	unsigned int mask = STREAM_TYPE_IONS;
+
+
+	if(wantClusterSizeDist || wantClusterComposition )
+		mask|= STREAM_TYPE_PLOT | STREAM_TYPE_DRAW;
+		
+	if(wantClusterMorphology)
+		mask|= STREAM_TYPE_PLOT2D | STREAM_TYPE_DRAW;
+
+	return mask;
 }
 
 unsigned int ClusterAnalysisFilter::getRefreshUseMask() const
@@ -1801,10 +1808,9 @@ unsigned int ClusterAnalysisFilter::getRefreshUseMask() const
 	return STREAM_TYPE_IONS | STREAM_TYPE_RANGE;	
 }
 
-std::string ClusterAnalysisFilter::getErrString(unsigned int i) const
+std::string ClusterAnalysisFilter::getSpecificErrString(unsigned int i) const
 {
 	const char *errStrs[] = {"",
-		"Clustering aborted",
 		"No core ions for cluster",
 		"No bulk ions for cluster" };
 
@@ -1821,8 +1827,8 @@ void ClusterAnalysisFilter::setPropFromBinding(const SelectionBinding &b)
 
 unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<const FilterStreamData *> &dataIn,
 		std::vector< std::vector<IonHit> > &clusteredCore, 
-		std::vector<std::vector<IonHit>  > &clusteredBulk,ProgressData &progress,
-					bool (*callback)(bool)) 
+		std::vector<std::vector<IonHit>  > &clusteredBulk,ProgressData &progress)
+					
 {
 
 	//Clustering algorithm, as per 
@@ -1859,14 +1865,13 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 	// In the implementation, there are more steps, due to data structure construction
 	// and other computational concerns
 	
-	bool needCoring=enableCoreClassify;
 	bool needErosion=enableErosion && enableBulkLink;
 	unsigned int numClusterSteps=4;
 	if(enableBulkLink)
 		numClusterSteps+=2;
 	if(needErosion)
 		numClusterSteps++;
-	if(needCoring)
+	if(enableCoreClassify)
 		numClusterSteps++;
 
 
@@ -1905,11 +1910,11 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 	progress.filterProgress=0;
 	progress.stepName=TRANS("Collate");
 	progress.maxStep=numClusterSteps;
-	if(!(*callback)(true))
-		return ABORT_ERR;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	vector<IonHit> coreIons,bulkIons;
-	createRangedIons(dataIn,coreIons,bulkIons,progress,callback);
+	createRangedIons(dataIn,coreIons,bulkIons,progress);
 
 	if(coreIons.empty())
 		return 0;
@@ -1918,129 +1923,25 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 	K3DTreeMk2 coreTree,bulkTree;
 	BoundCube bCore,bBulk;
 
-	//Build the core KD tree
+	//Build the core KD & bulk trees
 	//----------
 	progress.step++;
 	progress.filterProgress=0;
 	progress.stepName=TRANS("Build Core");
-	if(!(*callback)(true))
-		return ABORT_ERR;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
-	//Build the core tree (eg, solute ions)
-	coreTree.setCallback(callback);
-	coreTree.setProgressPointer(&(progress.filterProgress));
-	coreTree.resetPts(coreIons,false);
-	coreTree.build();
-
-	coreTree.getBoundCube(bCore);
-	//----------
+	unsigned int errCode;
+	errCode=buildKDTrees(coreIons,bulkIons,coreTree,bulkTree,progress);
 
-	if(needCoring)
-	{
-		//Perform Clustering Stage (1) : clustering classification
-		//==	
-		progress.step++;
-		progress.stepName=TRANS("Classify Core");
-		if(!(*callback)(true))
-			return ABORT_ERR;
-		
-		vector<bool> coreOK;
-		ASSERT(coreIons.size() == coreTree.size());
-		coreOK.resize(coreTree.size());
-		float coreDistSqr=coreDist*coreDist;
 
-		//TODO: the trees internal Tags prevent us from parallelising this. 
-		//       :(. If we could pass a tag map to the tree, this would solve the problem
-		for(size_t ui=0;ui<coreTree.size();ui++)
-		{
-			const Point3D *p;
-			size_t pNN;	
-			unsigned int k;
-			vector<size_t> tagsToClear;
+	if(errCode)
+		return errCode;
+	coreTree.getBoundCube(bCore);
+	if(enableBulkLink)
+		bulkTree.getBoundCube(bBulk);
 		
-			//Don't match ourselves -- to do this we must "tag" this tree node before we start
-			p=coreTree.getPt(ui);
-			coreTree.tag(ui);
-			tagsToClear.push_back(ui);
-			
-			k=1;
-
-			//Loop through this ions NNs, seeing if the kth NN is within a given radius
-			do
-			{
-				pNN=coreTree.findNearestUntagged(*p,bCore,true);
-				tagsToClear.push_back(pNN);
-				k++;
-
-			}while( pNN !=(size_t)-1 && k<coreKNN);
-			
-
-			//Core is only OK if the NN is good, and within the 
-			//specified distance
-			if(pNN == (size_t)-1)
-			{
-				coreOK[coreTree.getOrigIndex(ui)]=false;
-				ASSERT(tagsToClear.back() == (size_t) -1);
-				tagsToClear.pop_back(); //get rid of the -1
-			}
-			else
-			{
-				float nnSqrDist;
-				nnSqrDist=p->sqrDist(*(coreTree.getPt(pNN)));
-				coreOK[coreTree.getOrigIndex(ui)] = nnSqrDist < coreDistSqr;
-			}
-
-
-			//reset the tags, so we can find near NNs
-			coreTree.clearTags(tagsToClear);
-			tagsToClear.clear();
-
-			if(!(ui%PROGRESS_REDUCE))
-			{
-				progress.filterProgress= (unsigned int)(((float)ui/(float)coreTree.size())*100.0f);
-				if(!(*callback)(false))
-					return ABORT_ERR;
-			}
-		}
-
-
-		for(size_t ui=coreOK.size();ui;)
-		{
-			ui--;
-
-			if(!coreOK[ui])
-			{
-				//We have to convert the core ion to a bulk ion
-				//as it is rejected.
-				bulkIons.push_back(coreIons[ui]);
-				coreIons[ui]=coreIons.back();
-				coreIons.pop_back();
-			}
-		}
 
-		//Re-Build the core KD tree
-		coreTree.setCallback(callback);
-		coreTree.setProgressPointer(&(progress.filterProgress));
-		coreTree.resetPts(coreIons,false);
-		coreTree.build();
-
-		coreTree.getBoundCube(bCore);
-		//==	
-	}
-
-
-	//Build the bulk tree (eg matrix ions.), as needed
-	if(enableBulkLink)
-	{
-		progress.step++;
-		progress.stepName=TRANS("Build Bulk");
-		if(!(*callback)(true))
-			return ABORT_ERR;
-		bulkTree.setCallback(callback);
-		bulkTree.setProgressPointer(&(progress.filterProgress));
-		bulkTree.resetPts(bulkIons,false);
-		bulkTree.build();
-	}
 	//----------
 	
 	
@@ -2053,17 +1954,15 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 
 	//Update progress stuff
 	progress.step++;
-	progress.stepName=TRANS("Core");
 	progress.filterProgress=0;
-	if(!(*callback)(true))
-		return ABORT_ERR;
+	progress.stepName=TRANS("Core");
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 		
 
 	vector<vector<size_t> > allCoreClusters,allBulkClusters;
 	const float linkDistSqr=linkDist*linkDist;
 	
-	size_t progressCount=0;
-	
 	//When this queue is exhausted, move to the next cluster
 	for(size_t ui=0;ui<coreTree.size();ui++)
 	{
@@ -2133,14 +2032,10 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 				}
 			
 
-				progressCount++;
-				if(!(progressCount%PROGRESS_REDUCE))
-				{
-					//Progress may be a little non-linear if cluster sizes are not random
-					progress.filterProgress= (unsigned int)(((float)ui/(float)coreTree.size())*100.0f);
-					if(!(*callback)(false))
-						return ABORT_ERR;
-				}
+				//Progress may be a little non-linear if cluster sizes are not random
+				progress.filterProgress= (unsigned int)(((float)ui/(float)coreTree.size())*100.0f);
+				if(*Filter::wantAbort)
+					return FILTER_ERR_ABORT;
 			}
 
 
@@ -2160,8 +2055,8 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 	//====
 
 	//update progress
-	if(!(*callback)(false))
-		return ABORT_ERR;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	//NOTE : Speedup trick. If we know the cluster size at this point
 	// and we know we don't want to count the bulk, we can strip out clusters
@@ -2198,10 +2093,10 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 
 		//Update progress stuff
 		progress.step++;
-		progress.stepName=TRANS("Bulk");
 		progress.filterProgress=0;
-		if(!(*callback)(true))
-			return ABORT_ERR;
+		progress.stepName=TRANS("Bulk");
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
 
 		if(bulkTree.size())
 		{
@@ -2298,8 +2193,8 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 							prog=PROGRESS_REDUCE;
 							//Progress may be a little non-linear if cluster sizes are not random
 							progress.filterProgress= (unsigned int)(((float)ui/(float)allCoreClusters.size())*100.0f);
-							if(!(*callback)(false))
-								return ABORT_ERR;
+							if(*Filter::wantAbort)
+								return FILTER_ERR_ABORT;
 
 						}
 					}
@@ -2328,10 +2223,10 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 	{
 		//Update progress stuff
 		progress.step++;
-		progress.stepName=TRANS("Erode");
 		progress.filterProgress=0;
-		if(!(*callback)(true))
-			return ABORT_ERR;
+		progress.stepName=TRANS("Erode");
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
 
 		//Now perform the "erosion" step, where we strip off previously
 		//tagged matrix, if it is within a given distance of some untagged
@@ -2388,7 +2283,7 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 				numCounted+=PROGRESS_REDUCE;
 				//Progress may be a little non-linear if cluster sizes are not random
 				progress.filterProgress= (unsigned int)(((float)numCounted/(float)allBulkClusters.size())*100.0f);
-				if(!(*callback)(false))
+				if(*Filter::wantAbort)
 					spin=true;
 				}
 
@@ -2397,17 +2292,17 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 		}
 
 		if(spin)
-			return ABORT_ERR;
+			return FILTER_ERR_ABORT;
 	}
 	//===
 
 	progress.step++;
-	progress.stepName=TRANS("Re-Collate");
 	progress.filterProgress=0;
+	progress.stepName=TRANS("Re-Collate");
 
 	//update progress
-	if(!(*callback)(true))
-		return ABORT_ERR;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 	clusteredCore.resize(allCoreClusters.size());
 	clusteredBulk.resize(allBulkClusters.size());
 
@@ -2437,12 +2332,135 @@ unsigned int ClusterAnalysisFilter::refreshLinkClustering(const std::vector<cons
 		}
 	}
 
-	//Update progress
-	(*callback)(false);
 
 	return 0;	
 }
 
+unsigned int ClusterAnalysisFilter::buildKDTrees(vector<IonHit> &coreIons, vector<IonHit> & bulkIons,
+		K3DTreeMk2 &coreTree, K3DTreeMk2 &bulkTree, ProgressData &progress) const
+{
+		
+	coreTree.resetPts(coreIons,false);
+	if(!coreTree.build())
+		return FILTER_ERR_ABORT;
+
+	BoundCube bCore;
+	coreTree.getBoundCube(bCore);
+
+
+	if(enableCoreClassify)
+	{
+		//Perform Clustering Stage (1) : clustering classification
+		// This modifies the trees, so we have to do it here.
+		//==	
+		progress.step++;
+		progress.filterProgress=0;
+		progress.stepName=TRANS("Classify Core");
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
+		
+		vector<bool> coreOK;
+		ASSERT(coreIons.size() == coreTree.size());
+		coreOK.resize(coreTree.size());
+		float coreDistSqr=coreDist*coreDist;
+
+		//TODO: the trees internal Tags prevent us from parallelising this. 
+		//       :(. If we could pass a tag map to the tree, this would solve the problem
+		for(size_t ui=0;ui<coreTree.size();ui++)
+		{
+			const Point3D *p;
+			size_t pNN;	
+			unsigned int k;
+			vector<size_t> tagsToClear;
+		
+			//Don't match ourselves -- to do this we must "tag" this tree node before we start
+			p=coreTree.getPt(ui);
+			coreTree.tag(ui);
+			tagsToClear.push_back(ui);
+			
+			k=1;
+
+			//Loop through this ions NNs, seeing if the kth NN is within a given radius
+			do
+			{
+				pNN=coreTree.findNearestUntagged(*p,bCore,true);
+				tagsToClear.push_back(pNN);
+				k++;
+
+			}while( pNN !=(size_t)-1 && k<coreKNN);
+			
+
+			//Core is only OK if the NN is good, and within the 
+			//specified distance
+			if(pNN == (size_t)-1)
+			{
+				coreOK[coreTree.getOrigIndex(ui)]=false;
+				ASSERT(tagsToClear.back() == (size_t) -1);
+				tagsToClear.pop_back(); //get rid of the -1
+			}
+			else
+			{
+				float nnSqrDist;
+				nnSqrDist=p->sqrDist(*(coreTree.getPt(pNN)));
+				coreOK[coreTree.getOrigIndex(ui)] = nnSqrDist < coreDistSqr;
+			}
+
+
+			//reset the tags, so we can find near NNs
+			coreTree.clearTags(tagsToClear);
+			tagsToClear.clear();
+
+			progress.filterProgress= (unsigned int)(((float)ui/(float)coreTree.size())*100.0f);
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
+		}
+
+
+		for(size_t ui=coreOK.size();ui;)
+		{
+			ui--;
+
+			if(!coreOK[ui])
+			{
+				//We have to convert the core ion to a bulk ion
+				//as it is rejected.
+				bulkIons.push_back(coreIons[ui]);
+				coreIons[ui]=coreIons.back();
+				coreIons.pop_back();
+			}
+		}
+
+		//Re-Build the core KD tree
+		coreTree.resetPts(coreIons,false);
+		if(!coreTree.build())
+			return FILTER_ERR_ABORT;
+		//==	
+	}
+	coreTree.getBoundCube(bCore);
+	//----------
+
+
+
+	//Build the bulk tree (eg matrix ions.), as needed
+	if(enableBulkLink)
+	{
+		progress.step++;
+		progress.filterProgress=0;
+		progress.stepName=TRANS("Build Bulk");
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
+
+		bulkTree.resetPts(bulkIons,false);
+		if(!bulkTree.build())
+			return FILTER_ERR_ABORT;
+
+	}
+
+	return 0;
+}
+
+
+
 #ifdef DEBUG
 
 bool ClusterAnalysisFilter::paranoidDebugAssert(
@@ -2491,6 +2509,9 @@ bool ClusterAnalysisFilter::paranoidDebugAssert(
 						//What!? We Don't have an NN? How did we get 
 						//clustered in the first place? This is wrong.
 						failure=true;
+					#ifdef DEBUG	
+						using std::cerr;	
+						using std::endl;	
 						cerr << "FAILED! " << endl;
 
 						cerr << "BULK:" << bulk[ui].size() << endl;
@@ -2504,7 +2525,7 @@ bool ClusterAnalysisFilter::paranoidDebugAssert(
 						{
 							cerr << core[ui][un].getPos() << endl;
 						}
-
+					#endif
 
 						break;
 					}
@@ -2526,7 +2547,7 @@ bool ClusterAnalysisFilter::paranoidDebugAssert(
 #endif
 
 void ClusterAnalysisFilter::createRangedIons(const std::vector<const FilterStreamData *> &dataIn,vector<IonHit> &core,
-			vector<IonHit> &bulk, ProgressData &p, bool (*callback)(bool)) const
+			vector<IonHit> &bulk, ProgressData &p) const
 {
 
 	//TODO: Progress reporting and callback
@@ -2687,7 +2708,7 @@ PlotStreamData* ClusterAnalysisFilter::clusterSizeDistribution(const vector<vect
 
 bool ClusterAnalysisFilter::stripClusterBySize(vector<vector<IonHit> > &clusteredCore,
 						vector<vector<IonHit> > &clusteredBulk,
-							bool (*callback)(bool),ProgressData &progress) const
+							ProgressData &progress) const
 
 {
 
@@ -2713,18 +2734,15 @@ bool ClusterAnalysisFilter::stripClusterBySize(vector<vector<IonHit> > &clustere
 				clusteredBulk[ui].swap(clusteredBulk.back());
 				clusteredBulk.pop_back();
 			}
-			if(!(ui%PROGRESS_REDUCE)  && clusteredCore.size())
-			{
-				progress.filterProgress= (unsigned int)(((float)ui/(float)clusteredCore.size()+1)*100.0f);
+			progress.filterProgress= (unsigned int)(((float)ui/(float)clusteredCore.size()+1)*100.0f);
 				
-				if(!(*callback)(false))
-					return ABORT_ERR;
-			}
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
 		}
 	}
 	else
 	{
-		//OK, we haven't any bulkbut we just want to count core;
+		//OK, we haven't any bulk, but we just want to count core;
 		//but operate on both
 		for(size_t ui=clusteredCore.size();ui;)
 		{
@@ -2735,13 +2753,11 @@ bool ClusterAnalysisFilter::stripClusterBySize(vector<vector<IonHit> > &clustere
 				clusteredCore[ui].swap(clusteredCore.back());
 				clusteredCore.pop_back();
 			}
-			if(!(ui%PROGRESS_REDUCE) )
-			{
-				progress.filterProgress= (unsigned int)(((float)ui/(float)clusteredCore.size()+1)*100.0f);
-				
-				if(!(*callback)(false))
-					return ABORT_ERR;
-			}
+
+			progress.filterProgress= (unsigned int)(((float)ui/(float)clusteredCore.size()+1)*100.0f);
+			
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
 		}
 
 	}
@@ -2913,167 +2929,175 @@ void ClusterAnalysisFilter::genCompositionVersusSize(const vector<vector<IonHit>
 
 }
 
-/*
-void ClusterAnalysisFilter::getSingularValues(const vector<vector<IonHit> > &clusteredCore, 
-	const vector<vector<IonHit> > &clusteredBulk, vector<vector<float> > &singularValues,
-		vector<std::pair<Point3D,vector<Point3D> > > &singularVectors) const
-{
-	const unsigned int DIMENSION=3;
+//Sudbrack, C. : Decomposition behavior in model Ni-Al-Cr-X superalloys: 
+// temporal evolution and compositional pathways on a nanoscale (Ph.D. Thesis, 2004) 
+//http://arc.nucapt.northwestern.edu/refbase/files/Sudbrack_Ph.D._thesis_2004_6MB.pdf 
+//============
 
-	float *data=0;
-	size_t dataSize=0;
-	if(clusteredBulk.size())
+//un-normalised deviation matrix summation (L, equation A1.1)
+void computeMatrixEntries(const vector<IonHit> &atoms,const Point3D &clusterCentre,  gsl_matrix *m)
+{
+	//TODO: ASsert matrix is 3x3
+	//Fill the data array with deviation vectors
+	for(size_t ui=0;ui<atoms.size();ui++)
 	{
-		ASSERT(clusteredCore.size() == clusteredBulk.size());
-//#pragma omp parallel for shared(singularValues,clusteredCore,clusteredBulk) private(data,dataSize)
-		for(unsigned int ui=0;ui<clusteredCore.size(); ui++)
-		{
-			vector<float> curSingularVals;
-			vector<Point3D> curSingularBases;
+		Point3D delta;
+		delta=atoms[ui].getPos() - clusterCentre;
+		//compute diagonal terms
+		for(size_t uj=0;uj<3;uj++)
+		{
+			//Stephenson matrix and sudbrack matrix dont match
+			// on main diagonal ?
+			// - stephenson matrix has error, as does sudbrack matrix (in writing, not code) 
+			unsigned int a,b;
+			a= (uj+1)%3; //for ui=0 [a b] <=>[y,z]; similarly,  {x,z}, {x,y}
+			b= (uj+2)%3;
+			
+			float v1,v2,vRes;	
+			v1 = delta[a];
+			v2 = delta[b];
+			vRes=v1*v1+v2*v2;
+			float v;
+			v=gsl_matrix_get(m,uj,uj);
+			v+=vRes;
+			gsl_matrix_set(m,uj,uj,v);
+		}
+
+		//compute off-diagonal terms. Note matrix is symmetric,
+		// so we only need to compute xy,xz and yz 
+		// Written equation in sudbrack thesis is incorrect, and mixes distance^4 and
+		// distance^2
+		float v;
+		//xy
+		v= gsl_matrix_get(m,0,1);
+		v-=delta[0]*delta[1];
+		gsl_matrix_set(m,0,1,v);
+		//xz
+		v= gsl_matrix_get(m,0,2);
+		v-=delta[0]*delta[2];
+		gsl_matrix_set(m,0,2,v);
+		//yz
+		v= gsl_matrix_get(m,1,2);
+		v-=delta[1]*delta[2];
+		gsl_matrix_set(m,1,2,v);
+
+	}
 
-			curSingularVals.clear();
-			curSingularBases.clear();
-			//For this cluster, compute its singular values amplitudes
-			// (ignore direction)
-			size_t numEntries = clusteredCore[ui].size() + clusteredBulk[ui].size();
 
+	//Mirror the off-diagonal terms
+	float v;
+	//yx
+	v= gsl_matrix_get(m,0,1);
+	gsl_matrix_set(m,1,0,v);
+	//zx
+	v= gsl_matrix_get(m,0,2);
+	gsl_matrix_set(m,2,0,v);
+	//zy
+	v= gsl_matrix_get(m,1,2);
+	gsl_matrix_set(m,2,1,v);
+}
 
-			//Check to see if we have sufficient data to compute an SVD
-			if(numEntries <=DIMENSION )
-			{
-#pragma omp critical
-				{
-				singularValues.push_back(curSingularVals);
-				singularVectors.push_back(make_pair(Point3D(0,0,0),curSingularBases));
-				}
-				continue; 
-			}
-			
-			//Allocate space, by growing as needed
-			if(numEntries*DIMENSION > dataSize)
-			{
-				//TODO: Is it faster to pre-compute the maximum cluster size?
-				//      then determine the cluster backbone from that? 
-				//      Should we be using realloc?
-				if(data)
-					delete[] data;
-				data = new float[numEntries*DIMENSION];
-				dataSize=numEntries*DIMENSION;
-			}
+// NOTE: This is not the enclosing ellipse. For that, see:
+// Nima Moshtagh - "MINIMUM VOLUME ENCLOSING ELLIPSOIDS", U.Penn. 
+// 10.1.1.116.7691.
+void ClusterAnalysisFilter::getEllipsoidalFit(const vector<IonHit> &coreAtoms, const vector<IonHit> &bulkAtoms,
+		std::pair< Point3D, vector<Point3D> > &ellipseData) 
+{
 
-			//Compute the cluster's centre of mass (assuming unit mass per object)
-			Point3D centroid[2];
-			IonHit::getCentroid(clusteredCore[ui],centroid[0]);
-			IonHit::getCentroid(clusteredBulk[ui],centroid[1]);
-		
-			Point3D clusterCentre;
-			clusterCentre= centroid[0]+centroid[1];
-			clusterCentre*=1.0f/(float)(clusteredCore[ui].size()+clusteredBulk[ui].size());
-			
-			//Fill the data array
-			float *p;
-			p=data;
-			for(size_t uj=0;uj<clusteredCore[ui].size();uj++)
-			{
-				for(size_t uk=0;uk<DIMENSION;uk++)
-				{
-					*(p) = clusteredCore[ui][uj].getPos()[uk]-clusterCentre[uk];
-					p++;
-				}
-			}
-			
-			for(size_t uj=0;uj<clusteredBulk[ui].size();uj++)
-			{
-				for(size_t uk=0;uk<DIMENSION;uk++)
-				{
-					*(p) = clusteredBulk[ui][uj].getPos()[uk]-clusterCentre[uk];
-					p++;
-				}
-			}
 
-			//Cmpute the DIMENSION-D singular value decomposition for
-			//this vector set
-//			computeSingularValues(data,numEntries,
-//						DIMENSION,curSingularVals,curSingularBases);
+	
+	gsl_matrix *m = gsl_matrix_alloc(3,3);
+	gsl_matrix_set_zero(m);
 
-#pragma omp critical
-			{
-			singularValues.push_back(curSingularVals);
-			singularVectors.push_back(make_pair(clusterCentre,curSingularBases));
-			}
 
-		}
+	Point3D clusterCentre;
+	if(bulkAtoms.size())
+	{	
+		//Compute the cluster's centre of mass (assuming unit mass per object)
+		//---
+		Point3D centroid[2];
+		IonHit::getCentroid(coreAtoms,centroid[0]);
+		IonHit::getCentroid(bulkAtoms,centroid[1]);
+
+		//compute overall centroid
+		float coreFactor,bulkFactor;
+		coreFactor = coreAtoms.size()/(float)(coreAtoms.size() + bulkAtoms.size());
+		bulkFactor = bulkAtoms.size()/(float)(coreAtoms.size() + bulkAtoms.size());
+		clusterCentre= centroid[0]*coreFactor + centroid[1]*bulkFactor;
+		//---
+	
+		//compute the components of the distance deviation matrix	
+		computeMatrixEntries(coreAtoms,clusterCentre,m);
+		computeMatrixEntries(bulkAtoms,clusterCentre,m);
+
 	}
 	else
 	{
+		IonHit::getCentroid(coreAtoms,clusterCentre);
+		computeMatrixEntries(coreAtoms,clusterCentre,m);
+	}	
 
-//#pragma omp parallel for shared(singularValues,clusteredCore,clusteredBulk) private(data,dataSize)
-		for(unsigned int ui=0;ui<clusteredCore.size(); ui++)
-		{
-			vector<float> curSingularVals;
-			vector<Point3D> curSingularBases;
+	//normalise matrix entries	
+	gsl_matrix_scale(m,1.0/(double)(coreAtoms.size() + bulkAtoms.size()));
 
-			curSingularVals.clear();
-			curSingularBases.clear();
-			//For this cluster, compute its singular values amplitudes
-			// (ignore direction)
-			size_t numEntries = clusteredCore[ui].size(); 
+	//std::cerr << "Fit matrix is :" << std::endl;
+	//gslPrint(m);
 
+	//compute SVD to obtain eigenvalues
+	vector<float> vals;
+	vector<Point3D> pts;
+	computeEigenValues(m,3,3,vals,pts);
 
-			//Check to see if we have sufficient data to compute an SVD
-			if(numEntries <=DIMENSION )
-			{
-#pragma omp critical
-				{
-				singularValues.push_back(curSingularVals);
-				singularVectors.push_back(make_pair(Point3D(0,0,0),curSingularBases));
-				}
-				continue; 
-			}
-			//Compute the cluster's centre of mass (assuming unit mass per object)
-			Point3D centroid;
-			IonHit::getCentroid(clusteredCore[ui],centroid);
-			centroid*=1.0f/(float)(clusteredCore[ui].size());
-			//Allocate space, by growing as needed
-			if(numEntries*DIMENSION > dataSize)
-			{
-				//TODO: Is it faster to pre-compute the maximum cluster size?
-				//      then determine the cluster backbone from that? 
-				//      Should we be using realloc?
-				if(data)
-					delete[] data;
-				data = new float[numEntries*DIMENSION];
-				dataSize=numEntries*DIMENSION;
-			}
+	gsl_matrix_free(m);
 
-			//Fill the data array
-			for(size_t uj=0;uj<clusteredCore[ui].size();uj++)
-			{
-				for(size_t uk=0;uk<DIMENSION;uk++)
-					data[uj*DIMENSION + uk] = clusteredCore[ui][uj].getPos()[uk];
-			}
-			//Cmpute the DIMENSION-D singular value decomposition for
-			//this vector set, and the basis vectors 
-			computeSingularValues(data,numEntries,
-				DIMENSION,curSingularVals,curSingularBases);
+	//Convert eigenvalues to their positive form
+	 
 
-#pragma omp critical
-			{
-			singularValues.push_back(curSingularVals);
-			singularVectors.push_back(make_pair(centroid,curSingularBases));
-			}
+	//Check that the values are sorted in *descending* order (e1 >=e2 >=e3)
+	ASSERT(vals[0] >=vals[1] && vals[1]>=vals[2]);
+
+	//Convert to semi-axes of ellipse
+	ellipseData.second.resize(3,Point3D(0,0,0));
+
+	float semiAxes[3];
+	for(size_t ui=0;ui<3;ui++)
+	{
+		unsigned int a,b,c;
+		a=ui;
+		b=(ui+1)%3;
+		c=(ui+2)%3;
+
+	
+		if(vals[b] + vals[c] > vals[a])
+		{
+			//sudbrack's example code does something different to the equations, and multiples by 2,
+			// perhaps obtaining full diameter axis, rather than semi.
+			semiAxes[ui]=sqrt(5.0/2.0*(vals[b] + vals[c] - vals[a]));
+		}
+		else
+		{
+			WARN(true,"Imaginary semi axis value - zeroing");
+			semiAxes[ui] =0;
 		}
 	}
 
-	if(data)
-		delete[] data;
+	for(unsigned int ui=0;ui<3;ui++)
+	{
+		pts[ui]*=semiAxes[ui];
+	}
+
+	ellipseData.first=clusterCentre;
+	ellipseData.second.swap(pts);
 }
-*/
+//============
+
 
 #ifdef DEBUG
 
 #include <memory>
 
+using std::auto_ptr;
+
 //Cluster Ids for generating cluster test datasets with genCluster
 enum
 {
@@ -3094,6 +3118,7 @@ bool isolatedClusterTest();
 //Test the core mode of the core-link clustering algorithm
 bool coreClusterTest();
 
+
 //Unit tests
 bool ClusterAnalysisFilter::runUnitTests()
 {
@@ -3101,6 +3126,8 @@ bool ClusterAnalysisFilter::runUnitTests()
 		return false;
 
 	if(!coreClusterTest())
+		return false;
+	if(!singularValueTest())
 		return false;	
 	return true;
 }
@@ -3263,7 +3290,7 @@ bool isolatedClusterTest()
 
 		//Do the refresh
 		ProgressData p;
-		TEST(!(f->refresh(streamIn,streamOut,p,dummyCallback)),"Refresh err code");
+		TEST(!(f->refresh(streamIn,streamOut,p)),"Refresh err code");
 
 		//Kill the input ion stream, and remove old pointer
 		delete d; 
@@ -3375,7 +3402,7 @@ bool coreClusterTest()
 
 	//Do the refresh
 	ProgressData p;
-	TEST(!(f->refresh(streamIn,streamOut,p,dummyCallback)),"Refresh err code");
+	TEST(!(f->refresh(streamIn,streamOut,p)),"Refresh err code");
 	delete f;
 	delete ionData;
 	delete rng;
@@ -3392,6 +3419,113 @@ bool coreClusterTest()
 	return true;	
 }
 
+bool ClusterAnalysisFilter::singularValueTest()
+{
+
+	gsl_matrix *m = gsl_matrix_alloc(3,3);
+	gsl_matrix_set(m,0,0,1);
+	gsl_matrix_set(m,0,1,3);
+	gsl_matrix_set(m,0,2,0);
+	
+
+	gsl_matrix_set(m,1,0,3);
+	gsl_matrix_set(m,1,1,-3);
+	gsl_matrix_set(m,1,2,2);
+
+	gsl_matrix_set(m,2,0,0);
+	gsl_matrix_set(m,2,1,2);
+	gsl_matrix_set(m,2,2,3);
+
+	vector<float> vals;
+	vector<Point3D> pts;
+	computeEigenValues(m,3,3,vals,pts);
+	gsl_matrix_free(m);
+
+	TEST(vals.size() == pts.size(),"Vector sizes");
+	TEST(vals.size() == 3,"vector size");
+
+	//check eigen values, sorted in desc. order
+	TEST(EQ_TOL(vals[0] ,4.0) ,"Correct eigen value");
+	TEST(EQ_TOL(vals[1] ,2.0) ,"Correct eigen value");
+	TEST(EQ_TOL(vals[2] ,-5.000),"Correct eigen value");
+
+
+	pts.clear();
+
+	//check the matrix makes sense
+	vector<IonHit> ionVec;
+	ionVec.push_back(IonHit(Point3D(0,0,1),1));	
+	ionVec.push_back(IonHit(Point3D(0,0,-1),1));
+	ionVec.push_back(IonHit(Point3D(1,0,0),1));	
+	ionVec.push_back(IonHit(Point3D(-1,0,0),1));
+	ionVec.push_back(IonHit(Point3D(0,1,0),1));	
+	ionVec.push_back(IonHit(Point3D(0,-1,0),1));
+
+	m = gsl_matrix_alloc(3,3);
+	gsl_matrix_set_zero(m);
+	computeMatrixEntries(ionVec,Point3D(0,0,0),m);
+	//gslPrint(m);
+	gsl_matrix_free(m);
+	{
+	vector<IonHit> dummy;
+	pair<Point3D, vector<Point3D> > resultEllipse;
+	getEllipsoidalFit(ionVec,dummy, resultEllipse);
+	for(size_t ui=0;ui<resultEllipse.second.size() ;ui++)
+	{
+		ASSERT(EQ_TOL(resultEllipse.second[ui].sqrMag(), 5.0/2.0*2.0/3.0));
+		
+	}
+	}
+	ionVec.clear();
+	pts.clear();
+
+	//generate some random points in an ellipse
+	RandNumGen rng;
+	rng.initTimer();
+
+	const float SEMI_AXIS[] = {1,2,3};
+	const unsigned int NUM_PTS = 10000;
+	
+	
+	Point3D p;
+	while(ionVec.size() < NUM_PTS)	
+	{
+
+		float f[3];
+		for(unsigned int ui=0;ui<3;ui++)
+		{
+			f[ui]=rng.genUniformDev()-0.5f;
+			ASSERT(f[ui] <0.51 && f[ui] >-0.51);
+		}
+	      //generate some random points, then check for inside ellipse
+		p = Point3D(2.0*SEMI_AXIS[0]*f[0], 2.0*SEMI_AXIS[1]*f[1],
+				2.0*SEMI_AXIS[2]*f[2]);
+
+		//check if the pt is outside the ellipsoid
+		Point3D ellipsePt;
+		for(size_t ui=0;ui<3;ui++)
+			ellipsePt[ui]=p[ui]/SEMI_AXIS[ui];
+
+		if( ellipsePt.sqrMag() < 1.0f)
+			ionVec.push_back(IonHit(p,1.0));
+	}
+
+	IonHit::makePos(ionVec,"test-ellipsoid.pos");
+
+
+	//Run the ellipsoidal fit. This will crash if any NaNs or imag. values
+	// are generated.
+	vector<IonHit> dummy;
+	pair<Point3D, vector<Point3D> > resultEllipse;
+	getEllipsoidalFit(ionVec,dummy, resultEllipse);
+
+	for(size_t ui=0;ui<3;ui++)
+	{
+		TEST(fabs(sqrtf(resultEllipse.second[ui].sqrMag()) -SEMI_AXIS[ui]) < 0.25f,"Semi axes retrieval");
+	}
+
+	return true;
+}
 
 
 #endif
diff --git a/src/backend/filters/clusterAnalysis.h b/src/backend/filters/clusterAnalysis.h
index 266bdff..0ffa88e 100644
--- a/src/backend/filters/clusterAnalysis.h
+++ b/src/backend/filters/clusterAnalysis.h
@@ -1,6 +1,6 @@
 /*
  *	clusterAnalysis.h - Cluster analysis on valued point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -20,7 +20,12 @@
 #include "../filter.h"
 #include "../../common/translation.h"
 
+#include "algorithms/K3DTree-mk2.h"
+
 #include <map>
+#include <vector>
+
+
 
 //!Cluster analysis filter
 class ClusterAnalysisFilter : public Filter
@@ -54,7 +59,6 @@ class ClusterAnalysisFilter : public Filter
 		//convert clusters mass to an ID #?
 		bool wantClusterID;
 		//---	
-
 		//post processing options
 		//Minimum/max number of "core" entires to qualify as,
 		//well, a meaningful cluster
@@ -77,51 +81,54 @@ class ClusterAnalysisFilter : public Filter
 		//!Which ions are core/builk for a  particular incoming range?
 		std::vector<bool> ionCoreEnabled,ionBulkEnabled;
 
+
+		unsigned int buildKDTrees(std::vector<IonHit> &coreIons, std::vector<IonHit> &bulkIons,K3DTreeMk2 &coreTree,K3DTreeMk2 &bulkTree, ProgressData &prog) const;
+
 		//Do cluster refresh using Link Algorithm (Core + max sep)
 		unsigned int refreshLinkClustering(const std::vector<const FilterStreamData *> &dataIn,
 				std::vector< std::vector<IonHit> > &clusteredCore, 
-				std::vector<std::vector<IonHit>  > &clusteredBulk,ProgressData &progress,
-					       		bool (*callback)(bool));
+				std::vector<std::vector<IonHit>  > &clusteredBulk,ProgressData &progress);
 
 
-		//Helper function to create core and bulk vectors of ions from input ionstreams
+		//Helper function to create core and bulk std::vectors of ions from input ionstreams
 		void createRangedIons(const std::vector<const FilterStreamData *> &dataIn,
 						std::vector<IonHit> &core,std::vector<IonHit> &bulk,
-					       		ProgressData &p,bool (*callback)(bool)) const;
+					       		ProgressData &p) const;
 
 
 		//Check to see if there are any core or bulk ions enabled respectively.
 		void checkIonEnabled(bool &core, bool &bulk) const;
 
 		static void buildRangeEnabledMap(const RangeStreamData *r,
-					map<size_t,size_t> &rangeEnabledMap);
+					std::map<size_t,size_t> &rangeEnabledMap);
 
 		//Strip out clusters with a given number of elements
-		bool stripClusterBySize(vector<vector<IonHit> > &clusteredCore,
-						vector<vector<IonHit> > &clusteredBulk,
-							bool (*callback)(bool), ProgressData &p) const;
+		bool stripClusterBySize(std::vector<std::vector<IonHit> > &clusteredCore,
+						std::vector<std::vector<IonHit> > &clusteredBulk,
+							ProgressData &p) const;
 		//Build a plot that is the cluster size distribution as a function of cluster size
-		PlotStreamData *clusterSizeDistribution(const vector<vector<IonHit> > &solutes, 
-						const vector<vector<IonHit> > &matrix) const;
+		PlotStreamData *clusterSizeDistribution(const std::vector<std::vector<IonHit> > &solutes, 
+						const std::vector<std::vector<IonHit> > &matrix) const;
 
 
 		//Build plots that are the cluster size distribution as
 		// a function of cluster size, specific to each ion type.
-		void genCompositionVersusSize(const vector<vector<IonHit> > &clusteredCore,
-				const vector<vector<IonHit> > &clusteredBulk, const RangeFile *rng,
-							vector<PlotStreamData *> &plots) const;
+		void genCompositionVersusSize(const std::vector<std::vector<IonHit> > &clusteredCore,
+				const std::vector<std::vector<IonHit> > &clusteredBulk, const RangeFile *rng,
+							std::vector<PlotStreamData *> &plots) const;
 
 #ifdef DEBUG
 		bool paranoidDebugAssert(const std::vector<std::vector<IonHit > > &core, 
 				const std::vector<std::vector<IonHit> > &bulk) const;
+		
+		//Check to see if the singular value routine is working
+		static bool singularValueTest(); 
+
 #endif
-#ifdef DEBUG
-	public:
-#endif
-		//COmpute the singular values that area associated with each cluster
-		void getSingularValues(const vector<vector<IonHit> > &clusteredCore,
-				const vector<vector<IonHit> > &clusteredBulk, vector<vector<float> > &singularValues,
-				vector<std::pair<Point3D,vector<Point3D> > > &singularVectors) const;
+		///Find the best fit ellipse, per Karnesky et al to a set of IonHit events. Returned values are a pair : [ centroid, vector<semiaxes of ellipse> ]
+		static void getEllipsoidalFit(const std::vector<IonHit> &coreAtoms, const std::vector<IonHit> &bulkAtoms,
+
+		std::pair< Point3D, std::vector<Point3D> > &ellipseData);
 	public:
 		ClusterAnalysisFilter(); 
 		//!Duplicate filter contents, excluding cache.
@@ -138,13 +145,13 @@ class ClusterAnalysisFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 					std::vector<const FilterStreamData *> &getOut, 
-					ProgressData &progress, bool (*callback)(bool));
+					ProgressData &progress);
 		//!Get the type string  for this filter
 		virtual std::string typeString() const { return std::string(TRANS("Cluster Analysis"));};
 
-		std::string getErrString(unsigned int i) const;
+		std::string getSpecificErrString(unsigned int i) const;
 
-		//!Get the properties of the filter, in key-value form. First vector is for each output.
+		//!Get the properties of the filter, in key-value form. First std::vector is for each output.
 		void getProperties(FilterPropGroup &propertyList) const;
 
 		//!Set the properties for the nth filter
diff --git a/src/backend/filters/compositionProfile.cpp b/src/backend/filters/compositionProfile.cpp
index f0b7c75..b613e37 100644
--- a/src/backend/filters/compositionProfile.cpp
+++ b/src/backend/filters/compositionProfile.cpp
@@ -1,6 +1,6 @@
 /*
  *	compositionProfile.cpp - Compute composition profiles from valued point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -21,15 +21,23 @@
 #include "filterCommon.h"
 #include "geometryHelpers.h"
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::map;
+
 
 //!Possible primitive types for composition profiles
 enum
 {
-	PRIMITIVE_CYLINDER,
+	PRIMITIVE_CYLINDER_AXIAL,
+	PRIMITIVE_CYLINDER_RADIAL,
 	PRIMITIVE_SPHERE,
 	PRIMITIVE_END, //Not actually a primitive, just end of enum
 };
 
+
 //!Error codes
 enum
 {
@@ -40,7 +48,8 @@ enum
 };
 
 const char *PRIMITIVE_NAME[]={
-	NTRANS("Cylinder"),
+	NTRANS("Cylinder (axial)"),
+	NTRANS("Cylinder (radial)"),
 	NTRANS("Sphere")
 };
 
@@ -49,7 +58,7 @@ const float DEFAULT_RADIUS = 10.0f;
 const unsigned int MINEVENTS_DEFAULT =10;
 
 
-CompositionProfileFilter::CompositionProfileFilter() : primitiveType(PRIMITIVE_CYLINDER),
+CompositionProfileFilter::CompositionProfileFilter() : primitiveType(PRIMITIVE_CYLINDER_AXIAL),
 	showPrimitive(true), lockAxisMag(false),normalise(true), fixedBins(0),
 	nBins(1000), binWidth(0.5f), minEvents(MINEVENTS_DEFAULT), rgba(0,0,1), plotStyle(0)
 {
@@ -65,7 +74,8 @@ CompositionProfileFilter::CompositionProfileFilter() : primitiveType(PRIMITIVE_C
 	haveRangeParent=false;
 }
 
-
+//Puts an ion in its appropriate range position, given ionID mapping,
+//range data (if any), mass to charge and the output table
 void CompositionProfileFilter::binIon(unsigned int targetBin, const RangeStreamData* rng, 
 	const map<unsigned int,unsigned int> &ionIDMapping,
 	vector<vector<size_t> > &frequencyTable, float massToCharge) 
@@ -127,7 +137,7 @@ Filter *CompositionProfileFilter::cloneUncached() const
 	p->nBins = nBins;
 	p->plotStyle=plotStyle;
 	p->errMode=errMode;
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -151,8 +161,7 @@ void CompositionProfileFilter::initFilter(const std::vector<const FilterStreamDa
 }
 
 unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-			std::vector<const FilterStreamData *> &getOut, ProgressData &progress, 
-								bool (*callback)(bool))
+			std::vector<const FilterStreamData *> &getOut, ProgressData &progress) 
 {
 	//Clear selection devices
 	// FIXME: Leaking drawables.
@@ -166,7 +175,8 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 		drawData->parent=this;
 		switch(primitiveType)
 		{
-			case PRIMITIVE_CYLINDER:
+			case PRIMITIVE_CYLINDER_AXIAL:
+			case PRIMITIVE_CYLINDER_RADIAL:
 			{
 				//Origin + normal
 				ASSERT(vectorParams.size() == 2);
@@ -176,7 +186,7 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 				dC->setRadius(scalarParams[0]);
 				dC->setColour(0.5,0.5,0.5,0.3);
 				dC->setSlices(40);
-				dC->setLength(sqrt(vectorParams[1].sqrMag())*2.0f);
+				dC->setLength(sqrtf(vectorParams[1].sqrMag())*2.0f);
 				dC->setDirection(vectorParams[1]);
 				dC->wantsLight=true;
 				drawData->drawables.push_back(dC);
@@ -307,7 +317,7 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 
 		ASSERT(filterOutputs.back()->getStreamType() == STREAM_TYPE_PLOT);
 
-			
+		progress.filterProgress=100;
 		return 0;
 	}
 			
@@ -325,12 +335,14 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 		}
 	}
 
-	float length;
 	unsigned int numBins, errCode;
+	{
+	float length;
 	errCode=getBinData(numBins,length);
 
 	if(!numBins)
 		return 0;
+	}
 
 	if(errCode)
 		return errCode;
@@ -397,11 +409,12 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 	size_t totalSize=numElements(dataIn);
 
 	map<size_t,size_t> primitiveMap;
-	primitiveMap[PRIMITIVE_CYLINDER] = CROP_CYLINDER_INSIDE;
+	primitiveMap[PRIMITIVE_CYLINDER_AXIAL] = CROP_CYLINDER_INSIDE_AXIAL;
+	primitiveMap[PRIMITIVE_CYLINDER_RADIAL] = CROP_CYLINDER_INSIDE_RADIAL;
 	primitiveMap[PRIMITIVE_SPHERE] = CROP_SPHERE_INSIDE;
 
-	CropHelper dataMapping(callback,&progress.filterProgress,
-			totalSize,primitiveMap[primitiveType], vectorParams,scalarParams  );
+	CropHelper dataMapping(totalSize,primitiveMap[primitiveType], 
+					vectorParams,scalarParams  );
 	dataMapping.setMapMaxima(numBins);
 
 	unsigned int curProg=NUM_CALLBACK;
@@ -448,7 +461,7 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 						n+=NUM_CALLBACK;
 						progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
 						curProg=NUM_CALLBACK;
-						if(!(*callback)(false))
+						if(*Filter::wantAbort)
 						{
 							#ifdef _OPENMP
 								spin=true;
@@ -495,29 +508,39 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 	//Perform the appropriate normalisation
 	if(!rngData && normalise)
 	{
-		float dx;
-		if(fixedBins)
-			dx=(length/(float)numBins);
-		else
-			dx=binWidth;
 		// For density plots, normalise by
 		//  the volume of the primitive's shell
 		switch(primitiveType)
 		{
-			case PRIMITIVE_CYLINDER:
+			case PRIMITIVE_CYLINDER_AXIAL:
+			case PRIMITIVE_CYLINDER_RADIAL:
 			{
+
+				float dx;
+				if(fixedBins)
+					dx=(sqrtf(vectorParams[1].sqrMag())/(float)numBins);
+
+				else
+					dx=binWidth;
 				needNormalise=true;
-				for(unsigned int uj=0;uj<ionFrequencies[0].size(); uj++)
-				{
-					//Normalise by cylinder volume, pi*r^2*h
-					normalisationFactor[uj] = 1.0/(M_PI*
-						scalarParams[0]*scalarParams[0]*dx);
-				}
+				float nFact;
+				//Normalise by cylinder slice volume, pi*r^2*h.
+				// This is the same in both radial and axial mode as the radial slices are equi-volume, 
+				// same as axial mode
+				nFact=1.0/(M_PI*scalarParams[0]*scalarParams[0]*dx);
+				for(unsigned int uj=0;uj<normalisationFactor.size(); uj++)
+					normalisationFactor[uj] = nFact;
 				break;
 			}
 			case PRIMITIVE_SPHERE:
 			{
-				for(unsigned int uj=0;uj<ionFrequencies[0].size(); uj++)
+				float dx;
+				if(fixedBins)
+					dx=(scalarParams[0]/(float)numBins);
+
+				else
+					dx=binWidth;
+				for(unsigned int uj=0;uj<normalisationFactor.size(); uj++)
 				{
 					//Normalise by sphere shell volume, 
 					// 4/3 *PI*dx^3*((n+1)^3-n^3)
@@ -616,7 +639,7 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 		for(unsigned int uj=0;uj<ionFrequencies[ui].size(); uj++)
 		{
 			float xPos;
-			xPos = (((float)uj+0.5f)/(float)ionFrequencies[ui].size())*length;
+			xPos = getBinPosition(uj);
 
 			if(ionFrequencies[ui][uj] < minEvents)
 				continue;
@@ -662,7 +685,7 @@ unsigned int CompositionProfileFilter::refresh(const std::vector<const FilterStr
 	return 0;
 }
 
-std::string  CompositionProfileFilter::getErrString(unsigned int code) const
+std::string  CompositionProfileFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errCodes[] =   { "",
 		"Too many bins in comp. profile.",
@@ -688,7 +711,7 @@ bool CompositionProfileFilter::setProperty( unsigned int key,
 			if(stream_cast(newBinWidth,value))
 				return false;
 
-			if(newBinWidth < sqrt(std::numeric_limits<float>::epsilon()))
+			if(newBinWidth < sqrtf(std::numeric_limits<float>::epsilon()))
 				return false;
 
 			binWidth=newBinWidth;
@@ -708,16 +731,16 @@ bool CompositionProfileFilter::setProperty( unsigned int key,
 			if(!newPt.parse(value))
 				return false;
 
-			if(primitiveType == PRIMITIVE_CYLINDER)
+			if(primitiveType == PRIMITIVE_CYLINDER_AXIAL)
 			{
 				if(lockAxisMag && 
-					newPt.sqrMag() > sqrt(std::numeric_limits<float>::epsilon()))
+					newPt.sqrMag() > sqrtf(std::numeric_limits<float>::epsilon()))
 				{
 					newPt.normalise();
-					newPt*=sqrt(vectorParams[1].sqrMag());
+					newPt*=sqrtf(vectorParams[1].sqrMag());
 				}
 			}
-			if(newPt.sqrMag() < sqrt(std::numeric_limits<float>::epsilon()))
+			if(newPt.sqrMag() < sqrtf(std::numeric_limits<float>::epsilon()))
 				return false;
 
 			if(!(vectorParams[1] == newPt ))
@@ -770,7 +793,8 @@ bool CompositionProfileFilter::setProperty( unsigned int key,
 			// preserving data where possible
 			switch(primitiveType)
 			{
-				case PRIMITIVE_CYLINDER:
+				case PRIMITIVE_CYLINDER_AXIAL:
+				case PRIMITIVE_CYLINDER_RADIAL:
 				{
 					if(vectorParams.size() != 2)
 					{
@@ -794,6 +818,10 @@ bool CompositionProfileFilter::setProperty( unsigned int key,
 						else
 							scalarParams.resize(1);
 					}
+
+					if(primitiveType == PRIMITIVE_CYLINDER_RADIAL)
+						fixedBins=true;
+
 					break;
 				}
 				case PRIMITIVE_SPHERE:
@@ -830,7 +858,7 @@ bool CompositionProfileFilter::setProperty( unsigned int key,
 			if(stream_cast(newRad,value))
 				return false;
 
-			if(newRad < sqrt(std::numeric_limits<float>::epsilon()))
+			if(newRad < sqrtf(std::numeric_limits<float>::epsilon()))
 				return false;
 
 			if(scalarParams[0] != newRad )
@@ -955,7 +983,8 @@ void CompositionProfileFilter::getProperties(FilterPropGroup &propertyList) cons
 
 	switch(primitiveType)
 	{
-		case PRIMITIVE_CYLINDER:
+		case PRIMITIVE_CYLINDER_AXIAL:
+		case PRIMITIVE_CYLINDER_RADIAL:
 		{
 			ASSERT(vectorParams.size() == 2);
 			ASSERT(scalarParams.size() == 1);
@@ -1018,13 +1047,17 @@ void CompositionProfileFilter::getProperties(FilterPropGroup &propertyList) cons
 			ASSERT(false);
 	}
 
-	p.key=COMPOSITION_KEY_FIXEDBINS;
-	stream_cast(str,fixedBins);
-	p.name=TRANS("Fixed Bin Num");
-	p.data=str;
-	p.type=PROPERTY_TYPE_BOOL;
-	p.helpText=TRANS("If true, use a fixed number of bins for profile, otherwise use fixed step size");
-	propertyList.addProperty(p,curGroup);
+	//Must be fixed bin num in radial mode. Disallow turning this off
+	if(primitiveType!= PRIMITIVE_CYLINDER_RADIAL)
+	{
+		p.key=COMPOSITION_KEY_FIXEDBINS;
+		stream_cast(str,fixedBins);
+		p.name=TRANS("Fixed Bin Num");
+		p.data=str;
+		p.type=PROPERTY_TYPE_BOOL;
+		p.helpText=TRANS("If true, use a fixed number of bins for profile, otherwise use fixed step size");
+		propertyList.addProperty(p,curGroup);
+	}
 
 	if(fixedBins)
 	{
@@ -1039,6 +1072,7 @@ void CompositionProfileFilter::getProperties(FilterPropGroup &propertyList) cons
 	}
 	else
 	{
+		ASSERT(primitiveType!=PRIMITIVE_CYLINDER_RADIAL);
 		str = TRANS("Bin width");
 		stream_cast(tmpStr,binWidth);
 		p.name=str;
@@ -1146,9 +1180,13 @@ unsigned int CompositionProfileFilter::getBinData(unsigned int &numBins, float &
 			//radius of sphere
 			length=scalarParams[0];
 			break;
-		case PRIMITIVE_CYLINDER:
+		case PRIMITIVE_CYLINDER_AXIAL:
 			//length of cylinder, full axis length
-			length=sqrt(vectorParams[1].sqrMag());
+			length=sqrtf(vectorParams[1].sqrMag());
+			break;
+		case PRIMITIVE_CYLINDER_RADIAL:
+			//radius of cylinder
+			length =scalarParams[0];
 			break;
 		default:
 			ASSERT(false);
@@ -1160,7 +1198,8 @@ unsigned int CompositionProfileFilter::getBinData(unsigned int &numBins, float &
 	{
 		switch(primitiveType)
 		{
-			case PRIMITIVE_CYLINDER:
+			case PRIMITIVE_CYLINDER_AXIAL:
+			case PRIMITIVE_CYLINDER_RADIAL:
 			case PRIMITIVE_SPHERE:
 			{
 
@@ -1182,6 +1221,26 @@ unsigned int CompositionProfileFilter::getBinData(unsigned int &numBins, float &
 	return 0;
 }
 
+float CompositionProfileFilter::getBinPosition(unsigned int nBin) const
+{
+	unsigned int nBinsMax; float fullLen, xPos;
+	getBinData(nBinsMax,fullLen);	
+	ASSERT(nBin < nBinsMax)
+	xPos = ((float) nBin + 0.5)/(float)nBinsMax;
+	if( primitiveType == PRIMITIVE_CYLINDER_RADIAL)
+	{
+		float maxPosSqr = fullLen*fullLen;
+		//compute fraction
+		xPos = sqrt ( xPos*maxPosSqr);
+	}
+	else
+	{
+		xPos = xPos*fullLen;
+	}			
+
+	return xPos;
+}
+
 //!Get approx number of bytes for caching output
 size_t CompositionProfileFilter::numBytesForCache(size_t nObjects) const
 {
@@ -1270,6 +1329,7 @@ bool CompositionProfileFilter::readState(xmlNodePtr &nodePtr, const std::string
 	if(XMLHelpFwdToElem(nodePtr,"primitivetype"))
 		return false;
 
+
 	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
 	if(!xmlString)
 		return false;
@@ -1279,6 +1339,16 @@ bool CompositionProfileFilter::readState(xmlNodePtr &nodePtr, const std::string
 	if(stream_cast(primitiveType,tmpStr))
 		return false;
 
+	//FIXME: DEPRECATE 3Depict versions <=0.0.17 had only two primitives,
+	// cylinder and sphere. 
+/*	if(versionCheckGreater(Filter::stateWriterVersion,("0.0.17")))
+	{
+		//remap the primitive type as needed
+		if(primitiveType == PRIMITIVE_CYLINDER_RADIAL)
+			primitiveType=PRIMITIVE_SPHERE;
+
+	}
+*/
 	if(primitiveType >= PRIMITIVE_END)
 	       return false;	
 	xmlFree(xmlString);
@@ -1395,7 +1465,8 @@ bool CompositionProfileFilter::readState(xmlNodePtr &nodePtr, const std::string
 	//Check the scalar params match the selected primitive	
 	switch(primitiveType)
 	{
-		case PRIMITIVE_CYLINDER:
+		case PRIMITIVE_CYLINDER_AXIAL:
+		case PRIMITIVE_CYLINDER_RADIAL:
 			if(vectorParams.size() != 2 || scalarParams.size() !=1)
 				return false;
 			break;
@@ -1656,7 +1727,7 @@ bool testCompositionCylinder()
 
 
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 
 	//2* plot, 1*rng, 1*draw
 	TEST(streamOut.size() == 4, "output stream count");
@@ -1687,7 +1758,7 @@ bool testCompositionCylinder()
 			break;
 		}
 	}
-
+	TEST(plotData,"Should have plot data");
 	TEST(plotData->xyData.size(),"Plot data size");
 
 	for(size_t ui=0;ui<plotData->xyData.size(); ui++)
@@ -1744,7 +1815,7 @@ bool testDensityCylinder()
 	TEST(f->setProperty(COMPOSITION_KEY_RADIUS,"5",needUp),"Set radius");
 
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 	delete f;
 	delete d;
 
diff --git a/src/backend/filters/compositionProfile.h b/src/backend/filters/compositionProfile.h
index b4a677b..73e1eb0 100644
--- a/src/backend/filters/compositionProfile.h
+++ b/src/backend/filters/compositionProfile.h
@@ -1,6 +1,6 @@
 /*
  *	compositionProfile.h - Composition profiles of 3D point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -55,9 +55,9 @@ class CompositionProfileFilter : public Filter
 		//Lock the primitive axis during for cylinder?
 		bool lockAxisMag; 
 		//!Vector parameters for different primitives
-		vector<Point3D> vectorParams;
+		std::vector<Point3D> vectorParams;
 		//!Scalar parameters for different primitives
-		vector<float> scalarParams;
+		std::vector<float> scalarParams;
 
 		//!Frequency or percentile mode (0 - frequency; 1-normalised (ion freq))
 		bool normalise;
@@ -88,13 +88,16 @@ class CompositionProfileFilter : public Filter
 		
 		//!internal function for binning an ion dependant upon range data
 		static void binIon(unsigned int targetBin, const RangeStreamData* rng, const std::map<unsigned int,unsigned int> &ionIDMapping,
-			vector<vector<size_t> > &frequencyTable, float massToCharge);
+			std::vector<std::vector<size_t> > &frequencyTable, float massToCharge);
 
 		static unsigned int getPrimitiveId(const std::string &s);;
 
 		//obtain the size of each bin, and number of bins required for profile
 		unsigned int getBinData(unsigned int &numBins, float &binLength) const;
 
+		//Obtain the X coordinate of a given bin's centre, given the bin value
+		float getBinPosition(unsigned int nBin) const;
+
 	public:
 		CompositionProfileFilter();
 		//!Duplicate filter contents, excluding cache.
@@ -112,7 +115,7 @@ class CompositionProfileFilter : public Filter
 		//!update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 						std::vector<const FilterStreamData *> &getOut, 
-						ProgressData &progress, bool (*callback)(bool));
+						ProgressData &progress);
 		
 		virtual std::string typeString() const { return std::string(TRANS("Comp. Prof."));};
 
@@ -123,7 +126,7 @@ class CompositionProfileFilter : public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format, 
diff --git a/src/backend/filters/dataLoad.cpp b/src/backend/filters/dataLoad.cpp
index b35a2df..0fd76bb 100644
--- a/src/backend/filters/dataLoad.cpp
+++ b/src/backend/filters/dataLoad.cpp
@@ -1,6 +1,6 @@
 /*
  *	dataLoad.cpp - filter to  load datasets from various source
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -27,6 +27,11 @@
 
 #include "backend/APT/APTFileIO.h"
 
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::endl;
+
 //Default number of ions to load
 const size_t MAX_IONS_LOAD_DEFAULT=5*1024*1024/(4*sizeof(float)); //5 MB worth.
 
@@ -102,7 +107,7 @@ Filter *DataLoadFilter::cloneUncached() const
 	for(size_t ui=0;ui<INDEX_LENGTH;ui++)
 		p->index[ui]=index[ui];
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -155,6 +160,10 @@ void DataLoadFilter::guessNumColumns()
 	if(ionFilename.size() > 4)
 		extension = ionFilename.substr ( ionFilename.size() - 4, 4 );
 
+	//By default, return 4. If you want to have other file types,
+	// uncomment the below
+	numColumns=4;
+	/*
 	//Set extension to lowercase version
 	for(size_t ui=0;ui<extension.size();ui++)
 		extension[ui] = tolower(extension[ui]);
@@ -163,7 +172,7 @@ void DataLoadFilter::guessNumColumns()
 		numColumns = 4;
 		return;
 	}
-	numColumns = 4;
+	numColumns = 4;*/
 }
 
 //!Get (approx) number of bytes required for cache
@@ -181,7 +190,7 @@ size_t DataLoadFilter::numBytesForCache(size_t nObjects) const
 }
 
 unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 
 	errStr="";
@@ -226,6 +235,7 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 			propagateCache(getOut);
 
 			propagateStreams(dataIn,getOut);
+			progress.filterProgress=100;
 			return 0;
 		}
 	}
@@ -261,6 +271,10 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 	IonStreamData *ionData = new IonStreamData;
 	ionData->parent=this;	
 
+	progress.step=1;
+	progress.stepName=TRANS("Reading File");
+	progress.maxStep=1;
+
 	unsigned int uiErr;	
 	switch(fileType)
 	{
@@ -271,7 +285,7 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 				
 				//Load the pos file, limiting how much you pull from it
 				if((uiErr = LimitLoadPosFile(numColumns, INDEX_LENGTH, index, ionData->data, ionFilename.c_str(),
-									maxIons,progress.filterProgress,callback,strongRandom)))
+									maxIons,progress.filterProgress,(*Filter::wantAbort),strongRandom)))
 				{
 					consoleOutput.push_back(string(TRANS("Error loading file: ")) + ionFilename);
 					delete ionData;
@@ -284,7 +298,7 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 			{
 				//Load the entirety of the file
 				if((uiErr = GenericLoadFloatFile(numColumns, INDEX_LENGTH, index, ionData->data, ionFilename.c_str(),
-									progress.filterProgress,callback)))
+									progress.filterProgress,(*Filter::wantAbort))))
 				{
 					consoleOutput.push_back(string(TRANS("Error loading file: ")) + ionFilename);
 					delete ionData;
@@ -327,7 +341,8 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 			{
 				//Load the output data using a random sampling technique. Load up to 4 data columns
 				if((uiErr=limitLoadTextFile(4,outDat,ionFilename.c_str(),
-						TEXT_DELIMINATORS,maxIons,progress.filterProgress,callback,strongRandom)))
+						TEXT_DELIMINATORS,maxIons,progress.filterProgress,(*Filter::wantAbort),strongRandom)))
+
 				{
 					consoleOutput.push_back(string(TRANS("Error loading file: ")) + ionFilename);
 					delete ionData;
@@ -395,11 +410,10 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 		}
 		case FILEDATA_TYPE_ATO:
 		{
-			//TODO: Load Ato file with sampling?
-
-			//Load the pos file, limiting how much you pull from it
+			//TODO: Load Ato file with sampling
+			//Load the file
 			if((uiErr = LoadATOFile(ionFilename.c_str(), ionData->data,
-						progress.filterProgress,callback)))
+						progress.filterProgress,(*Filter::wantAbort))))
 			{
 				consoleOutput.push_back(string(TRANS("Error loading file: ")) + ionFilename);
 				delete ionData;
@@ -434,6 +448,7 @@ unsigned int DataLoadFilter::refresh(const std::vector<const FilterStreamData *>
 		delete ionData;
 		return	0;
 	}
+	progress.filterProgress=100;
 
 
 	BoundCube dataCube;
@@ -1127,7 +1142,7 @@ unsigned int DataLoadFilter::getRefreshUseMask() const
 	return 0;
 }
 
-std::string  DataLoadFilter::getErrString(unsigned int code) const
+std::string  DataLoadFilter::getSpecificErrString(unsigned int code) const
 {
 	ASSERT(errStr.size());
 	return errStr;
@@ -1140,7 +1155,6 @@ void DataLoadFilter::setPropFromBinding(const SelectionBinding &b)
 
 bool DataLoadFilter::writeState(std::ostream &f,unsigned int format, unsigned int depth) const
 {
-	using std::endl;
 	switch(format)
 	{
 		case STATE_FORMAT_XML:
@@ -1294,7 +1308,7 @@ bool posFileTest()
 
 	vector<const FilterStreamData*> streamIn,streamOut;
 	ProgressData prog;
-	TEST(!d->refresh(streamIn,streamOut,prog,dummyCallback),"Refresh error code");
+	TEST(!d->refresh(streamIn,streamOut,prog),"Refresh error code");
 	delete d;
 
 
@@ -1326,7 +1340,7 @@ bool textFileTest()
 	//TODO: do better than this
 	const char *FILENAME="test-3mdfuneaascn.txt";
 	//see if we can open the file for input. If so, it must exist,
-	//and thus we don't want to overwite it, as it may contain useful data.
+	//and thus we don't want to overwrite it, as it may contain useful data.
 	std::ifstream inFile(FILENAME);
 	if(inFile)
 	{
@@ -1379,7 +1393,7 @@ bool textFileTest()
 
 	vector<const FilterStreamData*> streamIn,streamOut;
 	ProgressData prog;
-	TEST(!d->refresh(streamIn,streamOut,prog,dummyCallback),"Refresh error code");
+	TEST(!d->refresh(streamIn,streamOut,prog),"Refresh error code");
 	delete d;
 
 
@@ -1389,7 +1403,7 @@ bool textFileTest()
 	TEST(streamOut[0]->getNumBasicObjects() == NUM_PTS,"Stream count");
 
 #if defined(__LINUX__) || defined(__APPLE__)
-	//Hackish mathod to delete file
+	//Hackish method to delete file
 	std::string s;
 	s=string("rm -f ") + string(FILENAME);
 	system(s.c_str());
diff --git a/src/backend/filters/dataLoad.h b/src/backend/filters/dataLoad.h
index cb28899..4907116 100644
--- a/src/backend/filters/dataLoad.h
+++ b/src/backend/filters/dataLoad.h
@@ -1,6 +1,6 @@
 /*
  *	dataLoad.h - Load data from various file sources
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -128,7 +128,7 @@ class DataLoadFilter:public Filter
 		//!Refresh object data
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 				std::vector<const FilterStreamData *> &getOut, 
-				ProgressData &progress, bool (*callback)(bool));
+				ProgressData &progress);
 
 		void updatePosData();
 
@@ -141,7 +141,7 @@ class DataLoadFilter:public Filter
 		bool setProperty( unsigned int key, const std::string &value, bool &needUpdate);
 		
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format, 
@@ -163,7 +163,7 @@ class DataLoadFilter:public Filter
 		virtual unsigned int getRefreshUseMask() const; 
 	
 		//!Pos filter has state overrides	
-		virtual void getStateOverrides(std::vector<string> &overrides) const; 
+		virtual void getStateOverrides(std::vector<std::string> &overrides) const; 
 		
 		//!Set internal property value using a selection binding  (Disabled, this filter has no bindings)
 		void setPropFromBinding(const SelectionBinding &b)  ;
diff --git a/src/backend/filters/externalProgram.cpp b/src/backend/filters/externalProgram.cpp
index fa621f2..7cf5556 100644
--- a/src/backend/filters/externalProgram.cpp
+++ b/src/backend/filters/externalProgram.cpp
@@ -1,6 +1,6 @@
 /*
  *	externalProgram.cpp - Call out external programs as a datasource/sink
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -26,11 +26,16 @@
 #include <wx/filename.h>
 #include <wx/dir.h>
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
 
 //!Error codes
 enum
 {
 	COMMANDLINE_FAIL=1,
+	SYSTEM_EXEC_FAIL,
 	SETWORKDIR_FAIL,
 	WRITEPOS_FAIL,
 	WRITEPLOT_FAIL,
@@ -61,7 +66,7 @@ Filter *ExternalProgramFilter::cloneUncached() const
 	p->alwaysCache=alwaysCache;
 	p->cleanInput=cleanInput;
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -209,19 +214,21 @@ size_t ExternalProgramFilter::substituteVariables(const std::string &commandStr,
 }
 
 unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	//use the cached copy if we have it.
 	if(cacheOK)
 	{
 		propagateCache(getOut);
-
+		progress.filterProgress=100;
 		return 0;
 	}
 
 	if(commandLine.empty())
+	{
+		progress.filterProgress=100;
 		return 0;
-
+	}
 	vector<string> ionOutputNames,plotOutputNames;
 
 	//Compute the bounding box of the incoming streams
@@ -245,8 +252,11 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 		if(!wxDirExists(tempDir) )	
 			return MAKEDIR_FAIL;
 
-	}
 	
+	}
+	progress.maxStep=3;
+	progress.step=1;
+	progress.stepName=TRANS("Collate Input");	
 	for(unsigned int ui=0;ui<dataIn.size() ;ui++)
 	{
 		switch(dataIn[ui]->getStreamType())
@@ -308,8 +318,10 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 	//Nothing to do.
 	if(plotOutputNames.empty() &&
 		ionOutputNames.empty())
+	{
+		progress.filterProgress=100;
 		return 0;
-
+	}
 	std::string substitutedCommand;
 	size_t errCode;
 	errCode=substituteVariables(commandLine,ionOutputNames,plotOutputNames,
@@ -331,9 +343,16 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 			return SETWORKDIR_FAIL;
 	}
 
-	bool result;
+	int result;
+	progress.step=2;
+	progress.stepName=TRANS("Execute");	
+
 	//Execute the program
-	result=wxShell((substitutedCommand));
+	//TODO: IO redirection - especially under windows?
+	result=std::system(substitutedCommand.c_str());
+
+	if(result == -1)
+		return SYSTEM_EXEC_FAIL; 
 
 	if(cleanInput)
 	{
@@ -341,23 +360,20 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 		//delete the input files.
 		for(unsigned int ui=0;ui<ionOutputNames.size();ui++)
 		{
-			//try to delete the file
-			wxRemoveFile((ionOutputNames[ui]));
+			//try to delete the file, if the command did not
+			// remove it
+			if(wxFileExists(ionOutputNames[ui]))
+				wxRemoveFile(ionOutputNames[ui]);
 
-			//call the update to be nice
-			(*callback)(false);
 		}
 		for(unsigned int ui=0;ui<plotOutputNames.size();ui++)
 		{
 			//try to delete the file
 			wxRemoveFile((plotOutputNames[ui]));
-
-			//call the update to be nice
-			(*callback)(false);
 		}
 	}
 	wxSetWorkingDirectory(origDir);	
-	if(!result)
+	if(result)
 		return COMMAND_FAIL; 
 	
 	wxSetWorkingDirectory(origDir);	
@@ -369,6 +385,8 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 	else
 		dir->GetAllFiles(wxGetCwd(),a,wxT("*.pos"),wxDIR_FILES);
 
+	progress.step=3;
+	progress.stepName=TRANS("Collate output");	
 
 	//read the output files, which is assumed to be any "pos" file
 	//in the working dir
@@ -399,7 +417,7 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 			unsigned int index2[] = {
 					0, 1, 2, 3
 					};
-			if(GenericLoadFloatFile(4, 4, index2, d->data,sTmp.c_str(),dummy,dummyCallback))
+			if(GenericLoadFloatFile(4, 4, index2, d->data,sTmp.c_str(),dummy,*(Filter::wantAbort)))
 			{
 				delete d;
 				delete dir;
@@ -531,6 +549,7 @@ unsigned int ExternalProgramFilter::refresh(const std::vector<const FilterStream
 
 	delete dir;
 	delete a;
+	progress.filterProgress=100;
 
 	return 0;
 }
@@ -551,7 +570,7 @@ void ExternalProgramFilter::getProperties(FilterPropGroup &propertyList) const
 	
 	p.name=TRANS("Work Dir");
 	p.data= workingDir;
-	p.type=PROPERTY_TYPE_STRING;
+	p.type=PROPERTY_TYPE_DIR;
 	p.helpText=TRANS("Directory to run the command in");
 	p.key=EXTERNALPROGRAM_KEY_WORKDIR;		
 	propertyList.addProperty(p,curGroup);
@@ -624,10 +643,11 @@ bool ExternalProgramFilter::setProperty(  unsigned int key,
 }
 
 
-std::string  ExternalProgramFilter::getErrString(unsigned int code) const
+std::string  ExternalProgramFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[] = 	{ "",
 			"Error processing command line",
+			"Unable to launch external program",
 			"Unable to set working directory",
 			"Error saving posfile result for external program",
 			"Error saving plot result for externalprogram",
@@ -636,7 +656,7 @@ std::string  ExternalProgramFilter::getErrString(unsigned int code) const
 			"Unable to parse plot result from external program",
 			"Unable to load ions from external program", 
 			"Unable to perform commandline substitution",
-			"Error executing external program" };
+			"Error executing external program, returned nonzero" };
 	
 	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(errStrs) == EXT_PROG_ERR_ENUM_END);
 	ASSERT(code < EXT_PROG_ERR_ENUM_END);
@@ -743,6 +763,9 @@ unsigned int ExternalProgramFilter::getRefreshUseMask() const
 #ifdef DEBUG
 #include <memory>
 
+using std::auto_ptr;
+using std::ifstream;
+
 bool echoTest()
 {
 	int errCode;
@@ -771,7 +794,7 @@ bool echoTest()
 	//Simulate some data to send to the filter
 	vector<const FilterStreamData*> streamIn,streamOut;
 	ProgressData p;
-	f->refresh(streamIn,streamOut,p,dummyCallback);
+	f->refresh(streamIn,streamOut,p);
 
 
 	s=stlStr(tmpFilename);
@@ -832,9 +855,10 @@ bool posTest()
 		
 	wxMkdir(tmpDir);
 
-	tmpFilename=wxFileName::CreateTempFileName(tmpDir+ wxT("unittest-"));
-	tmpFilename+=wxT(".pos");
-	s ="mv -f \%i " + stlStr(tmpFilename);
+	std::string randName;
+	genRandomFilename(randName);
+	tmpFilename = tmpDir + "/" + randName + ".pos";
+	s ="mv -f \%i " + tmpFilename;
 
 	ASSERT(tmpFilename.size());
 	
@@ -844,7 +868,7 @@ bool posTest()
 	vector<const FilterStreamData*> streamIn,streamOut;
 	streamIn.push_back(someData.get());
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 
 	//Should have exactly one stream, which is an ion stream
 	TEST(streamOut.size() == 1,"stream count");
diff --git a/src/backend/filters/externalProgram.h b/src/backend/filters/externalProgram.h
index 9b2baa5..a7a8efb 100644
--- a/src/backend/filters/externalProgram.h
+++ b/src/backend/filters/externalProgram.h
@@ -1,6 +1,6 @@
 /*
  *	externalProgram.h - Call out external programs as data sources/sinks
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -45,7 +45,7 @@ class ExternalProgramFilter : public Filter
 		bool cleanInput;
 
 		static size_t substituteVariables(const std::string &commandStr,
-				const vector<string> &ions, const vector<string> &plots, 
+				const std::vector<std::string> &ions, const std::vector<std::string> &plots, 
 							std::string &substitutedCommand);
 
 	public:
@@ -64,7 +64,7 @@ class ExternalProgramFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 					std::vector<const FilterStreamData *> &getOut, 
-					ProgressData &progress, bool (*callback)(bool));
+					ProgressData &progress);
 		
 		virtual std::string typeString() const { return std::string(TRANS("Ext. Program"));};
 
@@ -75,7 +75,7 @@ class ExternalProgramFilter : public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format,
diff --git a/src/backend/filters/filterCommon.cpp b/src/backend/filters/filterCommon.cpp
index 4a754f7..aa3e8e8 100644
--- a/src/backend/filters/filterCommon.cpp
+++ b/src/backend/filters/filterCommon.cpp
@@ -1,6 +1,6 @@
 /*
  *	filterCommon.cpp - Helper routines for filter classes
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -35,7 +35,8 @@ bool qhullInited=false;
 
 //Wrapper for qhull single-pass run
 unsigned int doHull(unsigned int bufferSize, double *buffer, 
-			vector<Point3D> &resHull, Point3D &midPoint,bool freeHullOnExit);
+			vector<Point3D> &resHull, Point3D &midPoint,	
+			bool wantVolume, bool freeHullOnExit);
 
 void writeVectorsXML(ostream &f,const char *containerName,
 		const vector<Point3D> &vectorParams, unsigned int depth)
@@ -247,7 +248,7 @@ unsigned int getIonstreamIonID(const IonStreamData *d, const RangeFile *r)
 
 //!Extend a point data vector using some ion data
 unsigned int extendPointVector(std::vector<Point3D> &dest, const std::vector<IonHit> &vIonData,
-				bool (*callback)(bool),unsigned int &progress, size_t offset)
+				unsigned int &progress, size_t offset)
 {
 	unsigned int curProg=NUM_CALLBACK;
 	unsigned int n =offset;
@@ -270,7 +271,7 @@ unsigned int extendPointVector(std::vector<Point3D> &dest, const std::vector<Ion
 			progress= (unsigned int)(((float)n/(float)dest.size())*100.0f);
 			if(!omp_get_thread_num())
 			{
-				if(!(*callback)(false))
+				if(*Filter::wantAbort)
 					spin=true;
 			}
 			}
@@ -291,7 +292,7 @@ unsigned int extendPointVector(std::vector<Point3D> &dest, const std::vector<Ion
 		{
 			n+=NUM_CALLBACK;
 			progress= (unsigned int)(((float)n/(float)dest.size())*100.0f);
-			if(!(*callback)(false))
+			if(*(Filter::wantAbort))
 				return 1;
 		}
 
@@ -303,8 +304,9 @@ unsigned int extendPointVector(std::vector<Point3D> &dest, const std::vector<Ion
 }
 
 
+//FIXME: Abort pointer?
 unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsigned int *progress,
-					bool (*callback)(bool),std::vector<Point3D> &curHull, bool freeHull)
+					std::vector<Point3D> &curHull, bool wantVolume,bool freeHull)
 {
 
 	size_t numPts;
@@ -325,11 +327,10 @@ unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsi
 
 	//Do the convex hull in steps for two reasons
 	// 1) qhull chokes on large data
-	// 2) we need to run the callback every now and again, so we have to
+	// 2) we need to check for abort every now and again, so we have to
 	//   work in batches.
 	Point3D midPoint;
 	float maxSqrDist=-1;
-	size_t progressReduce=PROGRESS_REDUCE;
 	size_t n=0;
 	for(size_t ui=0; ui<data.size(); ui++)
 	{
@@ -373,7 +374,7 @@ unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsi
 
 					unsigned int errCode=0;
 					
-					errCode=doHull(bufferOffset,buffer,curHull,midPoint,freeHull);
+					errCode=doHull(bufferOffset,buffer,curHull,midPoint,wantVolume,freeHull);
 					if(errCode)
 					{
 						free(buffer);
@@ -393,19 +394,15 @@ unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsi
 			}
 			n++;
 
-			//Update the progress information, and run callback periodically
-			if(!progressReduce--)
+			//Update the progress information, and run abort check
+			if(*Filter::wantAbort)
 			{
-				if(!(*callback)(false))
-				{
-					free(buffer);
-					return HULL_ERR_USER_ABORT;
-				}
-	
-				*progress= (unsigned int)((float)(n)/((float)numPts)*100.0f);
-
-				progressReduce=PROGRESS_REDUCE;
+				free(buffer);
+				return HULL_ERR_USER_ABORT;
 			}
+
+			*progress= (unsigned int)((float)(n)/((float)numPts)*100.0f);
+
 		}
 	}
 
@@ -430,12 +427,13 @@ unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsi
 			buffer[3*(bufferOffset+ui)+2]=curHull[ui][2];
 		}
 
-		unsigned int errCode=doHull(bufferOffset+curHull.size(),buffer,curHull,midPoint,freeHull);
+		unsigned int errCode=doHull(bufferOffset+curHull.size(),buffer,
+						curHull,midPoint,wantVolume,freeHull);
 
 		if(errCode)
 		{
 			free(buffer);
-			//Free the last convex hull mem
+			//FIXME: Free the last convex hull mem??
 			return errCode;
 		}
 	}
@@ -446,7 +444,7 @@ unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsi
 }
 
 unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progress,
-					bool (*callback)(bool),std::vector<Point3D> &curHull, bool freeHull)
+				const bool &abortPtr,std::vector<Point3D> &curHull, bool wantVolume, bool freeHull)
 {
 
 	//Easy case of no data
@@ -471,7 +469,6 @@ unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progre
 	float maxSqrDist=-1;
 
 
-	size_t progressReduce=PROGRESS_REDUCE;
 
 	for(size_t uj=0; uj<data.size(); uj++)
 	{
@@ -510,7 +507,7 @@ unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progre
 
 				unsigned int errCode=0;
 				
-				errCode=doHull(bufferOffset,buffer,curHull,midPoint,freeHull);
+				errCode=doHull(bufferOffset,buffer,curHull,midPoint,wantVolume,freeHull);
 				if(errCode)
 					return errCode;
 
@@ -525,18 +522,14 @@ unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progre
 			}
 		}
 
-		if(!progressReduce--)
+		if(*Filter::wantAbort)
 		{
-			if(!(*callback)(false))
-			{
-				free(buffer);
-				return HULL_ERR_USER_ABORT;
-			}
-	
-			*progress= (unsigned int)((float)(uj)/((float)data.size())*100.0f);
-
-			progressReduce=PROGRESS_REDUCE;
+			free(buffer);
+			return HULL_ERR_USER_ABORT;
 		}
+
+		*progress= (unsigned int)((float)(uj)/((float)data.size())*100.0f);
+
 	}
 
 
@@ -563,7 +556,7 @@ unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progre
 			buffer[3*(bufferOffset+ui)+2]=curHull[ui][2];
 		}
 
-		unsigned int errCode=doHull(bufferOffset+curHull.size(),buffer,curHull,midPoint,freeHull);
+		unsigned int errCode=doHull(bufferOffset+curHull.size(),buffer,curHull,midPoint,wantVolume,freeHull);
 
 		if(errCode)
 		{
@@ -579,14 +572,20 @@ unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progre
 }
 
 unsigned int doHull(unsigned int bufferSize, double *buffer, 
-			vector<Point3D> &resHull, Point3D &midPoint, bool freeHullOnExit)
+			vector<Point3D> &resHull, Point3D &midPoint, bool wantVolume ,
+			bool freeHullOnExit)
 {
 	if(qhullInited)
 	{
-		qh_freeqhull(true);
+		qh_freeqhull(qh_ALL);
+		int curlong,totlong;
+		//This seems to be required? Cannot find any documentation on the difference
+		// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
+		qh_memfreeshort (&curlong, &totlong);    
 		qhullInited=false;
 	}
 
+
 	const int dim=3;
 	//Now compute the new hull
 	//Generate the convex hull
@@ -609,11 +608,23 @@ unsigned int doHull(unsigned int bufferSize, double *buffer,
 		outSquelch=stderr;
 	}
 
+	 //Joggle the output, such that only simplical facets are generated, Also compute area/volume
+	const char *argsOptions[]= { "qhull QJ FA",
+				   "qhull QJ"
+					};
+
+	const char *args;
+	if(wantVolume)
+		args = argsOptions[0];
+	else
+		args=argsOptions[1];
+	
+
 	qh_new_qhull(	dim,
 			bufferSize,
 			buffer,
 			false,
-			(char *)"qhull QJ", //Joggle the output, such that only simplical facets are generated
+			(char *)args ,
 			outSquelch, //QHULL's interface is bizarre, no way to set null pointer in qhull 2012 - result is inf. loop in qhull_fprintf and error reporting func. 
 			outSquelch);
 	qhullInited=true;
@@ -666,7 +677,11 @@ unsigned int doHull(unsigned int bufferSize, double *buffer,
 	//--
 	if(freeHullOnExit)
 	{
-		qh_freeqhull(true);
+		qh_freeqhull(qh_ALL);
+		int curlong,totlong;
+		//This seems to be required? Cannot find any documentation on the difference
+		// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
+		qh_memfreeshort (&curlong, &totlong);    
 		qhullInited=false;
 	
 	}
@@ -677,7 +692,11 @@ unsigned int doHull(unsigned int bufferSize, double *buffer,
 
 void freeConvexHull()
 {
-	qh_freeqhull(true);
+	qh_freeqhull(qh_ALL);
+	int curlong,totlong;
+	//This seems to be required? Cannot find any documentation on the difference
+	// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
+	qh_memfreeshort (&curlong, &totlong);    
 	qhullInited=false;
 }
 
diff --git a/src/backend/filters/filterCommon.h b/src/backend/filters/filterCommon.h
index 5085124..e1bbf36 100644
--- a/src/backend/filters/filterCommon.h
+++ b/src/backend/filters/filterCommon.h
@@ -1,6 +1,6 @@
 /*
  *	filterCommon.h - Helper routines for filter classes
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -115,18 +115,18 @@ unsigned int getIonstreamIonID(const IonStreamData *d, const RangeFile *r);
 
 //!Extend a point data vector using some ion data
 unsigned int extendPointVector(std::vector<Point3D> &dest, const std::vector<IonHit> &vIonData,
-				bool (*callback)(bool),unsigned int &progress, size_t offset);
+				unsigned int &progress, size_t offset);
 
 const RangeFile *getRangeFile(const std::vector<const FilterStreamData*> &dataIn);
 
 //Compute the convex hull of a set of input points from fiilterstream data
 unsigned int computeConvexHull(const std::vector<const FilterStreamData*> &data, 
-			unsigned int *progress, bool (*callback)(bool), 
-			std::vector<Point3D> &hullPts, bool freeHull=true);
+			unsigned int *progress, 
+			std::vector<Point3D> &hullPts, bool wantVolume, bool freeHull=true);
 //Compute the convex hull of a set of input points
 unsigned int computeConvexHull(const std::vector<Point3D> &data, 
-			unsigned int *progress, bool (*callback)(bool), 
-			std::vector<Point3D> &hullPts, bool freeHull=true);
+			unsigned int *progress, const bool &abortPtr,
+			std::vector<Point3D> &hullPts, bool wantVolume, bool freeHull=true);
 
 //Release the memory held by qhull, and notify the computeConvexHull routines that this has been done
 void freeConvexHull();
diff --git a/src/backend/filters/geometryHelpers.cpp b/src/backend/filters/geometryHelpers.cpp
index 2521ab0..e7aaf31 100644
--- a/src/backend/filters/geometryHelpers.cpp
+++ b/src/backend/filters/geometryHelpers.cpp
@@ -1,6 +1,6 @@
 /*
  *	geometryHelpers.cpp - Various spatial geometry operators for point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
 
 #include "backend/APT/ionhit.h"
 
+#include "backend/filter.h"
+
 #ifdef _OPENMP
 #include <omp.h>
 #endif
@@ -39,12 +41,10 @@ const size_t MIN_SAMPLE_TEST = 1000;
 const size_t MIN_PARALLELISE = 20000;
 //---
 
-CropHelper::CropHelper(bool (*callback)(bool), unsigned int *p, 
-			size_t totalData,size_t filterMode,
+CropHelper::CropHelper(	size_t totalData,size_t filterMode,
 			vector<Point3D> &vectors, vector<float> &scalars)
 {
 	algorithm=filterMode;
-	curProgCount=0;
 	mapMax=0;
 	invertedClip=false;
 
@@ -77,7 +77,8 @@ CropHelper::CropHelper(bool (*callback)(bool), unsigned int *p,
 		}
 		case CROP_CYLINDER_OUTSIDE:
 			invertedClip=true;
-		case CROP_CYLINDER_INSIDE:
+		case CROP_CYLINDER_INSIDE_AXIAL:
+		case CROP_CYLINDER_INSIDE_RADIAL:
 		{
 			ASSERT(vectors.size() == 2);
 			ASSERT(scalars.size() == 1);
@@ -104,9 +105,6 @@ CropHelper::CropHelper(bool (*callback)(bool), unsigned int *p,
 	setAlgorithm();
 
 
-	numCallback=DEFAULT_NUM_CALLBACK;
-	callbackFunc=callback;
-	progressPtr=p;
 	totalDataCount=totalData;
 }
 
@@ -127,10 +125,14 @@ void CropHelper::setAlgorithm()
 		case CROP_PLANE_BACK:
 			cropFunc=&CropHelper::filterPlaneFront;
 			break;
-		case CROP_CYLINDER_INSIDE:
 		case CROP_CYLINDER_OUTSIDE:
+		case CROP_CYLINDER_INSIDE_AXIAL:
+			cropFunc=&CropHelper::filterCylinderInside;
+			mapFunc=&CropHelper::mapCylinderInsideAxial;
+			break;
+		case CROP_CYLINDER_INSIDE_RADIAL:
 			cropFunc=&CropHelper::filterCylinderInside;
-			mapFunc=&CropHelper::mapCylinderInside;
+			mapFunc=&CropHelper::mapCylinderInsideRadial;
 			break;
 		case CROP_AAB_INSIDE:
 		case CROP_AAB_OUTSIDE:
@@ -142,7 +144,8 @@ void CropHelper::setAlgorithm()
 }
 
 unsigned int CropHelper::runFilter(const vector<IonHit> &dataIn,
-				vector<IonHit> &dataOut ) 
+				vector<IonHit> &dataOut, float progressStart,
+				float progressEnd, unsigned int &progress ) 
 {
 	//FIXME!: Shouldn't be using this here - should be obeying
 	// system-wide rng preferences
@@ -155,12 +158,13 @@ unsigned int CropHelper::runFilter(const vector<IonHit> &dataIn,
 	// pre-allocate enough space for output data
 	if(dataIn.size() > MIN_SAMPLE_TEST)
 	{
+
 		const size_t SAMPLE_SIZE=30;
 
 		vector<size_t> samples;
 		unsigned int dummy;
 		randomDigitSelection(samples,dataIn.size(),rng, 
-				SAMPLE_SIZE,dummy, dummyCallback);
+				SAMPLE_SIZE,dummy);
 
 		size_t tally=0;
 		for(size_t ui=0;ui<SAMPLE_SIZE;ui++)
@@ -174,45 +178,37 @@ unsigned int CropHelper::runFilter(const vector<IonHit> &dataIn,
 
 
 #ifndef _OPENMP
-		return runFilterLinear(dataIn,dataOut,allocHint);
+	return runFilterLinear(dataIn,dataOut,allocHint,progressStart,progressEnd,progress);
 #else
 	if(dataIn.size() < MIN_PARALLELISE  || rng.genUniformDev() < 0.5f)
-		return runFilterLinear(dataIn,dataOut,allocHint);
+		return runFilterLinear(dataIn,dataOut,allocHint,progressStart,progressEnd,progress);
 	else
 	{
-		return runFilterParallel(dataIn,dataOut,allocHint);
+		return runFilterParallel(dataIn,dataOut,allocHint,progressStart,progressEnd,progress);
 	}
 #endif
 }
 
 unsigned int CropHelper::runFilterLinear(const vector<IonHit> &dataIn,
-				vector<IonHit> &dataOut,float allocHint ) 
+				vector<IonHit> &dataOut,float allocHint, float minProg,float maxProg, unsigned int &prog ) 
 {
 	if(allocHint > 0.0f)
 		dataOut.reserve((unsigned int) ( (float)dataIn.size()*allocHint));
 
-	size_t &n=curProgCount;
-	size_t curProg=0;
 	//Run the data filtering using a single threaded algorithm
 	// copying to output
 	if(!invertedClip)
 	{
 		for(size_t ui=0; ui<dataIn.size(); ui++)
 		{
-		//Use XOR operand on cropFunc conditional
 			if(((this->*cropFunc)(dataIn[ui].getPosRef())))
 				dataOut.push_back(dataIn[ui]);
 
-			//update progress every CALLBACK ions
-			if(!curProg--)
-			{
-				n+=numCallback;
-				*progressPtr= (unsigned int)((float)(n)/((float)totalDataCount)*100.0f);
-				curProg=numCallback;
+			if(ui & 100)
+				prog = (float)ui/(float)dataIn.size() * (maxProg-minProg)+minProg;
 
-				if(!(*callbackFunc)(false))
-					return ERR_CROP_CALLBACK_FAIL;
-			}
+			if(*Filter::wantAbort)
+				return ERR_CROP_CALLBACK_FAIL;
 		}
 
 	}
@@ -223,29 +219,26 @@ unsigned int CropHelper::runFilterLinear(const vector<IonHit> &dataIn,
 			if(!((this->*cropFunc)(dataIn[ui].getPosRef())))
 				dataOut.push_back(dataIn[ui]);
 
-			//update progress every CALLBACK ions
-			if(!curProg--)
-			{
-				n+=numCallback;
-				*progressPtr= (unsigned int)((float)(n)/((float)totalDataCount)*100.0f);
-				curProg=numCallback;
+			if(ui & 100)
+				prog = (float)ui/(float)dataIn.size() * (maxProg-minProg)+minProg;
 
-				if(!(*callbackFunc)(false))
-					return ERR_CROP_CALLBACK_FAIL;
-			}
+			if(*Filter::wantAbort)
+				return ERR_CROP_CALLBACK_FAIL;
 		}
 	}
 
+	prog=maxProg;
 	return 0;
 }
 
 unsigned int CropHelper::runFilterParallel(const vector<IonHit> &dataIn,
-				vector<IonHit> &dataOut, float allocHint )
+				vector<IonHit> &dataOut, float allocHint, float minProg,float maxProg, unsigned int &prog )
 {
 #ifdef _OPENMP
 
-	size_t &n=curProgCount;
-	size_t curProg=0;
+	size_t n=0;
+	const size_t PROGRESS_REDUCE=5000;
+	size_t curProg=PROGRESS_REDUCE;
 
 	//Create a vector of indices for which 
 	// points successfully passed the test
@@ -271,21 +264,22 @@ unsigned int CropHelper::runFilterParallel(const vector<IonHit> &dataIn,
 		if(spin)
 			continue;
 
-		//Use XOR operand on cropFunc conditionale
+		//Use XOR operand on cropFunc conditional
 		if(((this->*cropFunc)(dataIn[ui].getPosRef())) ^ invertedClip)
 			inside[omp_get_thread_num()].push_back(ui);
 		
-		//update progress every CALLBACK ions
+		//update progress every PROGRESS_REDUCE ions
 		if(!curProg--)
 		{
 #pragma omp critical
 			{
-			n+=numCallback;
-			*progressPtr= (unsigned int)((float)(n)/((float)totalDataCount)*100.0f);
-			if(!(*callbackFunc)(false))
+			n+=PROGRESS_REDUCE;
+			prog = (float)n/(float)dataIn.size() * (maxProg-minProg)+minProg;
+			
+			if(*Filter::wantAbort)
 				spin=true;
 			}
-			curProg=numCallback;
+			curProg=PROGRESS_REDUCE;
 		}
 	}
 
@@ -317,6 +311,8 @@ unsigned int CropHelper::runFilterParallel(const vector<IonHit> &dataIn,
 #else
 	ASSERT(false); // what are you doing here??
 #endif
+
+	prog=maxProg;
 	return 0;
 }
 
@@ -376,7 +372,7 @@ bool CropHelper::filterCylinderInside(const Point3D &testPt) const
 unsigned int CropHelper::mapSphereInside(const Point3D &testPt) const
 {
 	float radius;
-	radius = sqrt(testPt.sqrDist(pA));
+	radius = sqrtf(testPt.sqrDist(pA));
 
 	if(radius <=fB)
 		return (unsigned int) (mapMax*(radius/fB));
@@ -384,7 +380,7 @@ unsigned int CropHelper::mapSphereInside(const Point3D &testPt) const
 		return -1;
 }
 
-unsigned int CropHelper::mapCylinderInside( const Point3D &testPt) const
+unsigned int CropHelper::mapCylinderInsideAxial( const Point3D &testPt) const
 {
 
 	//pA - cylinder origin
@@ -427,15 +423,62 @@ unsigned int CropHelper::mapCylinderInside( const Point3D &testPt) const
 
 }
 
+unsigned int CropHelper::mapCylinderInsideRadial( const Point3D &testPt) const
+{
+
+	//pA - cylinder origin
+	//fA - cylinder half length (origin to end, along axis)
+	//fB - cylinder sqr radius
+	//qA - rotation quaternion
+	Point3D ptmp;
+	float fSqrRad;
+	if(nearAxis)
+	{
+		ptmp=testPt-pA;
+
+		if(!(ptmp[2] < fA && ptmp[2] > -fA && 
+				ptmp[0]*ptmp[0]+ptmp[1]*ptmp[1] < fB) )
+			return (unsigned int)-1;
+		else
+		{
+			fSqrRad=ptmp[0]*ptmp[0] + ptmp[1]*ptmp[1];
+		}
+	}
+	else
+	{
+		Point3f p;
+		//Translate to get position w respect to cylinder centre
+		ptmp=testPt-pA;
+		p.fx=ptmp[0];
+		p.fy=ptmp[1];
+		p.fz=ptmp[2];
+		//rotate ion position into cylindrical coordinates
+		quat_rot_apply_quat(&p,&qA);
+
+		//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))
+			return (unsigned int)-1;
+
+		fSqrRad=ptmp[0]*ptmp[0] + ptmp[1]*ptmp[1];
+		
+	}
+
+	//Area is constant in square space
+	return (unsigned int)(fSqrRad/fB*(float)mapMax);
+
+}
+
 //Direction is the axis along the full length of the cylinder
 void CropHelper::setupCylinder(Point3D origin,float radius, Point3D direction)
 {
-	ASSERT(direction.sqrMag() > sqrt(std::numeric_limits<float>::epsilon()));
+	ASSERT(direction.sqrMag() > sqrtf(std::numeric_limits<float>::epsilon()));
 	ASSERT(radius > 0.0f);
 
 	pA=origin;
 	//cylinder half length
-	fA=sqrt(direction.sqrMag())/2.0f;
+	fA=sqrtf(direction.sqrMag())/2.0f;
 	//cylinder square radius
 	fB=radius*radius;
 	
@@ -445,8 +488,8 @@ void CropHelper::setupCylinder(Point3D origin,float radius, Point3D direction)
 	float angle = zDir.angle(direction);
 	//Check that we actually need to rotate, to avoid numerical singularity
 	//when cylinder axis is too close to (or is) z-axis
-	if(angle > sqrt(std::numeric_limits<float>::epsilon())
-	&& angle < M_PI - sqrt(std::numeric_limits<float>::epsilon()))
+	if(angle > sqrtf(std::numeric_limits<float>::epsilon())
+	&& angle < M_PI - sqrtf(std::numeric_limits<float>::epsilon()))
 	{
 		//Cross product desired direction with 
 		//zDirection to produce rotation vector, that when applied
diff --git a/src/backend/filters/geometryHelpers.h b/src/backend/filters/geometryHelpers.h
index ca433ee..aa2718e 100644
--- a/src/backend/filters/geometryHelpers.h
+++ b/src/backend/filters/geometryHelpers.h
@@ -1,6 +1,6 @@
 /*
  *	geometryHelpers.h - 3D Geometric operations helper classes
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -28,7 +28,8 @@ enum
 	CROP_SPHERE_OUTSIDE,
 	CROP_PLANE_FRONT,
 	CROP_PLANE_BACK,
-	CROP_CYLINDER_INSIDE,
+	CROP_CYLINDER_INSIDE_AXIAL,
+	CROP_CYLINDER_INSIDE_RADIAL,
 	CROP_CYLINDER_OUTSIDE,
 	CROP_AAB_OUTSIDE,
 	CROP_AAB_INSIDE,
@@ -81,13 +82,9 @@ class CropHelper
 		CropFuncPtr cropFunc;
 		MapFuncPtr mapFunc;
 
-		size_t curProgCount;
 		size_t totalDataCount;
-		unsigned int *progressPtr;
-	
-		bool (*callbackFunc)(bool);
-		size_t numCallback;
 		//--
+	
 
 
 		//Various testing point containment against primitive
@@ -114,9 +111,12 @@ class CropHelper
 		bool filterBoxInside(const Point3D &testPt) const;
 		//----
 
-		//Mapping functions. returns 0 -> mapMax
-		unsigned int mapCylinderInside(const Point3D &p) const;
+		//Mapping functions. returns 0 -> mapMax, along axial direction
+		unsigned int mapCylinderInsideAxial(const Point3D &p) const;
+		unsigned int mapCylinderInsideRadial(const Point3D &p) const;
 
+
+		//
 		unsigned int mapSphereInside(const Point3D &p) const;
 
 
@@ -132,24 +132,26 @@ class CropHelper
 		// allocHint, if >0 , is the recommended fraction of input to reserve
 		// ahead of copying
 		unsigned int runFilterLinear(const std::vector<IonHit> &dataIn,
-				std::vector<IonHit> &dataOut,float allocHint);
+				std::vector<IonHit> &dataOut,float allocHint, 
+				float minProg, float maxProg, unsigned int &prog);
 	
 		//Run the input filtering in parallel (multi CPU) mode
 		unsigned int runFilterParallel(const std::vector<IonHit> &dataIn,
-				std::vector<IonHit> &dataOut,float allocHint);
+				std::vector<IonHit> &dataOut,float allocHint,
+				float minProg, float maxProg, unsigned int &prog);
 	public:
 	
 		//Input vectors and scalars represent the fundamental
 		// basis for the desired geometry
-		CropHelper(bool (*callback)(bool), unsigned int *prog, 
-			size_t totalData,size_t filterMode,
+		CropHelper(size_t totalData,size_t filterMode,
 			std::vector<Point3D> &vectors, std::vector<float> &scalars);
 		
 		//Filter the input ion data in order to generate output points
 		// output data may contain previous data - this will be appended to,
 		// not overwritten
 		unsigned int runFilter(const std::vector<IonHit> &dataIn,
-				std::vector<IonHit> &dataOut);
+				std::vector<IonHit> &dataOut,
+				float progStart, float progEnd,unsigned int &prog) ;
 
 
 		void setMapMaxima(size_t maxima){ASSERT(maxima); mapMax=maxima;};
@@ -159,7 +161,7 @@ class CropHelper
 
 		//Choose the cropping mode for the filter
 		void setFilterMode(size_t filterMode);
-			
+
 };
 
 
diff --git a/src/backend/filters/ionClip.cpp b/src/backend/filters/ionClip.cpp
index 6502ea9..d0d1276 100644
--- a/src/backend/filters/ionClip.cpp
+++ b/src/backend/filters/ionClip.cpp
@@ -1,6 +1,6 @@
 /*
  *	ionClip.cpp -3Depict filter to perform clipping of 3D point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -23,6 +23,11 @@
 
 #include <map>
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::map;
 
 //!Error codes
 enum 
@@ -107,7 +112,7 @@ Filter *IonClipFilter::cloneUncached() const
 
 	p->lockAxisMag = lockAxisMag;
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -125,8 +130,7 @@ size_t IonClipFilter::numBytesForCache(size_t nObjects) const
 
 //!update filter
 unsigned int IonClipFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-			std::vector<const FilterStreamData *> &getOut, ProgressData &progress, 
-								bool (*callback)(bool))
+			std::vector<const FilterStreamData *> &getOut, ProgressData &progress) 
 {
 	ASSERT(vectorParams.size() || scalarParams.size());	
 	//Clear selection devices, first deleting any we have
@@ -252,7 +256,7 @@ unsigned int IonClipFilter::refresh(const std::vector<const FilterStreamData *>
 				dC->setRadius(scalarParams[0]);
 				dC->setColour(0.5,0.5,0.5,1.0);
 				dC->setSlices(40);
-				dC->setLength(sqrt(vectorParams[1].sqrMag()));
+				dC->setLength(sqrtf(vectorParams[1].sqrMag()));
 				dC->setDirection(vectorParams[1]);
 				dC->wantsLight=true;
 				drawData->drawables.push_back(dC);
@@ -379,6 +383,7 @@ unsigned int IonClipFilter::refresh(const std::vector<const FilterStreamData *>
 				getOut.push_back(dataIn[ui]);
 		}
 			
+		progress.filterProgress=100;
 		return 0;
 	}
 
@@ -396,7 +401,7 @@ unsigned int IonClipFilter::refresh(const std::vector<const FilterStreamData *>
 		primitiveTypeMap[make_pair((size_t)PRIMITIVE_SPHERE,true)]=CROP_SPHERE_OUTSIDE;
 		primitiveTypeMap[make_pair((size_t)PRIMITIVE_PLANE,false)]=CROP_PLANE_FRONT;
 		primitiveTypeMap[make_pair((size_t)PRIMITIVE_PLANE,true)]=CROP_PLANE_BACK;
-		primitiveTypeMap[make_pair((size_t)PRIMITIVE_CYLINDER,false)]=CROP_CYLINDER_INSIDE;
+		primitiveTypeMap[make_pair((size_t)PRIMITIVE_CYLINDER,false)]=CROP_CYLINDER_INSIDE_AXIAL;
 		primitiveTypeMap[make_pair((size_t)PRIMITIVE_CYLINDER,true)]=CROP_CYLINDER_OUTSIDE;
 		primitiveTypeMap[make_pair((size_t)PRIMITIVE_AAB,false)]=CROP_AAB_INSIDE;
 		primitiveTypeMap[make_pair((size_t)PRIMITIVE_AAB,true)]=CROP_AAB_OUTSIDE;
@@ -404,10 +409,10 @@ unsigned int IonClipFilter::refresh(const std::vector<const FilterStreamData *>
 		size_t mode;
 		mode = primitiveTypeMap[make_pair(primitiveType,invertedClip)];
 		size_t totalSize=numElements(dataIn);
-		CropHelper cropper(callback,&progress.filterProgress,
-					totalSize,mode,
-					vectorParams,scalarParams  );
+		CropHelper cropper(totalSize,mode,vectorParams,scalarParams  );
 
+		float minProg,maxProg;
+		size_t cumulativeSize=0;
 		for(unsigned int ui=0;ui<dataIn.size() ;ui++)
 		{
 			//Loop through each data set
@@ -417,9 +422,13 @@ unsigned int IonClipFilter::refresh(const std::vector<const FilterStreamData *>
 				{
 					d=new IonStreamData;
 					d->parent=this;
+					minProg=cumulativeSize/(float)totalSize;
+					cumulativeSize+=d->data.size();
+					maxProg=cumulativeSize/(float)totalSize;
 
 					//Filter input data to output data.
-					if(cropper.runFilter(((const IonStreamData *)dataIn[ui])->data,d->data))
+					if(cropper.runFilter(((const IonStreamData *)dataIn[ui])->data,
+						d->data,minProg,maxProg,progress.filterProgress))
 					{
 						delete d;
 						return CALLBACK_FAIL; 
@@ -754,10 +763,10 @@ bool IonClipFilter::setProperty(unsigned int key,
 			if(primitiveType == PRIMITIVE_CYLINDER)
 			{
 				if(lockAxisMag && 
-					newPt.sqrMag() > sqrt(std::numeric_limits<float>::epsilon()))
+					newPt.sqrMag() > sqrtf(std::numeric_limits<float>::epsilon()))
 				{
 					newPt.normalise();
-					newPt*=sqrt(vectorParams[1].sqrMag());
+					newPt*=sqrtf(vectorParams[1].sqrMag());
 				}
 			}
 			if(!(vectorParams[1] == newPt ))
@@ -797,7 +806,7 @@ bool IonClipFilter::setProperty(unsigned int key,
 }
 
 //!Get the human readable error string associated with a particular error code during refresh(...)
-std::string IonClipFilter::getErrString(unsigned int code) const
+std::string IonClipFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errCode[] = { "",
 				"Insufficient mem. for Ionclip",
@@ -870,7 +879,7 @@ bool IonClipFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileD
 		return false;
 	//====
 	
-	//Retrieve primitive visiblity 
+	//Retrieve primitive visibility 
 	//====
 	if(!XMLGetNextElemAttrib(nodePtr,tmpStr,"showprimitive","value"))
 		return false;
@@ -1077,7 +1086,7 @@ bool sphereTest()
 
 	//Do the refresh
 	ProgressData p;
-	f->refresh(streamIn,streamOut,p,dummyCallback);
+	f->refresh(streamIn,streamOut,p);
 
 	delete f;
 	delete d;
@@ -1095,7 +1104,7 @@ bool sphereTest()
 		Point3D p;
 		p=dOut->data[ui].getPos();
 
-		TEST(sqrt(p.sqrDist(pOrigin)) 
+		TEST(sqrtf(p.sqrDist(pOrigin)) 
 			<= TEST_RADIUS,"Sphere containment");
 	}
 	
@@ -1134,7 +1143,7 @@ bool planeTest()
 
 	//Do the refresh
 	ProgressData p;
-	f->refresh(streamIn,streamOut,p,dummyCallback);
+	f->refresh(streamIn,streamOut,p);
 
 	delete f;
 	delete d;
@@ -1186,7 +1195,7 @@ bool cylinderTest(const Point3D &pAxis, const unsigned int *span, float testRadi
 	TEST(f->setProperty(KEY_PRIMITIVE_SHOW,"0",needUp),"Set prop");
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 	delete f;
 	delete d;
 	
@@ -1198,7 +1207,7 @@ bool cylinderTest(const Point3D &pAxis, const unsigned int *span, float testRadi
 	DrawCylinder *dC = new DrawCylinder;
 	dC->setRadius(testRadius);
 	dC->setOrigin(pOrigin);
-	float len = sqrt(pAxis.sqrMag());
+	float len = sqrtf(pAxis.sqrMag());
 
 	Point3D axisNormal(pAxis);
 	axisNormal.normalise();
@@ -1211,7 +1220,7 @@ bool cylinderTest(const Point3D &pAxis, const unsigned int *span, float testRadi
 
 	delete dC;
 
-	b.expand(sqrt(std::numeric_limits<float>::epsilon()));
+	b.expand(sqrtf(std::numeric_limits<float>::epsilon()));
 	for(unsigned int ui=0;ui<dOut->data.size();ui++)
 	{
 		Point3D p;
@@ -1258,7 +1267,7 @@ bool rectTest()
 	TEST(f->setProperty(KEY_CORNER,s,needUp),"Set prop");
 
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 	delete f;
 	delete d;
 
diff --git a/src/backend/filters/ionClip.h b/src/backend/filters/ionClip.h
index 80adc78..5f56cdf 100644
--- a/src/backend/filters/ionClip.h
+++ b/src/backend/filters/ionClip.h
@@ -1,6 +1,6 @@
 /*
  *	ionClip.h - Clipping of 3D point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -39,9 +39,9 @@ class IonClipFilter :  public Filter
 		//!Whether to show the primitive or not
 		bool showPrimitive;
 		//!Vector paramaters for different primitives
-		vector<Point3D> vectorParams;
+		std::vector<Point3D> vectorParams;
 		//!Scalar paramaters for different primitives
-		vector<float> scalarParams;
+		std::vector<float> scalarParams;
 		//Lock the primitive axis during for cylinder?
 		bool lockAxisMag; 
 
@@ -60,7 +60,7 @@ class IonClipFilter :  public Filter
 		//!update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 			std::vector<const FilterStreamData *> &getOut, 
-			ProgressData &progress, bool (*callback)(bool));
+			ProgressData &progress);
 	
 		//!Return human readable name for filter	
 		virtual std::string typeString() const { return std::string(TRANS("Clipping"));};
@@ -72,7 +72,7 @@ class IonClipFilter :  public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format, unsigned int depth=0) const;
diff --git a/src/backend/filters/ionColour.cpp b/src/backend/filters/ionColour.cpp
index cf2e05b..72aba88 100644
--- a/src/backend/filters/ionColour.cpp
+++ b/src/backend/filters/ionColour.cpp
@@ -1,6 +1,6 @@
 /*
  *	ionColour.cpp - Filter to create coloured batches of ions based upon value
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -21,6 +21,10 @@
 
 #include "common/colourmap.h"
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
 
 
 const unsigned int MAX_NUM_COLOURS=256;
@@ -62,7 +66,7 @@ Filter *IonColourFilter::cloneUncached() const
 	p->showColourBar =showColourBar;	
 	p->reverseMap=reverseMap;	
 	
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -78,7 +82,7 @@ size_t IonColourFilter::numBytesForCache(size_t nObjects) const
 
 
 unsigned int IonColourFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	//use the cached copy if we have it.
 	if(cacheOK)
@@ -172,7 +176,7 @@ unsigned int IonColourFilter::refresh(const std::vector<const FilterStreamData *
 						n+=NUM_CALLBACK;
 						progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
 						curProg=NUM_CALLBACK;
-						if(!(*callback)(false))
+						if(*Filter::wantAbort)
 						{
 							for(unsigned int ui=0;ui<nColours;ui++)
 								delete d[ui];
@@ -407,7 +411,7 @@ bool IonColourFilter::setProperty(  unsigned int key,
 }
 
 
-std::string  IonColourFilter::getErrString(unsigned int code) const
+std::string  IonColourFilter::getSpecificErrString(unsigned int code) const
 {
 	//Currently the only error is aborting
 	return std::string(TRANS("Aborted"));
@@ -635,7 +639,7 @@ bool ionCountTest()
 	TEST(f->setProperty(KEY_IONCOLOURFILTER_SHOWBAR,"0",needUpdate),"Set prop");
 	
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 	delete f;
 	delete d;
 	
diff --git a/src/backend/filters/ionColour.h b/src/backend/filters/ionColour.h
index 8defcba..abfe02d 100644
--- a/src/backend/filters/ionColour.h
+++ b/src/backend/filters/ionColour.h
@@ -1,6 +1,6 @@
 /*
  *	ionColour.h - Filter to create coloured batches of ions based upon value
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -57,7 +57,7 @@ class IonColourFilter: public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 				std::vector<const FilterStreamData *> &getOut, 
-				ProgressData &progress, bool (*callback)(bool));
+				ProgressData &progress);
 
 		//!return string naming the human readable type of this class
 		virtual std::string typeString() const { return std::string(TRANS("Spectral Colour"));}
@@ -69,7 +69,7 @@ class IonColourFilter: public Filter
 		//!Set the properties for the nth filter
 		bool setProperty( unsigned int key, const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format, 
diff --git a/src/backend/filters/ionDownsample.cpp b/src/backend/filters/ionDownsample.cpp
index 6b0a174..b6e6bfc 100644
--- a/src/backend/filters/ionDownsample.cpp
+++ b/src/backend/filters/ionDownsample.cpp
@@ -1,6 +1,6 @@
 /*
  *	ionDownsample.cpp - Filter to perform sampling without replacement on input ion data
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -22,12 +22,13 @@
 
 #include "ionDownsample.h"
 
+using std::vector;
+using std::string;
 
 //!Downsampling filter
 enum
 {
-	IONDOWNSAMPLE_ABORT_ERR=1,
-	IONDOWNSAMPLE_BAD_ALLOC,
+	IONDOWNSAMPLE_BAD_ALLOC=1,
 	IONDOWNSAMPLE_ERR_ENUM_END
 };
 
@@ -149,7 +150,7 @@ Filter *IonDownsampleFilter::cloneUncached() const
 	std::copy(ionLimits.begin(),ionLimits.end(),p->ionLimits.begin());
 
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -174,7 +175,7 @@ size_t IonDownsampleFilter::numBytesForCache(size_t nObjects) const
 }
 
 unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	//use the cached copy if we have it.
 	if(cacheOK)
@@ -184,7 +185,10 @@ unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamDa
 
 		return 0;
 	}
-
+	
+	progress.step=1;
+	progress.maxStep=1;
+	progress.stepName=TRANS("Sampling");
 
 	size_t totalSize = numElements(dataIn,STREAM_TYPE_IONS);
 	if(!perSpecies)	
@@ -209,14 +213,19 @@ unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamDa
 							frac = (float)(((const IonStreamData*)dataIn[ui])->data.size())/(float)totalSize;
 
 							randomSelect(d->data,((const IonStreamData *)dataIn[ui])->data,
-										rng,maxAfterFilter*frac,progress.filterProgress,callback,strongRandom);
+										rng,maxAfterFilter*frac,progress.filterProgress,
+												*wantAbort,strongRandom);
 
+							if(*Filter::wantAbort)
+							{
+								delete d;
+								return FILTER_ERR_ABORT;
+							}
 
 						}
 						else
 						{
 
-							unsigned int curProg=NUM_CALLBACK;
 							size_t n=0;
 							//Reserve 90% of storage needed.
 							//highly likely with even modest numbers of ions
@@ -225,23 +234,17 @@ unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamDa
 
 							ASSERT(dataIn[ui]->getStreamType() == STREAM_TYPE_IONS);
 
-							for(vector<IonHit>::const_iterator it=((const IonStreamData *)dataIn[ui])->data.begin();
+							for(std::vector<IonHit>::const_iterator it=((const IonStreamData *)dataIn[ui])->data.begin();
 								       it!=((const IonStreamData *)dataIn[ui])->data.end(); ++it)
 							{
 								if(rng.genUniformDev() <  fraction)
 									d->data.push_back(*it);
 							
-								//update progress every CALLBACK ions
-								if(!curProg--)
+								progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
+								if(*Filter::wantAbort)
 								{
-									curProg=NUM_CALLBACK;
-									n+=NUM_CALLBACK;
-									progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-									if(!(*callback)(false))
-									{
-										delete d;
-										return IONDOWNSAMPLE_ABORT_ERR;
-									}
+									delete d;
+									return FILTER_ERR_ABORT;
 								}
 							}
 						}
@@ -347,14 +350,16 @@ unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamDa
 							//The total number of ions is the specified value for this ionID, multiplied by
 							//this stream's fraction of the total incoming data
 							randomSelect(d->data,input->data, rng,(size_t)(frac*ionLimits[ionIDVec[idPos]]),
-									progress.filterProgress,callback,strongRandom);
+									progress.filterProgress,*wantAbort,strongRandom);
+							if(*Filter::wantAbort)
+							{
+								delete d;
+								return FILTER_ERR_ABORT;
+							}
 						}
 						else
 						{
 							//Use the direct fractions as entered in by user. 
-
-							unsigned int curProg=NUM_CALLBACK;
-
 							float thisFraction = ionFractions[ionIDVec[idPos]];
 							
 							//Reserve 90% of storage needed.
@@ -370,17 +375,13 @@ unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamDa
 									if(rng.genUniformDev() <  thisFraction)
 										d->data.push_back(*it);
 								
-									//update progress every CALLBACK ions
-									if(!curProg--)
+									//update progress
+									progress.filterProgress= 
+										(unsigned int)((float)(n)/((float)totalSize)*100.0f);
+									if(*Filter::wantAbort)
 									{
-										n+=NUM_CALLBACK;
-										progress.filterProgress= 
-											(unsigned int)((float)(n)/((float)totalSize)*100.0f);
-										if(!(*callback)(false))
-										{
-											delete d;
-											return IONDOWNSAMPLE_ABORT_ERR;
-										}
+										delete d;
+										return FILTER_ERR_ABORT;
 									}
 								}
 						
@@ -429,6 +430,7 @@ unsigned int IonDownsampleFilter::refresh(const std::vector<const FilterStreamDa
 
 	}	
 
+	progress.filterProgress=100;
 	return 0;
 }
 
@@ -615,10 +617,9 @@ bool IonDownsampleFilter::setProperty(  unsigned int key,
 }
 
 
-std::string  IonDownsampleFilter::getErrString(unsigned int code) const
+std::string  IonDownsampleFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[] = { "",	
-		"Downsample Aborted",
 		"Insuffient memory for downsample",
 	};	
 	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(errStrs) == IONDOWNSAMPLE_ERR_ENUM_END);
@@ -756,7 +757,7 @@ unsigned int IonDownsampleFilter::getRefreshUseMask() const
 
 //Create a synthetic dataset of points
 // returned pointer *must* be deleted. Span must have 3 elements, 
-// and for best results could be co-prime with one another; eg all prime numbers
+// and for best results could be co-prime with one another; e.g. all prime numbers
 IonStreamData *synthDataPts(unsigned int span[],unsigned int numPts);
 
 //Test for fixed number of output ions
@@ -808,7 +809,7 @@ bool fixedSampleTest()
 
 	//Do the refresh
 	ProgressData p;
-	f->refresh(streamIn,streamOut,p,dummyCallback);
+	f->refresh(streamIn,streamOut,p);
 
 	delete f;
 	delete d;
@@ -845,7 +846,7 @@ bool variableSampleTest()
 
 	//Do the refresh
 	ProgressData p;
-	TEST(!(f->refresh(streamIn,streamOut,p,dummyCallback)),"refresh error code");
+	TEST(!(f->refresh(streamIn,streamOut,p)),"refresh error code");
 
 	delete f;
 	delete d;
diff --git a/src/backend/filters/ionDownsample.h b/src/backend/filters/ionDownsample.h
index 63b402a..c83d95a 100644
--- a/src/backend/filters/ionDownsample.h
+++ b/src/backend/filters/ionDownsample.h
@@ -1,6 +1,6 @@
 /*
  *	ionDownsample.h - Filter to perform sampling without replacement on input ion data
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -73,7 +73,7 @@ class IonDownsampleFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 				std::vector<const FilterStreamData *> &getOut, 
-				 ProgressData &progress, bool (*callback)(bool));
+				 ProgressData &progress);
 
 		//!return string naming the human readable type of this class
 		virtual std::string typeString() const { return std::string(TRANS("Ion Sampler"));}
@@ -86,7 +86,7 @@ class IonDownsampleFilter : public Filter
 		//!Set the properties for the nth filter
 		bool setProperty( unsigned int key, const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format, 
diff --git a/src/backend/filters/ionInfo.cpp b/src/backend/filters/ionInfo.cpp
index d640d9c..6a0e710 100644
--- a/src/backend/filters/ionInfo.cpp
+++ b/src/backend/filters/ionInfo.cpp
@@ -1,6 +1,6 @@
 /*
  *	ionInfo.cpp -Filter to compute various properties of valued point cloud
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -18,6 +18,13 @@
 #include "ionInfo.h"
 
 #include "filterCommon.h"
+#include "algorithms/mass.h"
+
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+
 
 enum
 {
@@ -40,7 +47,7 @@ enum
 
 
 bool getRectilinearBounds(const std::vector<const FilterStreamData *> &dataIn, BoundCube &bound,
-					unsigned int *progress, unsigned int totalSize,bool (*callback)(bool))
+					unsigned int *progress, unsigned int totalSize)
 {
 	bound.setInvalid();
 
@@ -79,7 +86,7 @@ bool getRectilinearBounds(const std::vector<const FilterStreamData *> &dataIn, B
 			}
 		
 			*progress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-			if(!(*callback)(false))
+			if(*Filter::wantAbort)
 				return false;
 		}
 	}
@@ -103,7 +110,7 @@ bool getRectilinearBounds(const std::vector<const FilterStreamData *> &dataIn, B
 
 IonInfoFilter::IonInfoFilter() : wantIonCounts(true), wantNormalise(false),
 	range(0), wantVolume(false), volumeAlgorithm(VOLUME_MODE_RECTILINEAR),
-	cubeSideLen(1.0f)
+	cubeSideLen(1.0f), fitMode(FIT_MODE_NONE), massBackStart(1.2),massBackEnd(1.8),binWidth(0.05)
 {
 	cacheOK=false;
 	cache=true; //By default, we should cache, but decision is made higher up
@@ -171,7 +178,7 @@ Filter *IonInfoFilter::cloneUncached() const
 	p->cubeSideLen=cubeSideLen;
 	p->volumeAlgorithm=volumeAlgorithm;
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -180,7 +187,7 @@ Filter *IonInfoFilter::cloneUncached() const
 }
 
 unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 
 	//Count the number of ions input
@@ -197,11 +204,36 @@ unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *>
 	//Compute ion counts/composition as needed
 	if(wantIonCounts)
 	{
+		
 		std::string str;
 		//Count the number of ions
 		if(range)
 		{
+			float intensity=0;
+			if(fitMode!= FIT_MODE_NONE)
+			{
+				BACKGROUND_PARAMS backParams;
+				backParams.massStart=massBackStart;
+				backParams.massEnd=massBackEnd;
+				backParams.binWidth=binWidth;
+				backParams.mode=fitMode;
+
+			
+				//fit a constant tof (1/sqrt (mass)) type background
+				if(doFitBackground(dataIn,backParams))
+				{
+					//display a warning that the background failed
+					consoleOutput.push_back(TRANS("Background fit failed - input data was considered ill formed (gauss-test)"));
+					consoleOutput.push_back(TRANS("Following data has not been corrected"));
+				}
+				else 
+				{
+					intensity=backParams.intensity;
+					
+				}
+			}
 			vector<size_t> numIons;
+
 			ASSERT(range);
 
 			const RangeFile *r=range->rangeFile;
@@ -218,14 +250,30 @@ unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *>
 
 				for(size_t uj=0;uj<i->data.size(); uj++)
 				{
-					unsigned int id;
-					id = r->getIonID(i->data[uj].getMassToCharge());
-					if(id != (unsigned int) -1)
-						numIons[id]++;
+					unsigned int idIon;
+					idIon = r->getIonID(i->data[uj].getMassToCharge());
+					if(idIon != (unsigned int) -1)
+						numIons[idIon]++;
 					else
 						numIons[numIons.size()-1]++;
 				}
 			}
+
+			if(intensity > 0)
+			{
+				for(unsigned int ui=0;ui<r->getNumRanges(); ui++)
+				{
+					pair<float,float> masses;
+					float integral;
+					
+					//compute the integral of the fitted background, then subtract this from the
+					// ion count	
+					masses=r->getRange(ui);
+					integral = 2.0f*intensity*(sqrtf(masses.second) - sqrtf(masses.first));
+
+					numIons[r->getIonID(ui)]-=integral;	
+				}
+			}
 			
 			stream_cast(str,numTotalPoints);
 			str=std::string(TRANS("--Counts--") );
@@ -294,7 +342,7 @@ unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *>
 			{
 				BoundCube bound;
 				if(!getRectilinearBounds(dataIn,bound,
-					&(progress.filterProgress),numTotalPoints,callback))
+					&(progress.filterProgress),numTotalPoints))
 					return ERR_USER_ABORT;
 
 				if(bound.isValid())
@@ -322,14 +370,16 @@ unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *>
 			{
 				//OK, so here we need to do a convex hull estimation of the volume.
 				unsigned int err;
-				err=convexHullEstimateVol(dataIn,computedVol,callback);
+				err=convexHullEstimateVol(dataIn,computedVol);
 				if(err)
 					return err;
 
 				std::string s;
 				stream_cast(s,computedVol);
 				if(computedVol>0)
+				{
 					consoleOutput.push_back(string(TRANS("Convex Volume (len^3): ")) + s);
+				}
 				else
 					consoleOutput.push_back(string(TRANS("Unable to compute volume")));
 
@@ -351,7 +401,7 @@ unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *>
 	//These
 	if(wantIonCounts && wantVolume)
 	{
-		if(computedVol > sqrt(std::numeric_limits<float>::epsilon()))
+		if(computedVol > sqrtf(std::numeric_limits<float>::epsilon()))
 		{
 			float density;
 			std::string s;
@@ -366,7 +416,6 @@ unsigned int IonInfoFilter::refresh(const std::vector<const FilterStreamData *>
 			density=(float)numTotalPoints/computedVol;
 			stream_cast(s,density);
 			consoleOutput.push_back(string(TRANS("Total Density (pts/vol):")) + s );
-
 	
 		}
 	}
@@ -406,6 +455,7 @@ void IonInfoFilter::getProperties(FilterPropGroup &propertyList) const
 	propertyList.addProperty(p,curGroup);
 
 
+	propertyList.setGroupTitle(curGroup,TRANS("Ion data"));
 	if(wantIonCounts && range)
 	{
 		stream_cast(str,wantNormalise);
@@ -416,9 +466,63 @@ void IonInfoFilter::getProperties(FilterPropGroup &propertyList) const
 		p.helpText=TRANS("Normalise count data");
 
 		propertyList.addProperty(p,curGroup);
+
+		/*
+		p.name=TRANS("Back. Correct");
+		choices.clear();
+		for(unsigned int ui=0;ui<FIT_MODE_ENUM_END; ui++)
+		{
+			choices.push_back(make_pair((unsigned int)ui,
+						TRANS(BACKGROUND_MODE_STRING[ui])));
+		}
+		str=choiceString(choices,fitMode);
+		p.data=str;
+		p.type=PROPERTY_TYPE_CHOICE;
+		p.helpText=TRANS("Background correction mode");
+		p.key=IONINFO_KEY_BACKMODE;
+		propertyList.addProperty(p,curGroup);
+
+
+		switch(fitMode)
+		{
+			case FIT_MODE_NONE:
+				break;
+			case FIT_MODE_CONST_TOF:
+				//we need mass start/end for our window
+				// and a binwidth to use for TOF binning
+				stream_cast(str,massBackStart);
+				p.data=str;
+				p.name=TRANS("Mass start");
+				p.type=PROPERTY_TYPE_REAL;
+				p.helpText=TRANS("Background correction fit starting mass");
+				p.key=IONINFO_KEY_BACK_MASSSTART;
+				propertyList.addProperty(p,curGroup);
+				
+				stream_cast(str,massBackEnd);
+				p.data=str;
+				p.name=TRANS("Mass end");
+				p.type=PROPERTY_TYPE_REAL;
+				p.helpText=TRANS("Background correction fit ending mass");
+				p.key=IONINFO_KEY_BACK_MASSEND;
+				propertyList.addProperty(p,curGroup);
+
+
+				stream_cast(str,binWidth);
+				p.data=str;
+				p.name=TRANS("Mass binning");
+				p.type=PROPERTY_TYPE_REAL;
+				p.helpText=TRANS("Bin size to use to build spectrum for performing fit");
+				p.key=IONINFO_KEY_BACK_BINSIZE;
+				propertyList.addProperty(p,curGroup);
+				break;
+			case FIT_MODE_ENUM_END:
+				ASSERT(false);
+	
+		}
+		*/
+		
 	}
 
-	propertyList.setGroupTitle(curGroup,TRANS("Ion data"));
 	curGroup++;
 
 	stream_cast(str,wantVolume);
@@ -430,7 +534,8 @@ void IonInfoFilter::getProperties(FilterPropGroup &propertyList) const
 	propertyList.addProperty(p,curGroup);
 
 	if(wantVolume)
-	{
+	{	
+		choices.clear();
 		for(unsigned int ui=0;ui<VOLUME_MODE_END; ui++)
 		{
 			choices.push_back(make_pair((unsigned int)ui,
@@ -481,6 +586,52 @@ bool IonInfoFilter::setProperty(  unsigned int key,
 				return false;
 			break;
 		}
+		case IONINFO_KEY_BACKMODE:
+		{
+			unsigned int newMode;
+			for(size_t ui=0;ui<FIT_MODE_ENUM_END; ui++)
+			{
+				if(string(BACKGROUND_MODE_STRING[ui]) == string(value))
+				{
+					newMode=ui;
+					break;
+				}
+			}
+
+			ASSERT(newMode <FIT_MODE_ENUM_END)
+			
+			fitMode=newMode;
+			cacheOK=false;
+			needUpdate=true;
+			
+			break;
+		}
+		case IONINFO_KEY_BACK_MASSSTART:
+		{
+			float tmpMass;
+			if(stream_cast(tmpMass,value))
+				return false;
+			if(tmpMass >=massBackEnd)
+				return false;
+	
+			if(!applyPropertyNow(massBackStart,value,needUpdate))
+				return false;
+			break;
+		}
+		case IONINFO_KEY_BACK_MASSEND:
+		{
+			float tmpMass;
+			if(stream_cast(tmpMass,value))
+				return false;
+			if(tmpMass <=massBackStart)
+				return false;
+	
+			if(!applyPropertyNow(massBackEnd,value,needUpdate))
+				return false;
+			break;
+		}
+
+			
 		case IONINFO_KEY_VOLUME_ALGORITHM:
 		{
 			unsigned int newAlg=VOLUME_MODE_END;
@@ -501,6 +652,21 @@ bool IonInfoFilter::setProperty(  unsigned int key,
 			needUpdate=true;
 			break;	
 		}
+		case IONINFO_KEY_BACK_BINSIZE:
+		{
+			float tmpWidth;
+			if(stream_cast(tmpWidth,value))
+				return false;
+			if(tmpWidth <=0.0f)
+				return false;
+	
+			if(tmpWidth > massBackEnd - massBackStart)
+				tmpWidth=massBackEnd-massBackStart;
+		
+			binWidth=tmpWidth;
+			
+			break;
+		}
 		default:
 			ASSERT(false);
 	}
@@ -508,7 +674,7 @@ bool IonInfoFilter::setProperty(  unsigned int key,
 	return true;
 }
 
-std::string  IonInfoFilter::getErrString(unsigned int code) const
+std::string  IonInfoFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[] = { "",
 		"Aborted",
@@ -527,20 +693,20 @@ void IonInfoFilter::setPropFromBinding(const SelectionBinding &b)
 }
 
 unsigned int IonInfoFilter::convexHullEstimateVol(const vector<const FilterStreamData*> &data, 
-								float &volume,bool (*callback)(bool))
+								float &volume)
 {
 	volume=0;
 
 	//TODO: replace with real progress
 	unsigned int dummyProgress;
-	unsigned int errCode;
 
 	//Compute the convex hull, leaving the qhull data structure intact
 	const bool NO_FREE_QHULL=false;
+	const bool WANT_QHULL_VOL=true;
 	vector<Point3D> hullPts;
-	if((errCode=computeConvexHull(data,&dummyProgress,callback,
-						hullPts,NO_FREE_QHULL)))
-		return errCode;
+	if(computeConvexHull(data,&dummyProgress,
+			hullPts,WANT_QHULL_VOL,NO_FREE_QHULL))
+		return ERR_BAD_QHULL;
 
 	Point3D midPt(0,0,0);
 
@@ -552,40 +718,7 @@ unsigned int IonInfoFilter::convexHullEstimateVol(const vector<const FilterStrea
 	//We don't need this result anymore
 	hullPts.clear();
 
-	//OK, so we computed the hull. Good work.
-	//now compute the volume
-	facetT *curFac = qh facet_list;
-	while(curFac != qh facet_tail)
-	{
-		vertexT *vertex;
-		unsigned int ui;
-		Point3D ptArray[3];
-
-		//Facet should be a simplical facet (i.e. triangle)
-		ASSERT(curFac->simplicial);
-
-		ui=0;
-		vertex  = (vertexT *)curFac->vertices->e[ui].p;
-		while(vertex)
-		{	//copy the vertex info into the pt array
-			(ptArray[ui])[0]  = vertex->point[0];
-			(ptArray[ui])[1]  = vertex->point[1];
-			(ptArray[ui])[2]  = vertex->point[2];
-
-			//increment before updating vertex
-			//to allow checking for NULL termination
-			ui++;
-			vertex  = (vertexT *)curFac->vertices->e[ui].p;
-
-		}
-
-		//note that this counter has been post incremented.
-		ASSERT(ui ==3);
-		volume+=pyramidVol(ptArray,midPt);
-
-
-		curFac=curFac->next;
-	}
+	volume=qh totvol;
 
 
 	//Free the convex hull mem
@@ -609,6 +742,9 @@ bool IonInfoFilter::writeState(std::ostream &f,unsigned int format, unsigned int
 			f << tabs(depth+1) << "<wantvolume value=\""<<wantVolume<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<volumealgorithm value=\""<<volumeAlgorithm<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<cubesidelen value=\""<<cubeSideLen<< "\"/>"  << endl;
+			f << tabs(depth+1) << "<background mode=\"" << fitMode << "\">" << endl;
+				f << tabs(depth+2) << "<fitwindow start=\"" << massBackStart<< "\" end=\"" << massBackEnd << "\"/>" << endl;
+			f << tabs(depth+1) << "</background>" << endl;
 
 			f << tabs(depth) << "</" <<trueName()<< ">" << endl;
 			break;
@@ -679,6 +815,28 @@ bool IonInfoFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileD
 	cubeSideLen=tmpFloat;
 	//--=
 
+	//Retrieve background fitting mode, if we have it
+	// Only available 3Depict >= 0.0.18
+	// internal rev > 3e41b89299f4
+	if(!XMLHelpFwdToElem(nodePtr,"background"))
+	{
+		if(XMLHelpGetProp(fitMode,nodePtr,"mode"))
+			return false;
+
+		if(!nodePtr->xmlChildrenNode)
+			return false;
+			
+		xmlNodePtr tmpNode=nodePtr;
+		nodePtr=nodePtr->xmlChildrenNode;
+		
+		if(!XMLGetNextElemAttrib(nodePtr,massBackStart,"fitwindow","start"))
+			return false;
+
+		if(XMLHelpGetProp(massBackEnd,nodePtr,"end"))
+			return false;
+		
+		nodePtr=tmpNode;
+	}
 
 	return true;
 }
@@ -698,6 +856,10 @@ unsigned int IonInfoFilter::getRefreshUseMask() const
 	return  STREAM_TYPE_IONS | STREAM_TYPE_RANGE;
 }
 
+bool IonInfoFilter::needsUnrangedData() const
+{ 
+	return fitMode == FIT_MODE_CONST_TOF;
+}
 #ifdef DEBUG
 
 void makeBox(float boxSize,IonStreamData *d)
@@ -780,7 +942,7 @@ bool volumeBoxTest()
 	streamIn.push_back(d);
 	
 	ProgressData p;
-	f->refresh(streamIn,streamOut,p,dummyCallback);
+	f->refresh(streamIn,streamOut,p);
 
 	//No ions come out of the info
 	TEST(streamOut.empty(),"stream size test");
@@ -799,7 +961,7 @@ bool volumeBoxTest()
 	volReal =SOMEBOX*SOMEBOX*SOMEBOX; 
 
 	TEST(fabs(volMeasure -volReal) < 
-		10.0f*sqrt(std::numeric_limits<float>::epsilon()),
+		10.0f*sqrtf(std::numeric_limits<float>::epsilon()),
 					"volume estimation test (rect)");
 
 	
@@ -807,11 +969,11 @@ bool volumeBoxTest()
 	stream_cast(s,(int)VOLUME_MODE_CONVEX);
 	f->setProperty(IONINFO_KEY_VOLUME_ALGORITHM, s,needUp);
 	
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback), "refresh");
+	TEST(!f->refresh(streamIn,streamOut,p), "refresh");
 	volMeasure=f->getLastVolume();
 
 	TEST(fabs(volMeasure -volReal) < 
-		10.0f*sqrt(std::numeric_limits<float>::epsilon()),
+		10.0f*sqrtf(std::numeric_limits<float>::epsilon()),
 				"volume estimation test (convex)");
 	
 
@@ -849,7 +1011,7 @@ bool volumeSphereTest()
 	streamIn.push_back(d);
 	
 	ProgressData p;
-	f->refresh(streamIn,streamOut,p,dummyCallback);
+	f->refresh(streamIn,streamOut,p);
 
 	//No ions come out of the info
 	TEST(streamOut.empty(),"stream size test");
@@ -875,7 +1037,7 @@ bool volumeSphereTest()
 	vector<string> dummy;
 	f->getConsoleStrings(dummy);
 
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 
 	volMeasure=f->getLastVolume();
 
diff --git a/src/backend/filters/ionInfo.h b/src/backend/filters/ionInfo.h
index 7f031c8..6c32a21 100644
--- a/src/backend/filters/ionInfo.h
+++ b/src/backend/filters/ionInfo.h
@@ -1,6 +1,6 @@
 /*
  *	ionInfo.h -Filter to compute various properties of valued point cloud
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -27,6 +27,10 @@ enum
 	IONINFO_KEY_NORMALISE,
 	IONINFO_KEY_VOLUME,
 	IONINFO_KEY_VOLUME_ALGORITHM,
+	IONINFO_KEY_BACKMODE,
+	IONINFO_KEY_BACK_MASSSTART,
+	IONINFO_KEY_BACK_MASSEND,
+	IONINFO_KEY_BACK_BINSIZE
 };
 
 //!Ion derived information filter, things like volume, composition, etc.
@@ -52,6 +56,14 @@ class IonInfoFilter : public Filter
 		//Side length for filled cube volume estimation
 		float cubeSideLen;
 
+		//mode for performing background correction
+		unsigned int fitMode;
+
+		//start/end mass for background correction
+		float massBackStart, massBackEnd;
+
+		//binwidth to use when performing background correction
+		float binWidth;
 #ifdef DEBUG
 		float lastVolume;
 #endif
@@ -62,8 +74,8 @@ class IonInfoFilter : public Filter
 		//Convex hull volume estimation routine.
 		//returns 0 on success. global "qh " "object"  will contain
 		//the hull. Volume is computed.
-		static unsigned int convexHullEstimateVol(const vector<const FilterStreamData*> &data, 
-							float &vol,bool (*callback)(bool));
+		static unsigned int convexHullEstimateVol(const std::vector<const FilterStreamData*> &data, 
+							float &vol);
 	public:
 		//!Constructor
 		IonInfoFilter();
@@ -80,7 +92,7 @@ class IonInfoFilter : public Filter
 		// ->cached.
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 							std::vector<const FilterStreamData *> &dataOut,
-							ProgressData &progress, bool (*callback)(bool));
+							ProgressData &progress);
 		//!Get (approx) number of bytes required for cache
 		size_t numBytesForCache(size_t nObjects) const;
 
@@ -102,7 +114,7 @@ class IonInfoFilter : public Filter
 		void setPropFromBinding( const SelectionBinding &b) ;
 
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 
 		//!Dump state to output stream, using specified format
 		/* Current supported formats are STATE_FORMAT_XML
@@ -127,6 +139,9 @@ class IonInfoFilter : public Filter
 		//!Get the bitmask encoded list of filterstreams that this filter may use during ::refresh.
 		unsigned int getRefreshUseMask() const;
 
+
+		//!Does the filter need unranged input?
+		bool needsUnrangedData() const; 
 #ifdef DEBUG
 		bool runUnitTests();
 
diff --git a/src/backend/filters/rangeFile.cpp b/src/backend/filters/rangeFile.cpp
index 667d917..479a92f 100644
--- a/src/backend/filters/rangeFile.cpp
+++ b/src/backend/filters/rangeFile.cpp
@@ -1,6 +1,6 @@
 /*
  *	rangeFile.cpp - bins ions into different value ranges given an input range file
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
 #include <map>
 
 using std::map;
+using std::vector;
+using std::string;
 
 const unsigned int NUM_ROWS_ION=3;
 const unsigned int NUM_ROWS_RANGE=4;
@@ -57,7 +59,7 @@ Filter *RangeFileFilter::cloneUncached() const
 	p->assumedFileFormat=assumedFileFormat;
 	p->dropUnranged=dropUnranged;
 
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -98,7 +100,7 @@ void RangeFileFilter::initFilter(const std::vector<const FilterStreamData *> &da
 }
 
 unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-		std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+		std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 
 	//use the cached copy of the data if we have it.
@@ -165,6 +167,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 			d[ui]->parent=this;
 		}
 		
+		const unsigned int RANGE_ALLOC_STEP=157; //is prime - less likely to form sequence?
 		if(!haveEnabled)
 		{
 			//There are no enabled ranges at all.
@@ -175,6 +178,8 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 			//Step 1: Do a first sweep to obtain range sizes needed
 			// then reserve the same amount of mem as we need on the output
 			//========================
+			//Don't examine every ion to determine the allocation size.
+			// perform step-wise examination of dataset
 			for(unsigned int ui=0;ui<dataIn.size() ;ui++)
 			{
 				switch(dataIn[ui]->getStreamType())
@@ -197,7 +202,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 						unsigned int curProg=NUM_CALLBACK;
 						bool spin=false;
 						#pragma omp parallel for firstprivate(curProg)
-						for(size_t uj=0; uj<src->data.size();uj++)
+						for(size_t uj=0; uj<src->data.size();uj+=RANGE_ALLOC_STEP)
 						{
 #ifdef _OPENMP
 							unsigned int thisT=omp_get_thread_num();
@@ -237,7 +242,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 								curProg=NUM_CALLBACK;
 
 
-								if(!(*callback)(false))
+								if(*Filter::wantAbort)
 									spin=true;
 								}
 							}
@@ -263,8 +268,9 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 		//reserve the vector to the exact size we need
 		try
 		{
+			//slightly over-allocate to allow for any variance
 			for(size_t ui=0;ui<d.size();ui++)
-				d[ui]->data.reserve(dSizes[ui]);
+				d[ui]->data.reserve(dSizes[ui]*1.f*RANGE_ALLOC_STEP+10);
 		}
 		catch(std::bad_alloc)
 		{
@@ -288,7 +294,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 		//=========================================
 		if(haveEnabled)
 		{
-			//	I tried parallelising this a few different ways, but the linear performance wa simply better.
+			//	I tried parallelising this a few different ways, but the linear performance was simply better.
 			//		- Tried an array of openmp locks
 			//		- Tried keeping a unique offset number and fixing the size of the output vectors
 			//		- Tried straight criticalling the push_back
@@ -359,7 +365,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 								curProg=NUM_CALLBACK;
 
 								
-								if(!(*callback)(false))
+								if(*Filter::wantAbort)
 								{
 									//Free space allocated for output ion streams...
 									for(unsigned int ui=0;ui<d.size();ui++)
@@ -410,7 +416,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 				}
 			}
 
-			if(!(*callback)(false))
+			if(*Filter::wantAbort)
 			{
 				//Free space allocated for output ion streams...
 				for(unsigned int ui=0;ui<d.size();ui++)
@@ -478,26 +484,7 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 
 	if(haveEnabled && showLegend)
 	{
-		//Create a legend bar, which shows the ions that are present
-		DrawStreamData *dS = new DrawStreamData;
-
-		dS->parent=this;
-
-		DrawPointLegendOverlay *dl=new DrawPointLegendOverlay;
-		dl->setPosition(0.1,0.1);
-	
-		for(unsigned int ui=0;ui<enabledIons.size();ui++)
-		{	
-			if(!enabledIons[ui])
-				continue;
-
-			RGBf curRGBf;
-			curRGBf =rng.getColour(ui);
-			dl->addItem(rng.getName(ui),
-				curRGBf.red, curRGBf.green,curRGBf.blue);
-		}
-
-		dS->drawables.push_back(dl);
+		DrawStreamData *dS = createLegend();
 		cacheAsNeeded(dS);
 
 		getOut.push_back(dS);
@@ -523,6 +510,8 @@ unsigned int RangeFileFilter::refresh(const std::vector<const FilterStreamData *
 	return 0;
 }
 
+
+
 bool RangeFileFilter::updateRng()
 {
 	if(!rng.openGuessFormat(rngName.c_str()))
@@ -782,8 +771,48 @@ bool RangeFileFilter::setProperty(unsigned int key,
 		}	
 		case RANGE_KEY_ENABLE_LEGEND:
 		{
-			if(!applyPropertyNow(showLegend,value,needUpdate))
+			if(!cacheOK || !cache)
+			{
+				if(!applyPropertyNow(showLegend,value,needUpdate))
+					return false;
+				break;
+			}
+
+			//Manually decode the value, then fiddle with the cache
+			// so we don't invalidate it and cause a full recomputation
+			bool newShow;
+			if(!boolStrDec(value,newShow))
 				return false;
+
+			if(showLegend == newShow)
+				return false;
+
+			if(showLegend)
+			{
+				//disabling legend, find it and destroy it in cache, rather
+				// than recomputing all ranging
+				for(size_t ui=0; ui<filterOutputs.size(); ui++)
+				{
+					if(filterOutputs[ui]->getStreamType() == STREAM_TYPE_DRAW)
+					{
+						delete filterOutputs[ui];
+						std::swap(filterOutputs[ui],filterOutputs.back());
+						filterOutputs.pop_back();
+						break;
+					}	
+				}
+			}
+			else
+			{
+				//enabling legend, create the new legened and add it to the cache
+				DrawStreamData *ds = createLegend();
+				ds->cached=1;
+				filterOutputs.push_back(ds);
+					
+			}
+
+			showLegend=newShow;
+			needUpdate=true;
 			break;
 		}	
 		case RANGE_KEY_ENABLE_ALL_RANGES:
@@ -998,7 +1027,7 @@ bool RangeFileFilter::setProperty(unsigned int key,
 }
 
 
-std::string  RangeFileFilter::getErrString(unsigned int code) const
+std::string  RangeFileFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[] ={ "",
 		"Ranging aborted by user",
@@ -1327,6 +1356,31 @@ bool RangeFileFilter::writePackageState(std::ostream &f, unsigned int format,
 	return result;
 }
 
+DrawStreamData *RangeFileFilter::createLegend() const
+{
+	//Create a legend bar, which shows the ions that are present
+	DrawStreamData *dS = new DrawStreamData;
+
+	dS->parent=this;
+
+	DrawPointLegendOverlay *dl=new DrawPointLegendOverlay;
+	dl->setPosition(0.1,0.1);
+
+	for(unsigned int ui=0;ui<enabledIons.size();ui++)
+	{	
+		if(!enabledIons[ui])
+			continue;
+
+		RGBf curRGBf;
+		curRGBf =rng.getColour(ui);
+		dl->addItem(rng.getName(ui),
+			curRGBf.red, curRGBf.green,curRGBf.blue);
+	}
+
+	dS->drawables.push_back(dl);
+
+	return dS;
+}
 #ifdef DEBUG
 
 bool testRanged();
@@ -1393,7 +1447,7 @@ bool testRanged()
 
 	//Run the initialisation stage
 	ProgressData prog;
-	TEST(!r->refresh(streamIn,streamOut,prog,dummyCallback),"Refresh error code");
+	TEST(!r->refresh(streamIn,streamOut,prog),"Refresh error code");
 	//--
 	
 	//Run the tests
diff --git a/src/backend/filters/rangeFile.h b/src/backend/filters/rangeFile.h
index f8190d9..e80616e 100644
--- a/src/backend/filters/rangeFile.h
+++ b/src/backend/filters/rangeFile.h
@@ -1,6 +1,6 @@
 /*
  *	rangeFile.h - bins ions into different value ranges given an input range file
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -55,13 +55,16 @@ class RangeFileFilter : public Filter
 		//Show a legend of enabled ions
 		bool showLegend;
 
+		//create a legend drawable. Note that the pointer 
+		// will not be deleted by this function 
+		DrawStreamData *createLegend() const;
 	public:
 
 		//!Set the format to assume when loading file
 		void setFormat(unsigned int format);
 	
 		std::vector<char> getEnabledRanges() const {return enabledRanges;};
-		void setEnabledRanges(const vector<char> &i) {enabledRanges = i;};
+		void setEnabledRanges(const std::vector<char> &i) {enabledRanges = i;};
 		
 		std::vector<char> getEnabledIons() const {return enabledIons;};
 
@@ -82,7 +85,7 @@ class RangeFileFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 					std::vector<const FilterStreamData *> &getOut, 
-					ProgressData &progress, bool (*callback)(bool));
+					ProgressData &progress);
 		//!Force a re-read of the rangefile, returning false on failure, true on success
 		bool updateRng();
 		
@@ -111,7 +114,7 @@ class RangeFileFilter : public Filter
 		//!Set a region update
 		virtual void setPropFromRegion(unsigned int method, unsigned int regionID, float newPos);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format,
@@ -126,10 +129,11 @@ class RangeFileFilter : public Filter
 		bool readState(xmlNodePtr &node, const std::string &packDir);
 		
 		//!filter has state overrides	
-		virtual void getStateOverrides(std::vector<string> &overrides) const; 
+		virtual void getStateOverrides(std::vector<std::string> &overrides) const; 
 		//!Set internal property value using a selection binding  (Disabled, this filter has no bindings)
 		void setPropFromBinding(const SelectionBinding &b)  ;
 
+		bool getDropUnranged() const { return dropUnranged; }
 #ifdef DEBUG
 		bool runUnitTests();
 #endif
diff --git a/src/backend/filters/spatialAnalysis.cpp b/src/backend/filters/spatialAnalysis.cpp
index b0fdb44..8d67790 100644
--- a/src/backend/filters/spatialAnalysis.cpp
+++ b/src/backend/filters/spatialAnalysis.cpp
@@ -1,6 +1,6 @@
 /*
  *	spatialAnalysis.cpp - Perform various data analysis on 3D point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -15,6 +15,8 @@
  *	You should have received a copy of the GNU General Public License
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+#include <gsl/gsl_sf_gamma.h>
+
 #include "spatialAnalysis.h"
 
 
@@ -27,6 +29,13 @@
 #include "backend/plot.h"
 #include "../APT/APTFileIO.h"
 
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::map;
+using std::list;
+
 enum
 {
 	KEY_STOPMODE,
@@ -34,6 +43,7 @@ enum
 	KEY_DISTMAX,
 	KEY_NNMAX,
 	KEY_NNMAX_NORMALISE,
+	KEY_NNMAX_SHOWRANDOM,
 	KEY_NUMBINS,
 	KEY_REMOVAL,
 	KEY_REDUCTIONDIST,
@@ -131,13 +141,28 @@ const bool WANT_RANGE_PROPAGATION[] = { false,
 //Default distance to use when performing axial distance computations
 const float DEFAULT_AXIAL_DISTANCE = 1.0f;
 
-template<class T>
-bool xorFunc(const T a, const T b)
+
+//Helper function for computing a weighted mean
+float weightedMean(const vector<float> &x, const vector<float> &y,bool zeroOutSingularity=true)
 {
-  return (a || b) && !(a && b);
-}
+	ASSERT(x.size() == y.size());
 
+	float num=0,denom=0;
+	for(size_t ui=0;ui<y.size();ui++)
+	{
+		num+=y[ui]*x[ui];
+		denom+=y[ui];
+	}
+
+	if(zeroOutSingularity)
+	{
+		if(denom <std::numeric_limits<float>::epsilon())
+			return 0;
+	}
 
+	ASSERT(denom);
+	return num/denom;
+}
 
 SpatialAnalysisFilter::SpatialAnalysisFilter()
 {
@@ -165,6 +190,7 @@ SpatialAnalysisFilter::SpatialAnalysisFilter()
 	//Density filtering params
 	densityCutoff=1.0f;
 	keepDensityUpper=true;
+	wantRandomNNHist=true;
 
 	//Binomial parameters
 	//--
@@ -178,7 +204,7 @@ SpatialAnalysisFilter::SpatialAnalysisFilter()
 	//--
 
 	//replace tolerance
-	replaceTolerance=sqrt(std::numeric_limits<float>::epsilon());
+	replaceTolerance=sqrtf(std::numeric_limits<float>::epsilon());
 	replaceMode=REPLACE_MODE_SUBTRACT;
 	replaceMass=true;
 
@@ -201,6 +227,8 @@ Filter *SpatialAnalysisFilter::cloneUncached() const
 	p->numBins=numBins;
 	p->excludeSurface=excludeSurface;
 	p->reductionDistance=reductionDistance;
+	p->normaliseNNHist = normaliseNNHist;
+	p->wantRandomNNHist=wantRandomNNHist;
 	
 	p->keepDensityUpper=keepDensityUpper;
 	p->densityCutoff=densityCutoff;
@@ -307,8 +335,36 @@ void SpatialAnalysisFilter::initFilter(const std::vector<const FilterStreamData
 	haveRangeParent=false;
 }
 
+
+void SpatialAnalysisFilter::createDevice(vector<const FilterStreamData *> &getOut) 
+{
+	//Create the user interaction device required for the user
+	// to interact with the algorithm parameters 
+	SelectionDevice *s=0;
+	DrawStreamData *d= new DrawStreamData;
+	d->parent=this;
+	d->cached=0;
+
+	switch(algorithm)
+	{
+		case ALGORITHM_AXIAL_DF:
+			createCylinder(d,s);
+		break;
+		default:	
+			;
+	}
+
+	if(s)
+	{
+		devices.push_back(s);
+		getOut.push_back(d);
+	}
+	else
+		delete d;
+}
+
 unsigned int SpatialAnalysisFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	//use the cached copy if we have it.
 	if(cacheOK)
@@ -317,6 +373,10 @@ unsigned int SpatialAnalysisFilter::refresh(const std::vector<const FilterStream
 		if(!WANT_RANGE_PROPAGATION[algorithm])
 			mask|=STREAM_TYPE_RANGE;
 
+
+		//create selection device for this algrithm
+		createDevice(getOut);
+
 		//Propagate input streams as desired 
 		propagateStreams(dataIn,getOut,mask,true);
 	
@@ -325,6 +385,12 @@ unsigned int SpatialAnalysisFilter::refresh(const std::vector<const FilterStream
 		return 0;
 	}
 
+	//Set K3D tree abort pointer and progress
+	K3DTree::setAbortFlag(Filter::wantAbort);
+	K3DTree::setProgressPtr(&progress.filterProgress);
+
+	K3DTreeMk2::setAbortFlag(Filter::wantAbort);
+	K3DTreeMk2::setProgressPtr(&progress.filterProgress);
 
 	//Find out how much total size we need in points vector
 	size_t totalDataSize=numElements(dataIn,STREAM_TYPE_IONS);
@@ -357,19 +423,19 @@ unsigned int SpatialAnalysisFilter::refresh(const std::vector<const FilterStream
 	switch(algorithm)
 	{
 		case ALGORITHM_DENSITY:
-			result=algorithmDensity(progress,callback,totalDataSize,
+			result=algorithmDensity(progress,totalDataSize,
 					dataIn,getOut);
 			break;
 		case ALGORITHM_RDF:
-			result=algorithmRDF(progress,callback,totalDataSize,
+			result=algorithmRDF(progress,totalDataSize,
 					dataIn,getOut,rngF);
 			break;
 		case ALGORITHM_DENSITY_FILTER:
-			result=algorithmDensityFilter(progress,callback,totalDataSize,
+			result=algorithmDensityFilter(progress,totalDataSize,
 					dataIn,getOut);
 			break;
 		case ALGORITHM_AXIAL_DF:
-			result=algorithmAxialDf(progress,callback,totalDataSize,
+			result=algorithmAxialDf(progress,totalDataSize,
 					dataIn,getOut,rngF);
 			break;
 		case ALGORITHM_BINOMIAL:
@@ -378,12 +444,12 @@ unsigned int SpatialAnalysisFilter::refresh(const std::vector<const FilterStream
 			{
 				return ERR_BINOMIAL_NO_RANGE;
 			}
-			result=algorithmBinomial(progress,callback,totalDataSize,
+			result=algorithmBinomial(progress,totalDataSize,
 						dataIn,getOut,rngF);
 			break;
 		}
 		case ALGORITHM_REPLACE:
-			result=algorithmReplace(progress,callback,totalDataSize,
+			result=algorithmReplace(progress,totalDataSize,
 						dataIn,getOut);
 			break;
 		default:
@@ -393,27 +459,25 @@ unsigned int SpatialAnalysisFilter::refresh(const std::vector<const FilterStream
 	return result;
 }
 
-size_t SpatialAnalysisFilter::algorithmReplace(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
+size_t SpatialAnalysisFilter::algorithmReplace(ProgressData &progress, size_t totalDataSize, 
 			const vector<const FilterStreamData *>  &dataIn, 
 			vector<const FilterStreamData * > &getOut)
 {
 	//Merge the ions form the incoming streams
 	vector<IonHit> inIons;
-	Filter::collateIons(dataIn,inIons,progress,callback,totalDataSize);
+	Filter::collateIons(dataIn,inIons,progress,totalDataSize);
 	
 	vector<IonHit> fileIons;
 	const unsigned int loadPositions[] = {
 						0,1,2,3};
 	unsigned int errCode=GenericLoadFloatFile(4,4,loadPositions,
-			fileIons,replaceFile.c_str(),progress.filterProgress,callback);
+			fileIons,replaceFile.c_str(),progress.filterProgress,*Filter::wantAbort);
 
 	if(errCode)
 		return ERR_FILE_READ_FAIL;
 
 
 	K3DTreeMk2 tree;
-	tree.setProgressPointer(&progress.filterProgress);
-	tree.setCallback(callback);
 	tree.resetPts(fileIons,false);
 	tree.build();
 	BoundCube b;
@@ -579,15 +643,29 @@ void SpatialAnalysisFilter::getProperties(FilterPropGroup &propertyList) const
 			p.type=PROPERTY_TYPE_INTEGER;
 			p.helpText=TRANS("Maximum number of neighbours to examine");
 			p.key=KEY_NNMAX;
+			propertyList.addProperty(p,curGroup);
+			
 			if(algorithm == ALGORITHM_RDF)
 			{
-				propertyList.addProperty(p,curGroup);
 
 				p.name=TRANS("Normalise bins");
 				p.data=boolStrEnc(normaliseNNHist);
 				p.type=PROPERTY_TYPE_BOOL;
 				p.helpText=TRANS("Normalise counts by binwidth. Needed when comparing NN histograms against one another");
 				p.key=KEY_NNMAX_NORMALISE;
+				propertyList.addProperty(p,curGroup);
+
+
+
+				p.name=TRANS("Show Random");
+				p.data=boolStrEnc(wantRandomNNHist);
+				p.type=PROPERTY_TYPE_BOOL;
+				p.helpText=TRANS("Show a fitted (density matched) theoretical distribution");
+				p.key=KEY_NNMAX_SHOWRANDOM;
+				propertyList.addProperty(p,curGroup);
+
+
+
 			}
 		}
 		else
@@ -598,8 +676,8 @@ void SpatialAnalysisFilter::getProperties(FilterPropGroup &propertyList) const
 			p.type=PROPERTY_TYPE_REAL;
 			p.helpText=TRANS("Maximum distance from each point for search");
 			p.key=KEY_DISTMAX;
+			propertyList.addProperty(p,curGroup);
 		}
-		propertyList.addProperty(p,curGroup);
 
 	}
 	
@@ -656,11 +734,8 @@ void SpatialAnalysisFilter::getProperties(FilterPropGroup &propertyList) const
 				
 				string sTmp;
 
-				if((size_t)std::count(ionSourceEnabled.begin(),
-					ionSourceEnabled.end(),true) == ionSourceEnabled.size())
-					sTmp="1";
-				else
-					sTmp="0";
+				sTmp = boolStrEnc((size_t)std::count(ionSourceEnabled.begin(),
+					ionSourceEnabled.end(),true) == ionSourceEnabled.size());
 
 				p.name=TRANS("Source");
 				p.data=sTmp;
@@ -688,11 +763,8 @@ void SpatialAnalysisFilter::getProperties(FilterPropGroup &propertyList) const
 
 				curGroup++;
 				
-				if((size_t)std::count(ionTargetEnabled.begin(),
-					ionTargetEnabled.end(),true) == ionTargetEnabled.size())
-					sTmp="1";
-				else
-					sTmp="0";
+				sTmp = boolStrEnc((size_t)std::count(ionTargetEnabled.begin(),
+					ionTargetEnabled.end(),true) == ionTargetEnabled.size());
 				
 				p.name=TRANS("Target");
 				p.data=sTmp;
@@ -1032,6 +1104,12 @@ bool SpatialAnalysisFilter::setProperty(  unsigned int key,
 				return false;
 			break;
 		}	
+		case KEY_NNMAX_SHOWRANDOM:
+		{
+			if(!applyPropertyNow(wantRandomNNHist,value,needUpdate))
+				return false;
+			break;
+		}	
 		case KEY_NUMBINS:
 		{
 			unsigned int ltmp;
@@ -1180,7 +1258,7 @@ bool SpatialAnalysisFilter::setProperty(  unsigned int key,
 			if(stream_cast(newRad,value))
 				return false;
 
-			if(newRad < sqrt(std::numeric_limits<float>::epsilon()))
+			if(newRad < sqrtf(std::numeric_limits<float>::epsilon()))
 				return false;
 
 			if(scalarParams[0] != newRad )
@@ -1197,7 +1275,7 @@ bool SpatialAnalysisFilter::setProperty(  unsigned int key,
 			if(!newPt.parse(value))
 				return false;
 
-			if(newPt.sqrMag() < sqrt(std::numeric_limits<float>::epsilon()))
+			if(newPt.sqrMag() < sqrtf(std::numeric_limits<float>::epsilon()))
 				return false;
 
 			if(!(vectorParams[1] == newPt ))
@@ -1399,7 +1477,7 @@ bool SpatialAnalysisFilter::setProperty(  unsigned int key,
 	return true;
 }
 
-std::string  SpatialAnalysisFilter::getErrString(unsigned int code) const
+std::string  SpatialAnalysisFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrings[] = {"",
 				"Spatial analysis aborted by user",
@@ -1476,6 +1554,7 @@ bool SpatialAnalysisFilter::writeState(std::ostream &f,unsigned int format, unsi
 			f << tabs(depth+1) << "<stopmode value=\""<<stopMode<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<nnmax value=\""<<nnMax<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<normalisennhist value=\""<<boolStrEnc(normaliseNNHist)<< "\"/>"  << endl;
+			f << tabs(depth+1) << "<wantrandomnnhist value=\""<<boolStrEnc(wantRandomNNHist)<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<distmax value=\""<<distMax<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<numbins value=\""<<numBins<< "\"/>"  << endl;
 			f << tabs(depth+1) << "<excludesurface value=\""<<excludeSurface<< "\"/>"  << endl;
@@ -1490,7 +1569,7 @@ bool SpatialAnalysisFilter::writeState(std::ostream &f,unsigned int format, unsi
 				<< "\" tolerance=\"" << replaceTolerance <<  "\" replacemass=\"" << boolStrEnc(replaceMass) << "\" />"  << endl;
 
 
-			//-- Binomial paramters ---
+			//-- Binomial parameters ---
 			f << tabs(depth+1) << "<binomial numions=\""<<numIonsSegment<< "\" maxblockaspect=\"" 
 						<< maxBlockAspect << "\" extrusiondirection=\"" 
 						<< extrusionDirection << "\"/>"  << endl;
@@ -1566,12 +1645,23 @@ bool SpatialAnalysisFilter::readState(xmlNodePtr &nodePtr, const std::string &st
 	// internal 5033191f0c61
 	//====== 
 	xmlNodePtr tmpNode = nodePtr;
-	if(!XMLGetNextElemAttrib(tmpNode,nnMax,"normalisennhist","value"))
+	if(!XMLGetNextElemAttrib(tmpNode,normaliseNNHist,"normalisennhist","value"))
 	{
 		normaliseNNHist=false;
 	}
 	//===
 	
+	//Retrieve histogram normalisation 
+	//TODO: COMPAT : did not exist prior to 0.0.18
+	// internal revision : 2302dbbfb3dd 
+	//====== 
+	tmpNode = nodePtr;
+	if(!XMLGetNextElemAttrib(tmpNode,wantRandomNNHist,"wantrandomnnhist","value"))
+	{
+		wantRandomNNHist=false;
+	}
+	//===
+	
 	//Retrieve distMax val
 	//====== 
 	if(!XMLGetNextElemAttrib(nodePtr,distMax,"distmax","value"))
@@ -1745,7 +1835,7 @@ void SpatialAnalysisFilter::setPropFromBinding(const SelectionBinding &b)
 		{
 			Point3D p;
 			b.getValue(p);
-			if(p.sqrMag() > sqrt(std::numeric_limits<float>::epsilon()))
+			if(p.sqrMag() > sqrtf(std::numeric_limits<float>::epsilon()))
 				vectorParams[1]=p;
 			break;
 		}
@@ -1792,13 +1882,13 @@ void SpatialAnalysisFilter::resetParamsAsNeeded()
 		;
 	}
 }
-//Scan input datstreams to build a two point vectors,
+//Scan input datastreams to build two point vectors,
 // one of those with points specified as "target" 
 // which is a copy of the input points
 //Returns 0 on no error, otherwise nonzero
 size_t SpatialAnalysisFilter::buildSplitPoints(const vector<const FilterStreamData *> &dataIn,
 				ProgressData &progress, size_t totalDataSize,
-				const RangeFile *rngF, bool (*callback)(bool),
+				const RangeFile *rngF,
 				vector<Point3D> &pSource, vector<Point3D> &pTarget
 				) const
 {
@@ -1822,7 +1912,7 @@ size_t SpatialAnalysisFilter::buildSplitPoints(const vector<const FilterStreamDa
 				{
 					//FIXME: Fallback handling  to re-range data
 					// - this can technically fail for inputs that are
-					// not homogenously ranged!
+					// not homogeneously ranged!
 					break;
 				}
 
@@ -1862,7 +1952,7 @@ size_t SpatialAnalysisFilter::buildSplitPoints(const vector<const FilterStreamDa
 
 				if(ionSourceEnabled[ionID])
 				{
-					if(extendPointVector(pSource,d->data,callback,
+					if(extendPointVector(pSource,d->data,
 					                     progress.filterProgress,curPos[0]))
 						return ERR_ABORT_FAIL;
 
@@ -1871,7 +1961,7 @@ size_t SpatialAnalysisFilter::buildSplitPoints(const vector<const FilterStreamDa
 
 				if(ionTargetEnabled[ionID])
 				{
-					if(extendPointVector(pTarget,d->data,callback,
+					if(extendPointVector(pTarget,d->data,
 					                     progress.filterProgress,curPos[1]))
 						return ERR_ABORT_FAIL;
 
@@ -1925,7 +2015,7 @@ void SpatialAnalysisFilter::filterSelectedRanges(const vector<IonHit> &ions, boo
 //Returns 0 on no error, otherwise nonzero
 size_t buildMonolithicPoints(const vector<const FilterStreamData *> &dataIn,
 				ProgressData &progress, size_t totalDataSize,
-				bool (*callback)(bool),	vector<Point3D> &p)
+				vector<Point3D> &p)
 {
 	//Build monolithic point set
 	//---
@@ -1934,8 +2024,8 @@ size_t buildMonolithicPoints(const vector<const FilterStreamData *> &dataIn,
 	size_t dataSize=0;
 
 	progress.filterProgress=0;
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	for(unsigned int ui=0;ui<dataIn.size() ;ui++)
 	{
@@ -1946,8 +2036,7 @@ size_t buildMonolithicPoints(const vector<const FilterStreamData *> &dataIn,
 				const IonStreamData *d;
 				d=((const IonStreamData *)dataIn[ui]);
 
-				if(extendPointVector(p,d->data,
-						callback,progress.filterProgress,
+				if(extendPointVector(p,d->data,	progress.filterProgress,
 						dataSize))
 					return ERR_ABORT_FAIL;
 
@@ -1963,7 +2052,7 @@ size_t buildMonolithicPoints(const vector<const FilterStreamData *> &dataIn,
 	return 0;
 }
 			
-size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
+size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, size_t totalDataSize, 
 		const vector<const FilterStreamData *>  &dataIn, 
 		vector<const FilterStreamData * > &getOut,const RangeFile *rngF)
 {
@@ -1975,12 +2064,10 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 	else
 		progress.maxStep=3;
 	
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	K3DTree kdTree;
-	kdTree.setCallbackMethod(callback);
-	kdTree.setProgressPointer(&(progress.filterProgress));
 	
 	//Source points
 	vector<Point3D> p;
@@ -1998,7 +2085,7 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 		ASSERT(ionNames.size());
 		size_t errCode;
 		if((errCode=buildSplitPoints(dataIn,progress,totalDataSize,
-				rngF,callback,pts[0],pts[1])))
+				rngF,pts[0],pts[1])))
 			return errCode;
 
 		progress.step=2;
@@ -2007,8 +2094,8 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 		//Build the tree using the target ions
 		//(its roughly nlogn timing, but worst case n^2)
 		kdTree.buildByRef(pts[1]);
-		if(!(*callback)(true))
-			return ERR_ABORT_FAIL;
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
 		pts[1].clear();
 		
 		//Remove surface points from sources if desired
@@ -2018,28 +2105,27 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			progress.step++;
 			progress.stepName=TRANS("Surface");
 
-			if(!(*callback)(true))
-				return ERR_ABORT_FAIL;
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
 
 
 			//Take the input points, then use them
 			//to compute the convex hull reduced 
 			//volume. 
 			vector<Point3D> returnPoints;
-			if(GetReducedHullPts(pts[0],reductionDistance,
-					&progress.filterProgress,callback,returnPoints))
+			errCode=GetReducedHullPts(pts[0],reductionDistance,
+					&progress.filterProgress,*(Filter::wantAbort),
+					returnPoints);
+			if(errCode ==1)
+				return INSUFFICIENT_SIZE_ERR;
+			else if(errCode)
 			{
-				if(errCode ==1)
-					return INSUFFICIENT_SIZE_ERR;
-				else
-				{
-					ASSERT(false);
-					return ERR_ABORT_FAIL;
-				}
+				ASSERT(false);
+				return ERR_ABORT_FAIL;
 			}
 			
-			if(!(*callback)(true))
-				return ERR_ABORT_FAIL;
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
 
 			pts[0].clear();
 			//Forget the original points, and use the new ones
@@ -2052,7 +2138,7 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 	else
 	{
 		size_t errCode;
-		if((errCode=buildMonolithicPoints(dataIn,progress,totalDataSize,callback,p)))
+		if((errCode=buildMonolithicPoints(dataIn,progress,totalDataSize,p)))
 			return errCode;
 		
 		progress.step=2;
@@ -2062,8 +2148,8 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 
 		//Build the tree (its roughly nlogn timing, but worst case n^2)
 		kdTree.buildByRef(p);
-		if(!(*callback)(true))
-			return ERR_ABORT_FAIL;
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
 
 		//Remove surface points if desired
 		if(excludeSurface)
@@ -2072,8 +2158,8 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			progress.step++;
 			progress.stepName=TRANS("Surface");
 		
-			if(!(*callback)(true))
-				return ERR_ABORT_FAIL;
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
 
 
 			//Take the input points, then use them
@@ -2082,7 +2168,7 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			vector<Point3D> returnPoints;
 			size_t errCode;
 			if((errCode=GetReducedHullPts(p,reductionDistance,
-					&progress.filterProgress,callback, 
+					&progress.filterProgress, *Filter::wantAbort,
 					returnPoints)) )
 			{
 				if(errCode ==1)
@@ -2101,8 +2187,8 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			//Forget the original points, and use the new ones
 			p.swap(returnPoints);
 			
-			if(!(*callback)(true))
-				return ERR_ABORT_FAIL;
+			if(*Filter::wantAbort)
+				return FILTER_ERR_ABORT;
 
 		}
 		
@@ -2138,7 +2224,7 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			//Run the analysis
 			errCode=generateNNHist(p,kdTree,nnMax,
 					numBins,histogram,binWidth,
-					&(progress.filterProgress),callback);
+					&(progress.filterProgress),*Filter::wantAbort);
 			switch(errCode)
 			{
 				case 0:
@@ -2161,29 +2247,26 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			vector<vector<float> > histogramFloat;
 			histogramFloat.resize(nnMax); 
 			//Normalise the NN histograms to a per bin width as required
-			if(normaliseNNHist)
+			for(unsigned int ui=0;ui<nnMax; ui++)
 			{
-				for(unsigned int ui=0;ui<nnMax; ui++)
+				histogramFloat[ui].resize(numBins);
+				if(normaliseNNHist)
 				{
-					histogramFloat[ui].resize(numBins);
 					for(unsigned int uj=0;uj<numBins;uj++)
 						histogramFloat[ui][uj] = (float)histogram[ui][uj]/binWidth[ui] ;
 				}
-			
-			}
-			else
-			{
-				for(unsigned int ui=0;ui<nnMax;ui++)
+				else
 				{
-					histogramFloat[ui].resize(numBins);
 					for(unsigned int uj=0;uj<numBins;uj++)
 						histogramFloat[ui][uj] = (float)histogram[ui][uj];
 				}
 			}
 			histogram.clear();
+
 	
 			//Alright then, we have the histogram in x-{y1,y2,y3...y_n} form
 			//lets make some plots shall we?
+			{
 			PlotStreamData *plotData[nnMax];
 
 			for(unsigned int ui=0;ui<nnMax;ui++)
@@ -2220,7 +2303,100 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 				
 				getOut.push_back(plotData[ui]);
 			}
+			}
+
+			//If requested, add a probability distribution.
+			// we need to scale it to match the displayed observed
+			// histogram
+			if(wantRandomNNHist)
+			{
+				vector<vector<float> > nnTheoHist;
+				nnTheoHist.resize(nnMax);
+				#pragma omp parallel for
+				for(unsigned int ui=0;ui<nnMax;ui++)
+				{
+
+					float total=0;
+					for(unsigned int uj=0;uj<numBins;uj++)
+						total+=histogramFloat[ui][uj]*binWidth[ui];
 
+
+					//Generate the eval points
+					vector<float> evalDist;
+					evalDist.resize(numBins);	
+					for(unsigned int uj=0;uj<numBins;uj++)
+						evalDist[uj] = (float)uj*binWidth[ui];
+
+					//Compute the random Knn density parameter from the histogram
+					//equation is from L. Stephenson PhD Thesis, Eq7.8, pp91, 2009,
+					// Univ. Sydney.
+					//--
+					// gamma(3/2+1)^(1/3)
+					const float GAMMA_FACTOR = 1.09954261650577;
+					const float SQRT_PI = 1.77245385090552;
+					
+
+					float mean=weightedMean(evalDist,histogramFloat[ui]);
+					
+					float densNumerator, densDenom;
+					densNumerator= gsl_sf_gamma( (ui+1) + 1.0/3.0);
+					densNumerator*=GAMMA_FACTOR;
+					densDenom=mean*SQRT_PI*gsl_sf_fact(ui);
+					float density;
+					density=densNumerator/densDenom;
+					density*=density*density; //Cubed
+
+					//--
+					//create the distribution
+					generateKnnTheoreticalDist(evalDist,density, ui+1,nnTheoHist[ui]);
+
+					//scale the dist
+					for(size_t uj=0;uj<nnTheoHist[ui].size();uj++)
+					{
+						nnTheoHist[ui][uj]*= total;
+					}
+
+				}
+
+				PlotStreamData *plotData[nnMax];
+				for(unsigned int ui=0;ui<nnMax;ui++)
+				{
+					plotData[ui] = new PlotStreamData;
+					plotData[ui]->index=ui+nnMax;
+					plotData[ui]->parent=this;
+					plotData[ui]->plotMode=PLOT_MODE_1D;
+					plotData[ui]->xLabel=TRANS("Radial Distance");
+
+//					plotData[ui]->lineStyle=LINE_STYLE_DASH;
+
+					if(normaliseNNHist)
+						plotData[ui]->yLabel=TRANS("Count/Distance");
+					else
+						plotData[ui]->yLabel=TRANS("Count");
+					std::string tmpStr;
+					stream_cast(tmpStr,ui+1);
+					plotData[ui]->dataLabel=getUserString() + string(" Random ") +tmpStr + TRANS("NN Freq.");
+
+					//Red plot.
+					plotData[ui]->r=rgba.r();
+					plotData[ui]->g=rgba.g();
+					plotData[ui]->b=rgba.b();
+					plotData[ui]->xyData.resize(numBins);
+
+					for(unsigned int uj=0;uj<numBins;uj++)
+					{
+						float dist;
+						ASSERT(ui < histogramFloat.size() && uj<histogramFloat[ui].size());
+						dist = (float)uj*binWidth[ui];
+						plotData[ui]->xyData[uj] = std::make_pair(dist,
+								nnTheoHist[ui][uj]);
+					}
+
+					cacheAsNeeded(plotData[ui]);
+					
+					getOut.push_back(plotData[ui]);
+				}
+			}
 			delete[] binWidth;
 			break;
 		}
@@ -2236,7 +2412,7 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 			//User is after an RDF analysis. Run it.
 			unsigned int errcode;
 			errcode=generateDistHist(p,kdTree,histogram,distMax,numBins,
-					warnBiasCount,&(progress.filterProgress),callback);
+					warnBiasCount,&(progress.filterProgress),*(Filter::wantAbort));
 
 			if(errcode)
 				return ERR_ABORT_FAIL;
@@ -2304,8 +2480,7 @@ size_t SpatialAnalysisFilter::algorithmRDF(ProgressData &progress, bool (*callba
 }
 
 size_t SpatialAnalysisFilter::algorithmDensity(ProgressData &progress, 
-		bool (*callback)(bool), size_t totalDataSize, 
-		const vector<const FilterStreamData *>  &dataIn, 
+	size_t totalDataSize, const vector<const FilterStreamData *>  &dataIn, 
 		vector<const FilterStreamData * > &getOut)
 {
 	vector<Point3D> p;
@@ -2313,29 +2488,25 @@ size_t SpatialAnalysisFilter::algorithmDensity(ProgressData &progress,
 	progress.step=1;
 	progress.stepName=TRANS("Collate");
 	progress.maxStep=3;
-	if((errCode=buildMonolithicPoints(dataIn,progress,totalDataSize,callback,p)))
+	if((errCode=buildMonolithicPoints(dataIn,progress,totalDataSize,p)))
 		return errCode;
 
 	progress.step=2;
 	progress.stepName=TRANS("Build");
 	progress.filterProgress=0;
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	BoundCube treeDomain;
 	treeDomain.setBounds(p);
 
 	//Build the tree (its roughly nlogn timing, but worst case n^2)
 	K3DTree kdTree;
-	kdTree.setCallbackMethod(callback);
-	kdTree.setProgressPointer(&(progress.filterProgress));
-	
 	kdTree.buildByRef(p);
 
 
-	//Update progress & User interface by calling callback
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	p.clear(); //We don't need pts any more, as tree *is* a copy.
 
@@ -2346,8 +2517,8 @@ size_t SpatialAnalysisFilter::algorithmDensity(ProgressData &progress,
 	progress.step=3;
 	progress.stepName=TRANS("Analyse");
 	progress.filterProgress=0;
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	//List of points for which there was a failure
 	//first entry is the point Id, second is the 
@@ -2403,14 +2574,14 @@ size_t SpatialAnalysisFilter::algorithmDensity(ProgressData &progress,
 
 						res.clear();
 						
-						//Update callback as needed
+						//Update progress as needed
 						if(!curProg--)
 						{
 							#pragma omp critical 
 							{
 							n+=NUM_CALLBACK/(nnMax);
 							progress.filterProgress= (unsigned int)(((float)n/(float)totalDataSize)*100.0f);
-							if(!(*callback)(false))
+							if(*Filter::wantAbort)
 								spin=true;
 							curProg=NUM_CALLBACK/(nnMax);
 							}
@@ -2465,13 +2636,13 @@ size_t SpatialAnalysisFilter::algorithmDensity(ProgressData &progress,
 							numInRad++;
 							//Advance ever so slightly beyond the next ion
 							deadDistSqr = res->sqrDist(r)+std::numeric_limits<float>::epsilon();
-							//Update callback as needed
+							//Update progress as needed
 							if(!curProg--)
 							{
 #pragma omp critical
 								{
 								progress.filterProgress= (unsigned int)((float)n/(float)totalDataSize*100.0f);
-								if(!(*callback)(false))
+								if(*Filter::wantAbort)
 								{
 #ifdef _OPENMP
 									spin=true;
@@ -2595,8 +2766,7 @@ size_t SpatialAnalysisFilter::algorithmDensity(ProgressData &progress,
 }
 
 size_t SpatialAnalysisFilter::algorithmDensityFilter(ProgressData &progress, 
-		bool (*callback)(bool), size_t totalDataSize, 
-		const vector<const FilterStreamData *>  &dataIn, 
+		size_t totalDataSize, const vector<const FilterStreamData *>  &dataIn, 
 		vector<const FilterStreamData * > &getOut)
 {
 	vector<Point3D> p;
@@ -2604,41 +2774,38 @@ size_t SpatialAnalysisFilter::algorithmDensityFilter(ProgressData &progress,
 	progress.step=1;
 	progress.stepName=TRANS("Collate");
 	progress.maxStep=3;
-	if((errCode=buildMonolithicPoints(dataIn,progress,totalDataSize,callback,p)))
+	if((errCode=buildMonolithicPoints(dataIn,progress,totalDataSize,p)))
 		return errCode;
 
 	progress.step=2;
 	progress.stepName=TRANS("Build");
 	progress.filterProgress=0;
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	BoundCube treeDomain;
 	treeDomain.setBounds(p);
 
 	//Build the tree (its roughly nlogn timing, but worst case n^2)
 	K3DTree kdTree;
-	kdTree.setCallbackMethod(callback);
-	kdTree.setProgressPointer(&(progress.filterProgress));
-	
 	kdTree.buildByRef(p);
 
 
-	//Update progress & User interface by calling callback
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	//Update progress 
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 	p.clear(); //We don't need pts any more, as tree *is* a copy.
 
 
-	//Its algorithim time!
+	//Its algorithm time!
 	//----
 	//Update progress stuff
 	size_t n=0;
 	progress.step=3;
 	progress.stepName=TRANS("Analyse");
 	progress.filterProgress=0;
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	//List of points for which there was a failure
 	//first entry is the point Id, second is the 
@@ -2678,7 +2845,7 @@ size_t SpatialAnalysisFilter::algorithmDensityFilter(ProgressData &progress,
 						{	
 							float maxSqrRad;
 
-							//Get the radius as the furtherst object
+							//Get the radius as the furthest object
 							maxSqrRad= (res[res.size()-1]->sqrDist(r));
 
 
@@ -2700,14 +2867,14 @@ size_t SpatialAnalysisFilter::algorithmDensityFilter(ProgressData &progress,
 
 						res.clear();
 						
-						//Update callback as needed
+						//Update progress as needed
 						if(!curProg--)
 						{
 							#pragma omp critical 
 							{
 							n+=NUM_CALLBACK/(nnMax);
 							progress.filterProgress= (unsigned int)(((float)n/(float)totalDataSize)*100.0f);
-							if(!(*callback)(false))
+							if(*Filter::wantAbort)
 								spin=true;
 							curProg=NUM_CALLBACK/(nnMax);
 							}
@@ -2762,13 +2929,13 @@ size_t SpatialAnalysisFilter::algorithmDensityFilter(ProgressData &progress,
 							numInRad++;
 							//Advance ever so slightly beyond the next ion
 							deadDistSqr = res->sqrDist(r)+std::numeric_limits<float>::epsilon();
-							//Update callback as needed
+							//Update progress as needed
 							if(!curProg--)
 							{
 #pragma omp critical
 								{
 								progress.filterProgress= (unsigned int)((float)n/(float)totalDataSize*100.0f);
-								if(!(*callback)(false))
+								if(*Filter::wantAbort)
 								{
 #ifdef _OPENMP
 									spin=true;
@@ -2904,7 +3071,7 @@ void SpatialAnalysisFilter::createCylinder(DrawStreamData * &drawData,
 	dC->setRadius(scalarParams[0]);
 	dC->setColour(0.5,0.5,0.5,0.3);
 	dC->setSlices(40);
-	dC->setLength(sqrt(vectorParams[1].sqrMag())*2.0f);
+	dC->setLength(sqrtf(vectorParams[1].sqrMag())*2.0f);
 	dC->setDirection(vectorParams[1]);
 	dC->wantsLight=true;
 	drawData->drawables.push_back(dC);
@@ -2959,8 +3126,7 @@ void SpatialAnalysisFilter::createCylinder(DrawStreamData * &drawData,
 }
 
 size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress, 
-		bool (*callback)(bool), size_t totalDataSize, 
-		const vector<const FilterStreamData *>  &dataIn, 
+		size_t totalDataSize, const vector<const FilterStreamData *>  &dataIn, 
 		vector<const FilterStreamData * > &getOut,const RangeFile *rngF)
 {
 	//Need bins to perform histogram
@@ -2971,17 +3137,16 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 	progress.filterProgress=0;
 	progress.maxStep=4;
 
-	bool wantAbort=false;
-
 	//Ions inside the selected cylinder,
 	// which are to be used as source points for dist. function query
 	vector<IonHit> ionsInside;
 	{
 		//Crop out a cylinder as the source data 
-		CropHelper cropHelp(callback,&progress.filterProgress,
-				totalDataSize, CROP_CYLINDER_INSIDE,
+		CropHelper cropHelp(totalDataSize, CROP_CYLINDER_INSIDE_AXIAL,
 				vectorParams,scalarParams);
-				
+		
+		float minProg,maxProg;
+		size_t cumulativeCount=0;		
 		//Run cropping over the input datastreams
 		for(size_t ui=0; ui<dataIn.size();ui++)
 		{
@@ -2990,7 +3155,12 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 				const IonStreamData* d;
 				d=(const IonStreamData *)dataIn[ui];
 				size_t errCode;
-				errCode=cropHelp.runFilter(d->data,ionsInside);
+				minProg=cumulativeCount/(float)totalDataSize;
+				cumulativeCount+=d->data.size();
+				maxProg=cumulativeCount/(float)totalDataSize;
+
+				errCode=cropHelp.runFilter(d->data,ionsInside,
+					minProg,maxProg,progress.filterProgress);
 			
 				if(errCode == ERR_CROP_INSUFFICIENT_MEM)
 					return INSUFFICIENT_SIZE_ERR;
@@ -2999,21 +3169,15 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 					//If we fail, abort, but we should use
 					// the appropriate error code
 					ASSERT(errCode == ERR_CROP_CALLBACK_FAIL);
-					wantAbort=true;
 					break;
 				}
 			}
 
-			if(!(*callback)(false))
-			{
-				wantAbort=true;
-				break;
-			}
 		
 		}
 	}
 
-	if(wantAbort)
+	if(*Filter::wantAbort)
 		return ERR_ABORT_FAIL;
 
 	//Now, the ions outside the targeting volume may be reduced 
@@ -3045,6 +3209,7 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 	progress.stepName=TRANS("Reduce");
 	progress.filterProgress=0;
 
+	//TODO: Improve progress
 	switch(stopMode)
 	{
 		case STOP_MODE_RADIUS:
@@ -3064,12 +3229,11 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 			vP[1].extend(distMax);
 
 			//Crop out a cylinder as the source data 
-			CropHelper cropHelp(callback,&progress.filterProgress,
-					totalDataSize, CROP_CYLINDER_INSIDE,
+			CropHelper cropHelp(totalDataSize, CROP_CYLINDER_INSIDE_AXIAL,
 					vP,sP);
 
 			vector<IonHit> tmp;
-			size_t errCode=cropHelp.runFilter(ionsOutside,tmp);
+			size_t errCode=cropHelp.runFilter(ionsOutside,tmp,0,100,progress.filterProgress);
 
 			switch(errCode)
 			{
@@ -3145,11 +3309,10 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 	ionsOutside.clear();
 
 	K3DTree tree;
-	tree.setProgressPointer(&progress.filterProgress);
-	tree.setCallbackMethod(callback);
+
 	tree.buildByRef(dest);
-	if(!(*callback)(true))
-		return ERR_ABORT_FAIL;
+	if(*Filter::wantAbort)
+		return FILTER_ERR_ABORT;
 
 	progress.step=4;
 	progress.stepName=TRANS("Compute");
@@ -3171,7 +3334,8 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 			axisNormal.normalise();
 
 			errCode=generate1DAxialNNHist(src,tree,axisNormal, histogram,
-					binWidth,nnMax,numBins,&progress.filterProgress,callback);
+					binWidth,nnMax,numBins,&progress.filterProgress,
+					*Filter::wantAbort);
 
 			break;
 		}
@@ -3181,7 +3345,7 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 			axisNormal.normalise();
 
 			errCode=generate1DAxialDistHist(src,tree,axisNormal, histogram,
-					distMax,numBins,&progress.filterProgress,callback);
+					distMax,numBins,&progress.filterProgress,*Filter::wantAbort);
 
 			histOK = (errCode ==0);
 			break;
@@ -3268,23 +3432,15 @@ size_t SpatialAnalysisFilter::algorithmAxialDf(ProgressData &progress,
 		}
 		
 	}
-	
-	//Create the user interaction device required for the user
-	// to interact with the algorithm parameters 
-	SelectionDevice *s;
-	DrawStreamData *d= new DrawStreamData;
-	d->parent=this;
-	d->cached=0;
 
-	createCylinder(d,s);
-	devices.push_back(s);
-	getOut.push_back(d);
+	//create the selection device for this algorithm
+	createDevice(getOut);	
+
 	return 0;
 }
 
 size_t SpatialAnalysisFilter::algorithmBinomial(ProgressData &progress, 
-		bool (*callback)(bool), size_t totalDataSize, 
-		const vector<const FilterStreamData *>  &dataIn, 
+		size_t totalDataSize, const vector<const FilterStreamData *>  &dataIn, 
 		vector<const FilterStreamData * > &getOut,const RangeFile *rngF)
 {
 	vector<IonHit> ions;
@@ -3295,13 +3451,12 @@ size_t SpatialAnalysisFilter::algorithmBinomial(ProgressData &progress,
 	progress.maxStep=2;
 
 	//Merge the ions form the incoming streams
-	Filter::collateIons(dataIn,ions,progress,callback,totalDataSize);
+	Filter::collateIons(dataIn,ions,progress,totalDataSize);
 
 	//Tell user we are on next step
 	progress.step++;
 	progress.stepName=TRANS("Binomial");
 	progress.filterProgress=0;
-	(*callback)(true);
 
 	size_t errCode;
 
@@ -3599,7 +3754,7 @@ bool densityPairTest()
 
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh OK");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh OK");
 	delete f;
 	//Kill the input ion stream
 	delete d; 
@@ -3615,7 +3770,7 @@ bool densityPairTest()
 	for(unsigned int ui=0;ui<2;ui++)
 	{
 		TEST( fabs( dOut->data[0].getMassToCharge()  - 1.0/(4.0/3.0*M_PI))
-			< sqrt(std::numeric_limits<float>::epsilon()),"NN density test");
+			< sqrtf(std::numeric_limits<float>::epsilon()),"NN density test");
 	}	
 
 
@@ -3657,12 +3812,14 @@ bool nnHistogramTest()
 	
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh OK");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh OK");
 	delete f;
 
 	streamIn.clear();
 
-	TEST(streamOut.size() == 1,"stream count");
+	TEST(streamOut.size() == 2,"stream count");
+	delete streamOut[1]; //wont use this
+	
 	TEST(streamOut[0]->getStreamType() == STREAM_TYPE_PLOT,"plot outputting");
 	const PlotStreamData* dPlot=(const PlotStreamData *)streamOut[0];
 
@@ -3713,7 +3870,7 @@ bool rdfPlotTest()
 	
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh OK");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh OK");
 	delete f;
 
 
@@ -3748,7 +3905,7 @@ bool axialDistTest()
 	vector<const FilterStreamData*> streamIn,streamOut;
 
 
-	//Create some inptu data
+	//Create some input data
 	//--
 	IonStreamData*d = new IonStreamData;
 	IonHit h;
@@ -3800,7 +3957,7 @@ bool axialDistTest()
 	TEST(f->setProperty(KEY_REMOVAL,"0",needUp),"Set prop (disable surface removal)");
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Checking refresh code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Checking refresh code");
 	delete f;
 	//Kill the input ion stream
 	delete d; 
@@ -3863,7 +4020,7 @@ bool replaceTest()
 	ProgressData p;
 	vector<const FilterStreamData*> streamIn,streamOut;
 	streamIn.push_back(d);
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh OK");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh OK");
 	delete f;
 	delete d;
 	streamIn.clear();
diff --git a/src/backend/filters/spatialAnalysis.h b/src/backend/filters/spatialAnalysis.h
index 42422fc..6963560 100644
--- a/src/backend/filters/spatialAnalysis.h
+++ b/src/backend/filters/spatialAnalysis.h
@@ -1,6 +1,6 @@
 /*
  *	spatialAnalysis.h - Perform various data analysis on 3D point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -60,6 +60,9 @@ class SpatialAnalysisFilter : public Filter
 		//!Change the NN histograms from counts to counts/nm
 		// - this allows comparing different binwidth histograms
 		bool normaliseNNHist;
+		
+		//!Do we want to display theoretical random NN distances on top?
+		bool wantRandomNNHist;
 		//--------
 		
 		//Density filtering specific params
@@ -72,9 +75,9 @@ class SpatialAnalysisFilter : public Filter
 		float densityCutoff; 
 
 		//!Vector paramaters for different primitives
-		vector<Point3D> vectorParams;
+		std::vector<Point3D> vectorParams;
 		//!Scalar paramaters for different primitives
-		vector<float> scalarParams;
+		std::vector<float> scalarParams;
 	
 		//Reset the scalar and vector parameters
 		// to their defaults, if the required parameters
@@ -112,7 +115,7 @@ class SpatialAnalysisFilter : public Filter
 		//Replace specific code
 		//---------
 		//file to use as other data source
-		string replaceFile;
+		std::string replaceFile;
 
 		//replacement operator mode
 		unsigned int replaceMode;
@@ -125,53 +128,59 @@ class SpatialAnalysisFilter : public Filter
 		//---------
 	
 		//Radial distribution function - creates a 1D histogram of spherical atom counts, centered around each atom
-		size_t algorithmRDF(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
-			const vector<const FilterStreamData *>  &dataIn, 
-			vector<const FilterStreamData * > &getOut,const RangeFile *rngF);
+		size_t algorithmRDF(ProgressData &progress, size_t totalDataSize, 
+			const std::vector<const FilterStreamData *>  &dataIn, 
+			std::vector<const FilterStreamData * > &getOut,const RangeFile *rngF);
 
 
 		//Local density function - places a sphere around each point to compute per-point density
-		size_t algorithmDensity(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
-			const vector<const FilterStreamData *>  &dataIn, 
-			vector<const FilterStreamData * > &getOut);
+		size_t algorithmDensity(ProgressData &progress, size_t totalDataSize, 
+			const std::vector<const FilterStreamData *>  &dataIn, 
+			std::vector<const FilterStreamData * > &getOut);
 		
 		//Density filter function - same as density function, but then drops points from output
 		// based upon their local density and some density cutoff data
-		size_t algorithmDensityFilter(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
-			const vector<const FilterStreamData *>  &dataIn, 
-			vector<const FilterStreamData * > &getOut);
+		size_t algorithmDensityFilter(ProgressData &progress, size_t totalDataSize, 
+			const std::vector<const FilterStreamData *>  &dataIn, 
+			std::vector<const FilterStreamData * > &getOut);
 
-		size_t algorithmAxialDf(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
-			const vector<const FilterStreamData *>  &dataIn, 
-			vector<const FilterStreamData * > &getOut,const RangeFile *rngF);
+		size_t algorithmAxialDf(ProgressData &progress, size_t totalDataSize, 
+			const std::vector<const FilterStreamData *>  &dataIn, 
+			std::vector<const FilterStreamData * > &getOut,const RangeFile *rngF);
 		
-		size_t algorithmBinomial(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
-			const vector<const FilterStreamData *>  &dataIn, 
-			vector<const FilterStreamData * > &getOut,const RangeFile *rngF);
+		size_t algorithmBinomial(ProgressData &progress, size_t totalDataSize, 
+			const std::vector<const FilterStreamData *>  &dataIn, 
+			std::vector<const FilterStreamData * > &getOut,const RangeFile *rngF);
 
-		size_t algorithmReplace(ProgressData &progress, bool (*callback)(bool), size_t totalDataSize, 
-			const vector<const FilterStreamData *>  &dataIn, 
-			vector<const FilterStreamData * > &getOut);
+		size_t algorithmReplace(ProgressData &progress, size_t totalDataSize, 
+			const std::vector<const FilterStreamData *>  &dataIn, 
+			std::vector<const FilterStreamData * > &getOut);
 
 		//Create a 3D manipulable cylinder as an output drawable
 		// using the parameters stored inside the vector/scalar params
 		// both parameters are outputs from this function
 		void createCylinder(DrawStreamData* &d, SelectionDevice * &s) const;
 
+
+		//Wrapper routeine to create the appropriate selection
+		// device for whatever algorithm is in use; device list will be appended to
+		// and if needed, output object will be generated 
+		void createDevice(std::vector<const FilterStreamData *> &getOut);
+
 		//Scan input datstreams to build a two point vectors,
 		// one of those with points specified as "target" 
 		// which is a copy of the input points
 		//Returns 0 on no error, otherwise nonzero
-		size_t buildSplitPoints(const vector<const FilterStreamData *> &dataIn,
+		size_t buildSplitPoints(const std::vector<const FilterStreamData *> &dataIn,
 					ProgressData &progress, size_t totalDataSize,
-					const RangeFile *rngF, bool (*callback)(bool),
-					vector<Point3D> &pSource, vector<Point3D> &pTarget) const;
+					const RangeFile *rngF,
+					std::vector<Point3D> &pSource, std::vector<Point3D> &pTarget) const;
 
 
 		//From the given input ions, filter them down using the user
 		// selection for ranges. If sourceFilter is true, filter by user
 		// source selection, otherwise by user target selection
-		void filterSelectedRanges(const vector<IonHit> &ions, bool sourceFilter, const RangeFile *rngF, vector<IonHit> &output) const;
+		void filterSelectedRanges(const std::vector<IonHit> &ions, bool sourceFilter, const RangeFile *rngF, std::vector<IonHit> &output) const;
 	public:
 		SpatialAnalysisFilter(); 
 		//!Duplicate filter contents, excluding cache.
@@ -188,7 +197,7 @@ class SpatialAnalysisFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 					std::vector<const FilterStreamData *> &getOut, 
-					ProgressData &progress, bool (*callback)(bool));
+					ProgressData &progress);
 		//!Get the type string  for this fitler
 		virtual std::string typeString() const { return std::string(TRANS("Spat. Analysis"));};
 
@@ -199,7 +208,7 @@ class SpatialAnalysisFilter : public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format,
diff --git a/src/backend/filters/spectrumPlot.cpp b/src/backend/filters/spectrumPlot.cpp
index 90f320a..7e4c8a9 100644
--- a/src/backend/filters/spectrumPlot.cpp
+++ b/src/backend/filters/spectrumPlot.cpp
@@ -1,6 +1,6 @@
 /*
  *	spectrumPlot.cpp - Compute histograms of values for valued 3D point data
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -16,11 +16,17 @@
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 #include "spectrumPlot.h"
+#include "algorithms/mass.h"
 
 #include "../plot.h"
 
 #include "filterCommon.h"
 
+using std::string;
+using std::vector;
+using std::pair;
+
+
 //!Error codes
 enum 
 {
@@ -30,6 +36,7 @@ enum
 	SPECTRUM_ERR_ENUM_END,
 };
 
+
 enum
 {
 	KEY_SPECTRUM_BINWIDTH,
@@ -37,8 +44,15 @@ enum
 	KEY_SPECTRUM_MIN,
 	KEY_SPECTRUM_MAX,
 	KEY_SPECTRUM_LOGARITHMIC,
+	KEY_SPECTRUM_NORMALISE,
+	KEY_SPECTRUM_NORMALISE_LOWERBOUND,
+	KEY_SPECTRUM_NORMALISE_UPPERBOUND,
 	KEY_SPECTRUM_PLOTTYPE,
-	KEY_SPECTRUM_COLOUR
+	KEY_SPECTRUM_COLOUR,
+	KEY_SPECTRUM_BACKMODE,
+	KEY_SPECTRUM_BACKMODE_FLAT_START,
+	KEY_SPECTRUM_BACKMODE_FLAT_END,
+	KEY_SPECTRUM_CORRECTED_ONLY
 };
 
 //Limit user to two :million: bins
@@ -46,6 +60,25 @@ const unsigned int SPECTRUM_MAX_BINS=2000000;
 
 const unsigned int SPECTRUM_AUTO_MAX_BINS=45000;
 
+
+//String to use on plot's y label
+const char *YLABEL_STRING=NTRANS("Count");
+
+enum
+{
+	NORMALISE_NONE,
+	NORMALISE_MAX,
+	NORMALISE_MAX_IN_BOUND,
+	NORMALISE_INTEGRAL_ONE,
+	NORMALISE_ENUM_END
+};
+
+const char *NORMALISE_STRING[] = {NTRANS("None"),
+				NTRANS("Maximum"),
+				NTRANS("Max in limit"),
+				NTRANS("Probability"),
+					};
+
 SpectrumPlotFilter::SpectrumPlotFilter()
 {
 	minPlot=0;
@@ -54,6 +87,12 @@ SpectrumPlotFilter::SpectrumPlotFilter()
 	binWidth=0.05;
 	plotStyle=0;
 	logarithmic=1;
+	fitMode=0;
+	massBackStart=1.2;
+	massBackEnd=1.9;
+	showOnlyCorrected=false;
+	normaliseMode=NORMALISE_NONE;
+	normaliseBounds=std::make_pair(0.0,100.0);
 
 	//Default to blue plot
 	rgba = ColourRGBAf(0,0,1.0f,1.0f);
@@ -70,9 +109,18 @@ Filter *SpectrumPlotFilter::cloneUncached() const
 	p->rgba=rgba;	
 	p->plotStyle=plotStyle;
 	p->logarithmic = logarithmic;
+	p->fitMode = fitMode;
+	p->massBackStart = massBackStart;
+	p->massBackEnd = massBackEnd;
+
+	p->normaliseMode=normaliseMode;
+	p->normaliseBounds=normaliseBounds;
 
+	p->showOnlyCorrected= showOnlyCorrected;
 
-	//We are copying wether to cache or not,
+
+
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -86,7 +134,7 @@ size_t SpectrumPlotFilter::numBytesForCache(size_t nObjects) const
 	//Check that we have good plot limits, and bin width. if not, we cannot estimate cache size
 	if(minPlot ==std::numeric_limits<float>::max() ||
 		maxPlot==-std::numeric_limits<float>::max()  || 
-		binWidth < sqrt(std::numeric_limits<float>::epsilon()))
+		binWidth < sqrtf(std::numeric_limits<float>::epsilon()))
 	{
 		return (size_t)(-1);
 	}
@@ -95,7 +143,7 @@ size_t SpectrumPlotFilter::numBytesForCache(size_t nObjects) const
 }
 
 unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 
 	if(cacheOK)
@@ -148,7 +196,7 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 							n+=NUM_CALLBACK;
 							progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
 							curProg=NUM_CALLBACK;
-							if(!(*callback)(false))
+							if(*Filter::wantAbort)
 								return SPECTRUM_ABORT_FAIL;
 						}
 					}
@@ -180,7 +228,7 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 		if(minPlot ==std::numeric_limits<float>::max() || 
 			minPlot ==-std::numeric_limits<float>::max() || 
 			fabs(delta) >  std::numeric_limits<float>::max() || // Check for out-of-range
-			 binWidth < sqrt(std::numeric_limits<float>::epsilon())	)
+			 binWidth < sqrtf(std::numeric_limits<float>::epsilon())	)
 		{
 			//If not, then simply set it to some defaults.
 			minPlot=0; maxPlot=1.0; binWidth=0.1;
@@ -234,7 +282,7 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 	d->index=0;
 	d->parent=this;
 	d->dataLabel = getUserString();
-	d->yLabel= TRANS("Count");
+	d->yLabel=TRANS(YLABEL_STRING);
 
 	//Check all the incoming ion data's type name
 	//and if it is all the same, use it for the plot X-axis
@@ -341,7 +389,7 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 					unsigned int bin;
 					bin = (unsigned int)((ions->data[uj].getMassToCharge()-minPlot)/binWidth);
 					//Dependant upon the bounds,
-					//actual data could be anywhere.
+					//actual data could be anywhere. >=0 is implicit
 					if( bin < d->xyData.size())
 						d->xyData[bin].second++;
 
@@ -351,7 +399,7 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 						n+=NUM_CALLBACK;
 						progress.filterProgress= (unsigned int)(((float)(n)/((float)totalSize))*100.0f);
 						curProg=NUM_CALLBACK;
-						if(!(*callback)(false))
+						if(*Filter::wantAbort)
 						{
 							delete d;
 							return SPECTRUM_ABORT_FAIL;
@@ -368,6 +416,95 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 
 	}
 
+	if(fitMode!= FIT_MODE_NONE)
+	{
+		BACKGROUND_PARAMS backParams;
+		backParams.massStart=massBackStart;
+		backParams.massEnd=massBackEnd;
+		backParams.binWidth=binWidth;
+		backParams.mode=fitMode;
+
+	
+		//fit a constant tof (1/sqrt (mass)) type background
+		if(doFitBackground(dataIn,backParams))
+		{
+			//display a warning that the background failed
+			consoleOutput.push_back(TRANS("Background fit failed - input data was considered ill formed (gauss-test)"));
+		}
+		else 
+		{
+			if (!showOnlyCorrected)
+			{
+				//Create a new plot which shows the spectrum's background
+				PlotStreamData *plotBack = new PlotStreamData;
+				plotBack->parent = this;
+				plotBack->dataLabel=string(TRANS("Background:")) + d->dataLabel;	
+				plotBack->plotMode=d->plotMode;
+				plotBack->xLabel=d->xLabel;	
+				plotBack->index=d->index+1;	
+				plotBack->yLabel=d->yLabel;
+				plotBack->xyData.reserve(d->xyData.size());
+				for(size_t ui=0;ui<d->xyData.size(); ui++)
+				{
+					//negative sqrt cannot does not work. Equation only valid for positive masses
+					if(d->xyData[ui].first <=0)
+						continue;
+		
+					plotBack->xyData.push_back( std::make_pair(d->xyData[ui].first,
+							backParams.intensity/(sqrtf(d->xyData[ui].first))));
+			
+				}
+
+				cacheAsNeeded(plotBack);
+
+				getOut.push_back(plotBack);
+			}
+			else
+			{
+				//Correct the mass spectrum that we display
+				for(size_t ui=0;ui<d->xyData.size(); ui++)
+				{
+					//negative sqrt cannot does not work. Equation only valid for positive masses
+					if(d->xyData[ui].first <=0)
+					{
+						d->xyData[ui].second =0;
+						continue;
+					}
+	
+					d->xyData[ui].second-=backParams.intensity/sqrtf(d->xyData[ui].first);
+
+			
+				}
+				//prevent negative values in log mode
+				if(logarithmic)
+				{
+					for(size_t ui=0;ui<d->xyData.size(); ui++)
+						d->xyData[ui].second=std::max(0.0f,d->xyData[ui].second);
+				}
+				
+			}
+		}
+	}
+
+	if(normaliseMode != NORMALISE_NONE)
+	{
+		normalise(d->xyData);
+		switch(normaliseMode)
+		{
+			case NORMALISE_MAX:
+			case NORMALISE_MAX_IN_BOUND:
+				d->yLabel=TRANS("Relative ") + d->yLabel;
+				break;
+			case NORMALISE_INTEGRAL_ONE:
+				d->yLabel=TRANS("Probability Density"); 
+				
+				break;	
+
+			default:
+				ASSERT(false);
+
+		}
+	}	
 	cacheAsNeeded(d);
 	
 	getOut.push_back(d);
@@ -375,6 +512,55 @@ unsigned int SpectrumPlotFilter::refresh(const std::vector<const FilterStreamDat
 	return 0;
 }
 
+
+void SpectrumPlotFilter::normalise(vector<pair<float,float> > &xyData) const
+{
+	float scaleFact=0;
+	switch(normaliseMode)
+	{
+		case NORMALISE_NONE:
+			return;
+		case NORMALISE_MAX:
+			for(size_t ui=0;ui<xyData.size();ui++)
+			{
+				scaleFact=std::max(xyData[ui].second,scaleFact);
+			}
+			break;
+		case NORMALISE_MAX_IN_BOUND:
+			for(size_t ui=0;ui<xyData.size();ui++)
+			{
+				if(xyData[ui].first < normaliseBounds.second &&
+					xyData[ui].first >=normaliseBounds.first)
+					scaleFact=std::max(xyData[ui].second,scaleFact);
+			}
+			break;
+		case NORMALISE_INTEGRAL_ONE:
+		{
+			#pragma omp parallel for reduction(+:scaleFact)
+			for(size_t ui=0;ui<xyData.size();ui++)
+				scaleFact+=xyData[ui].second;
+			float binDelta=1.0;
+			if(xyData.size() > 1)
+				binDelta=xyData[1].first - xyData[0].first;
+			scaleFact*=binDelta;
+			break;
+		}
+			
+		default:
+			ASSERT(false);
+	}
+
+	if(scaleFact > 0 )
+	{
+		#pragma omp parallel for
+		for(size_t ui=0;ui<xyData.size();ui++)
+		{
+			xyData[ui].second/=scaleFact;
+		}
+	}
+}
+
+
 void SpectrumPlotFilter::getProperties(FilterPropGroup &propertyList) const
 {
 
@@ -426,20 +612,50 @@ void SpectrumPlotFilter::getProperties(FilterPropGroup &propertyList) const
 	p.helpText=TRANS("Convert the plot to logarithmic mode");
 	propertyList.addProperty(p,curGroup);
 
-	//Let the user know what the valid values for plot type are
 	vector<pair<unsigned int,string> > choices;
+	string tmpStr;
+	for(unsigned int ui=0;ui<NORMALISE_ENUM_END;ui++)
+	{	
+		tmpStr=TRANS(NORMALISE_STRING[ui]);
+		choices.push_back(make_pair( ui,tmpStr));
+	}
+	
+	tmpStr= choiceString(choices,normaliseMode);
+	p.name=TRANS("Normalisation");
+	p.data=tmpStr;
+	p.type=PROPERTY_TYPE_CHOICE;
+	p.helpText=TRANS("Rescale the plot height, to make inter-spectrum comparisons easier");
+	p.key=KEY_SPECTRUM_NORMALISE;
+	propertyList.addProperty(p,curGroup);
 
 
-	string tmpStr;
-	tmpStr=plotString(PLOT_LINE_LINES);
-	choices.push_back(make_pair((unsigned int) PLOT_LINE_LINES,tmpStr));
-	tmpStr=plotString(PLOT_LINE_BARS);
-	choices.push_back(make_pair((unsigned int)PLOT_LINE_BARS,tmpStr));
-	tmpStr=plotString(PLOT_LINE_STEPS);
-	choices.push_back(make_pair((unsigned int)PLOT_LINE_STEPS,tmpStr));
-	tmpStr=plotString(PLOT_LINE_STEM);
-	choices.push_back(make_pair((unsigned int)PLOT_LINE_STEM,tmpStr));
+	if(normaliseMode == NORMALISE_MAX_IN_BOUND)
+	{
+		p.name=TRANS("Lower Bound");
+		stream_cast(tmpStr,normaliseBounds.first);
+		p.data=tmpStr;
+		p.type=PROPERTY_TYPE_REAL;
+		p.helpText=TRANS("Do not use data below this x-value for normalisation");
+		p.key=KEY_SPECTRUM_NORMALISE_LOWERBOUND;
+		propertyList.addProperty(p,curGroup);
+
+		p.name=TRANS("Upper Bound");
+		stream_cast(tmpStr,normaliseBounds.second);
+		p.data=tmpStr;
+		p.type=PROPERTY_TYPE_REAL;
+		p.helpText=TRANS("Do not use data above this x-value for normalisation");
+		p.key=KEY_SPECTRUM_NORMALISE_UPPERBOUND;
+		propertyList.addProperty(p,curGroup);
 
+	}
+	
+	//Let the user know what the valid values for plot type are
+	choices.clear();
+	for(unsigned int ui=PLOT_LINE_LINES; ui<PLOT_LINE_STEM+1;ui++)
+	{
+		tmpStr=plotString(ui);
+		choices.push_back(make_pair( ui,tmpStr));
+	}
 
 	tmpStr= choiceString(choices,plotStyle);
 	p.name=TRANS("Plot Type");
@@ -457,6 +673,63 @@ void SpectrumPlotFilter::getProperties(FilterPropGroup &propertyList) const
 	propertyList.addProperty(p,curGroup);
 
 	propertyList.setGroupTitle(curGroup,TRANS("Appearance"));
+
+	/* FIXME: This has been removed owing to broken unit test. 
+	curGroup++;
+
+	choices.clear();
+	for(unsigned int ui=0;ui<FIT_MODE_ENUM_END; ui++)
+	{
+		tmpStr=TRANS(BACKGROUND_MODE_STRING[ui]);
+		choices.push_back(make_pair(ui,tmpStr));
+	}
+	
+	p.name=TRANS("Model");
+	p.key=KEY_SPECTRUM_BACKMODE;
+	p.type=PROPERTY_TYPE_CHOICE;
+	p.helpText=TRANS("Fitting method to use");
+	p.data=choiceString(choices,fitMode);
+	propertyList.addProperty(p,curGroup);
+
+
+	switch(fitMode)
+	{
+		case FIT_MODE_NONE:
+			break;
+		case FIT_MODE_CONST_TOF:
+			//Add start/end TOF 
+			p.name=TRANS("Fit Start");
+			p.helpText=TRANS("Start mass value for fitting background");
+			p.type=PROPERTY_TYPE_REAL;
+			p.key=KEY_SPECTRUM_BACKMODE_FLAT_START;
+			stream_cast(p.data,massBackStart);
+			propertyList.addProperty(p,curGroup);
+
+			p.name=TRANS("Fit End");
+			p.helpText=TRANS("End mass value for fitting background");
+			p.type=PROPERTY_TYPE_REAL;
+			p.key=KEY_SPECTRUM_BACKMODE_FLAT_END;
+			stream_cast(p.data,massBackEnd);
+
+			propertyList.addProperty(p,curGroup);
+			break;
+		default:
+			ASSERT(false);
+	}
+
+	if(fitMode != FIT_MODE_NONE)
+	{
+		p.name=TRANS("Corr. Only");
+		p.helpText=TRANS("Only show corrected spectrum, not fit");
+		p.key=KEY_SPECTRUM_CORRECTED_ONLY;
+		p.type=PROPERTY_TYPE_BOOL;
+		p.data=boolStrEnc(showOnlyCorrected);
+		propertyList.addProperty(p,curGroup);
+
+	}
+
+	propertyList.setGroupTitle(curGroup,TRANS("Background Mode"));*/	
+	
 }
 
 bool SpectrumPlotFilter::setProperty( unsigned int key, 
@@ -564,7 +837,7 @@ bool SpectrumPlotFilter::setProperty( unsigned int key,
 				//we cached, in order to avoid recomputation
 				for(size_t ui=0;ui<filterOutputs.size();ui++)
 				{
-					if(filterOutputs[ui]->getStreamType() == STREAM_TYPE_PLOT)
+					if(filterOutputs[ui]->getStreamType() == STREAM_TYPE_PLOT )
 					{
 						PlotStreamData *p;
 						p =(PlotStreamData*)filterOutputs[ui];
@@ -608,6 +881,10 @@ bool SpectrumPlotFilter::setProperty( unsigned int key,
 				}
 
 			}
+			else
+			{
+				clearCache();
+			}
 
 			break;
 		}
@@ -638,6 +915,115 @@ bool SpectrumPlotFilter::setProperty( unsigned int key,
 				}
 
 			}
+			else
+				clearCache();
+			break;
+		}
+		case KEY_SPECTRUM_BACKMODE:
+		{
+			unsigned int newMode;
+			for(size_t ui=0;ui<FIT_MODE_ENUM_END; ui++)
+			{
+				if(string(BACKGROUND_MODE_STRING[ui]) == string(value))
+				{
+					newMode=ui;
+					break;
+				}
+			}
+
+			ASSERT(newMode <FIT_MODE_ENUM_END)
+			
+			fitMode=newMode;
+			cacheOK=false;
+			needUpdate=true;
+			clearCache();
+			
+			break;
+		}
+	
+		case KEY_SPECTRUM_BACKMODE_FLAT_START:
+		{
+			float tmpStart;
+			if(stream_cast(tmpStart,value))
+				return false;
+			if( tmpStart>=massBackEnd)
+				return false;
+
+			if(!applyPropertyNow(massBackStart,value,needUpdate))
+				return false;
+			break;
+		}
+		case KEY_SPECTRUM_BACKMODE_FLAT_END:
+		{
+			float tmpEnd;
+			if(stream_cast(tmpEnd,value))
+				return false;
+			if( tmpEnd<=massBackStart)
+				return false;
+
+			if(!applyPropertyNow(massBackEnd,value,needUpdate))
+				return false;
+			break;
+		}
+		case KEY_SPECTRUM_CORRECTED_ONLY:
+		{
+			if(!applyPropertyNow(showOnlyCorrected,value,needUpdate))
+				return false;
+			break;
+		}
+		case KEY_SPECTRUM_NORMALISE:
+		{
+			unsigned int newMode;
+			for(size_t ui=0;ui<NORMALISE_ENUM_END; ui++)
+			{
+				if(string(NORMALISE_STRING[ui]) == string(value))
+				{
+					newMode=ui;
+					break;
+				}
+			}
+			ASSERT(newMode <NORMALISE_ENUM_END)
+		
+			//TODO: Cache introspection?	
+			normaliseMode=newMode;
+			cacheOK=false;
+			needUpdate=true;
+			clearCache();
+			break;
+
+		}
+		case KEY_SPECTRUM_NORMALISE_LOWERBOUND:
+		{
+			float tmpVal;
+			if(stream_cast(tmpVal,value))
+				return false;
+			if( tmpVal>=normaliseBounds.second)
+				return false;
+
+			if(!applyPropertyNow(normaliseBounds.first,value,needUpdate))
+				return false;
+			
+		
+			//TODO: Cache introspection?	
+			normaliseBounds.first=tmpVal;
+			cacheOK=false;
+			break;
+		}
+		case KEY_SPECTRUM_NORMALISE_UPPERBOUND:
+		{
+			float tmpVal;
+			if(stream_cast(tmpVal,value))
+				return false;
+			if( tmpVal<=normaliseBounds.first)
+				return false;
+
+			if(!applyPropertyNow(normaliseBounds.second,value,needUpdate))
+				return false;
+			
+		
+			//TODO: Cache introspection?	
+			normaliseBounds.second=tmpVal;
+			cacheOK=false;
 			break;
 		}
 		default:
@@ -661,7 +1047,7 @@ void SpectrumPlotFilter::setUserString(const std::string &s)
 }
 
 
-std::string  SpectrumPlotFilter::getErrString(unsigned int code) const
+std::string  SpectrumPlotFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[] = {
 		"",
@@ -674,6 +1060,8 @@ std::string  SpectrumPlotFilter::getErrString(unsigned int code) const
 	return errStrs[code];
 }
 
+
+
 void SpectrumPlotFilter::setPropFromBinding(const SelectionBinding &b)
 {
 	ASSERT(false);
@@ -699,6 +1087,13 @@ bool SpectrumPlotFilter::writeState(std::ostream &f,unsigned int format, unsigne
 			f << tabs(depth+1) << "<logarithmic value=\"" << logarithmic<< "\"/>" << endl;
 
 			f << tabs(depth+1) << "<plottype value=\"" << plotStyle<< "\"/>" << endl;
+			f << tabs(depth+1) << "<background mode=\"" << fitMode << "\">" << endl;
+				f << tabs(depth+2) << "<fitwindow start=\"" << massBackStart<< "\" end=\"" << massBackEnd << "\"/>" << endl;
+				f << tabs(depth+2) << "<showonlycorrected value=\"" << boolStrEnc(showOnlyCorrected)  <<  "\"/>" << endl;
+
+
+			f << tabs(depth+1) << "</background>" << endl;
+			f << tabs(depth+1) << "<normalise mode=\"" << normaliseMode << "\" lowbound=\"" << normaliseBounds.first << "\" highbound=\"" << normaliseBounds.second << "\"/>" << endl;
 			
 			f << tabs(depth) << "</" << trueName() <<  ">" << endl;
 			break;
@@ -724,6 +1119,7 @@ bool SpectrumPlotFilter::readState(xmlNodePtr &nodePtr, const std::string &state
 	xmlChar *xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
 	if(!xmlString)
 		return false;
+
 	userString=(char *)xmlString;
 	xmlFree(xmlString);
 	//===
@@ -812,6 +1208,63 @@ bool SpectrumPlotFilter::readState(xmlNodePtr &nodePtr, const std::string &state
 	       return false;	
 	//====
 
+	//Retrieve background fitting mode, if we have it
+	// Only available 3Depict >= 0.0.18
+	// internal rev > 5e7b66e5ce3d
+	xmlNodePtr  tmpNode=nodePtr;
+	if(!XMLHelpFwdToElem(nodePtr,"background"))
+	{
+		if(XMLHelpGetProp(fitMode,nodePtr,"mode"))
+			return false;
+
+		if(!nodePtr->xmlChildrenNode)
+			return false;
+			
+		tmpNode=nodePtr;
+		nodePtr=nodePtr->xmlChildrenNode;
+		
+		if(!XMLGetNextElemAttrib(nodePtr,massBackStart,"fitwindow","start"))
+			return false;
+
+		if(XMLHelpGetProp(massBackEnd,nodePtr,"end"))
+			return false;
+		
+		if(!XMLGetNextElemAttrib(nodePtr,showOnlyCorrected,"showonlycorrected","value"))
+			return false;
+		nodePtr=tmpNode;
+	}
+	else
+	{
+		nodePtr=tmpNode;
+	}
+	
+	// Only available 3Depict >= 0.0.18
+	// internal rev > 73289623683a tip
+	if(!XMLHelpFwdToElem(nodePtr,"normalise"))
+	{
+		if(XMLHelpGetProp(normaliseMode,nodePtr,"mode"))
+			return false;
+
+		float tmpLow,tmpHigh;		
+		if(XMLHelpGetProp(tmpLow,nodePtr,"lowbound"))
+			return false;
+	
+		if(XMLHelpGetProp(tmpHigh,nodePtr,"highbound"))
+			return false;
+
+		if(tmpLow >=tmpHigh)
+			return false;
+
+		normaliseBounds.first=tmpLow;
+		normaliseBounds.second=tmpHigh;
+	}
+	else
+	{
+		//initialise to some default value
+		normaliseBounds=std::make_pair(0,100);
+		normaliseMode=NORMALISE_NONE;
+	}
+
 	return true;
 }
 
@@ -831,6 +1284,11 @@ unsigned int SpectrumPlotFilter::getRefreshUseMask() const
 	return STREAM_TYPE_IONS;
 }
 
+bool SpectrumPlotFilter::needsUnrangedData() const
+{
+	return fitMode == FIT_MODE_CONST_TOF;
+}
+
 #ifdef DEBUG
 #include <memory>
 
@@ -852,6 +1310,7 @@ IonStreamData *synDataPoints(const unsigned int span[], unsigned int numPts)
 
 bool countTest()
 {
+	using std::auto_ptr;
 	auto_ptr<IonStreamData> d;
 	const unsigned int VOL[]={
 				10,10,10
@@ -877,7 +1336,7 @@ bool countTest()
 	//Do the refresh
 	ProgressData p;
 	f->setCaching(false);
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 	delete f;
 	
 	TEST(streamOut.size() == 1,"stream count");
@@ -888,10 +1347,10 @@ bool countTest()
 
 	
 	//Check the plot colour is what we want.
-	TEST(fabs(plot->r -1.0f) < sqrt(std::numeric_limits<float>::epsilon()),"colour (r)");
+	TEST(fabs(plot->r -1.0f) < sqrtf(std::numeric_limits<float>::epsilon()),"colour (r)");
 	TEST(plot->g< 
-		sqrt(std::numeric_limits<float>::epsilon()),"colour (g)");
-	TEST(plot->b < sqrt(std::numeric_limits<float>::epsilon()),"colour (b)");
+		sqrtf(std::numeric_limits<float>::epsilon()),"colour (g)");
+	TEST(plot->b < sqrtf(std::numeric_limits<float>::epsilon()),"colour (b)");
 
 	//Count the number of ions in the plot, and check that it is equal to the number of ions we put in.
 	float sumV=0;
diff --git a/src/backend/filters/spectrumPlot.h b/src/backend/filters/spectrumPlot.h
index a4c207d..83b4391 100644
--- a/src/backend/filters/spectrumPlot.h
+++ b/src/backend/filters/spectrumPlot.h
@@ -1,6 +1,6 @@
 /*
  *	spectrumPlot.h - Compute histograms of values for valued 3D point data
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -25,16 +25,39 @@
 class SpectrumPlotFilter : public Filter
 {
 	private:
+		//minimum and maximum plot bounds
 		float minPlot,maxPlot;
+		//step size to use for histogram binning
 		float binWidth;
+		//automatically determine plot limits?
 		bool autoExtrema;
+		//plots should be sown in log mode?
 		bool logarithmic;
+	
+		//perform fitting	
+		unsigned int fitMode;
+		// when fitting, only show the corrected spectrum, rather than fit itself 
+		bool showOnlyCorrected;
+
+		//start/end of mass values to use when fitting mass spectrum
+		float massBackStart, massBackEnd;
 
 
 		//Vector of spectra. Each spectra is comprised of a sorted Y data
 		std::vector< std::vector<float > > spectraCache;
 		ColourRGBAf rgba;
 		unsigned int plotStyle;
+
+		//!Normalisation mode for scaling plot intensity
+		unsigned int normaliseMode;
+
+		//!Lower and upper bound for normalisation of spectrum.
+		// used to "crop" spectra when searching for normalisation
+		std::pair<float,float> normaliseBounds;
+
+
+		void normalise(std::vector<std::pair<float,float> > &spectrumData) const;
+
 	public:
 		SpectrumPlotFilter();
 		//!Duplicate filter contents, excluding cache.
@@ -48,7 +71,7 @@ class SpectrumPlotFilter : public Filter
 		//!update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 			std::vector<const FilterStreamData *> &getOut, 
-			ProgressData &progress, bool (*callback)(bool));
+			ProgressData &progress);
 		
 		virtual std::string typeString() const { return std::string(TRANS("Spectrum"));};
 
@@ -59,7 +82,7 @@ class SpectrumPlotFilter : public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 
 		//!Set the user string.
 		void setUserString(const std::string &s);
@@ -83,6 +106,9 @@ class SpectrumPlotFilter : public Filter
 		//!Set internal property value using a selection binding  (Disabled, this filter has no bindings)
 		void setPropFromBinding(const SelectionBinding &b)  ;
 
+		//!Does the filer need unranged data to operate?
+		bool needsUnrangedData() const;
+
 #ifdef DEBUG
 		bool runUnitTests() ;
 
diff --git a/src/backend/filters/transform.cpp b/src/backend/filters/transform.cpp
index 3da7e09..bcc6927 100644
--- a/src/backend/filters/transform.cpp
+++ b/src/backend/filters/transform.cpp
@@ -1,6 +1,6 @@
 /*
  *	transform.cpp - Perform geometrical transform operations on point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -19,7 +19,10 @@
 
 #include "filterCommon.h"
 
-
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
 
 
 enum
@@ -70,8 +73,8 @@ enum
 //!Error codes
 enum
 {
-	ERR_CALLBACK_FAIL=1,
-	ERR_NOMEM,
+	
+	ERR_NOMEM=1,
 	TRANSFORM_ERR_ENUM_END
 };
 
@@ -129,7 +132,7 @@ Filter *TransformFilter::cloneUncached() const
 	p->transformMode=transformMode;
 	p->showOrigin=showOrigin;
 	p->noiseType=noiseType;
-	//We are copying wether to cache or not,
+	//We are copying whether to cache or not,
 	//not the cache itself
 	p->cache=cache;
 	p->cacheOK=false;
@@ -185,7 +188,7 @@ DrawStreamData* TransformFilter::makeMarkerSphere(SelectionDevice* &s) const
 }
 
 unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-	std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+	std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	//use the cached copy if we have it.
 	if(cacheOK)
@@ -301,8 +304,8 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 
 	if( transformMode != MODE_VALUE_SHUFFLE)
 	{
-		//Dont cross the streams. Why? It would be bad.
-		//  - Im fuzzy on the whole good-bad thing, what do you mean bad?"
+		//Don't cross the streams. Why? It would be bad.
+		//  - I'm fuzzy on the whole good-bad thing, what do you mean bad?"
 		//  - Every ion in the data body can be operated on independently.
 		//
 		//  OK, important safety tip.
@@ -346,19 +349,19 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							d->valueType=src->valueType;
 
 							ASSERT(src->data.size() <= totalSize);
-							unsigned int curProg=NUM_CALLBACK;
 #ifdef _OPENMP
+							unsigned int curProg=PROGRESS_REDUCE;
 							//Parallel version
 							bool spin=false;
-							#pragma omp parallel for shared(spin)
+							#pragma omp parallel for shared(spin,n)
 							for(unsigned int ui=0;ui<src->data.size();ui++)
 							{
-								unsigned int thisT=omp_get_thread_num();
 								if(spin)
 									continue;
 
 								if(!curProg--)
 								{
+									unsigned int thisT=omp_get_thread_num();
 									#pragma omp critical
 									{
 									n+=NUM_CALLBACK;
@@ -368,7 +371,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 
 									if(thisT == 0)
 									{
-										if(!(*callback)(false))
+										if(*Filter::wantAbort)
 											spin=true;
 									}
 								}
@@ -381,7 +384,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							if(spin)
 							{			
 								delete d;
-								return ERR_CALLBACK_FAIL;
+								return FILTER_ERR_ABORT;
 							}
 
 #else
@@ -394,17 +397,12 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 								//set the position for the given ion
 								d->data[pos].setPos((it->getPosRef() - origin)*scaleFactor+origin);
 								d->data[pos].setMassToCharge(it->getMassToCharge());
-								//update progress every CALLBACK ions
-								if(!curProg--)
+								//update progress 
+								progress.filterProgress= (unsigned int)((float)(n++)/((float)totalSize)*100.0f);
+								if(*Filter::wantAbort)
 								{
-									n+=NUM_CALLBACK;
-									progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-									if(!(*callback)(false))
-									{
-										delete d;
-										return ERR_CALLBACK_FAIL;
-									}
-									curProg=NUM_CALLBACK;
+									delete d;
+									return FILTER_ERR_ABORT;
 								}
 								pos++;
 							}
@@ -460,8 +458,8 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							d->valueType=src->valueType;
 
 							ASSERT(src->data.size() <= totalSize);
-							unsigned int curProg=NUM_CALLBACK;
 #ifdef _OPENMP
+							unsigned int curProg=PROGRESS_REDUCE;
 							//Parallel version
 							bool spin=false;
 							#pragma omp parallel for shared(spin)
@@ -482,7 +480,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 
 									if(thisT == 0)
 									{
-										if(!(*callback)(false))
+										if(*Filter::wantAbort)
 											spin=true;
 									}
 								}
@@ -495,7 +493,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							if(spin)
 							{			
 								delete d;
-								return ERR_CALLBACK_FAIL;
+								return FILTER_ERR_ABORT;
 							}
 
 #else
@@ -508,17 +506,11 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 								//set the position for the given ion
 								d->data[pos].setPos((it->getPosRef() - origin)*transformVec+origin);
 								d->data[pos].setMassToCharge(it->getMassToCharge());
-								//update progress every CALLBACK ions
-								if(!curProg--)
+								progress.filterProgress= (unsigned int)((float)(n++)/((float)totalSize)*100.0f);
+								if(*Filter::wantAbort)
 								{
-									n+=NUM_CALLBACK;
-									progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-									if(!(*callback)(false))
-									{
-										delete d;
-										return ERR_CALLBACK_FAIL;
-									}
-									curProg=NUM_CALLBACK;
+									delete d;
+									return FILTER_ERR_ABORT;
 								}
 								pos++;
 							}
@@ -576,14 +568,13 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							d->valueType=src->valueType;
 							
 							ASSERT(src->data.size() <= totalSize);
-							unsigned int curProg=NUM_CALLBACK;
 #ifdef _OPENMP
 							//Parallel version
+							unsigned int curProg=PROGRESS_REDUCE;
 							bool spin=false;
 #pragma omp parallel for shared(spin)
 							for(unsigned int ui=0;ui<src->data.size();ui++)
 							{
-								unsigned int thisT=omp_get_thread_num();
 								if(spin)
 									continue;
 								
@@ -591,14 +582,14 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 								{
 #pragma omp critical
 									{
-										n+=NUM_CALLBACK;
+										n+=PROGRESS_REDUCE;
 										progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
 									}
 									
 									
-									if(thisT == 0)
+									if(omp_get_thread_num() == 0)
 									{
-										if(!(*callback)(false))
+										if(*Filter::wantAbort)
 											spin=true;
 									}
 								}
@@ -611,7 +602,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							if(spin)
 							{			
 								delete d;
-								return ERR_CALLBACK_FAIL;
+								return FILTER_ERR_ABORT;
 							}
 							
 #else
@@ -625,16 +616,11 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 								d->data[pos].setPos((it->getPosRef() - origin));
 								d->data[pos].setMassToCharge(it->getMassToCharge());
 								//update progress every CALLBACK ions
-								if(!curProg--)
+								progress.filterProgress= (unsigned int)((float)(n++)/((float)totalSize)*100.0f);
+								if(*Filter::wantAbort)
 								{
-									n+=NUM_CALLBACK;
-									progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-									if(!(*callback)(false))
-									{
-										delete d;
-										return ERR_CALLBACK_FAIL;
-									}
-									curProg=NUM_CALLBACK;
+									delete d;
+									return FILTER_ERR_ABORT;
 								}
 								pos++;
 							}
@@ -709,7 +695,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 									
 									if(thisT == 0)
 									{
-										if(!(*callback)(false))
+										if(*Filter::wantAbort)
 											spin=true;
 									}
 								}
@@ -722,7 +708,7 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 							if(spin)
 							{			
 								delete d;
-								return ERR_CALLBACK_FAIL;
+								return FILTER_ERR_ABORT;
 							}
 							
 #else
@@ -740,10 +726,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 								{
 									n+=NUM_CALLBACK;
 									progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-									if(!(*callback)(false))
+									if(*Filter::wantAbort)
 									{
 										delete d;
-										return ERR_CALLBACK_FAIL;
+										return FILTER_ERR_ABORT;
 									}
 									curProg=NUM_CALLBACK;
 								}
@@ -835,10 +821,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 								{
 									n+=NUM_CALLBACK;
 									progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
-									if(!(*callback)(false))
+									if(*Filter::wantAbort)
 									{
 										delete d;
-										return ERR_CALLBACK_FAIL;
+										return FILTER_ERR_ABORT;
 									}
 									curProg=NUM_CALLBACK;
 								}
@@ -926,10 +912,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 										{
 											curProg=NUM_CALLBACK;
 											progress.filterProgress= (unsigned int)((float)(ui)/((float)totalSize)*100.0f);
-											if(!(*callback)(false))
+											if(*Filter::wantAbort)
 											{
 												delete d;
-												return ERR_CALLBACK_FAIL;
+												return FILTER_ERR_ABORT;
 											}
 										}
 									}
@@ -956,10 +942,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 										{
 											curProg=NUM_CALLBACK;
 											progress.filterProgress= (unsigned int)((float)(ui)/((float)totalSize)*100.0f);
-											if(!(*callback)(false))
+											if(*Filter::wantAbort)
 											{
 												delete d;
-												return ERR_CALLBACK_FAIL;
+												return FILTER_ERR_ABORT;
 											}
 										}
 									}
@@ -990,8 +976,8 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 		progress.filterProgress=0;
 		progress.stepName=TRANS("Collate");
 		progress.maxStep=3;
-		if(!(*callback)(true))
-			return ERR_CALLBACK_FAIL;
+		if(*Filter::wantAbort)
+			return FILTER_ERR_ABORT;
 		//we have to cross the streams (I thought that was bad?) 
 		//  - Each dataset is no longer independent, and needs to
 		//  be mixed with the other datasets. Bugger; sounds mem. expensive.
@@ -1043,10 +1029,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 						d->data[uj+curPos].setPos(src->data[uj].getPos());
 					}
 				
-					if(!(*callback)(true))
+					if(*Filter::wantAbort)
 					{
 						delete d;
-						return ERR_CALLBACK_FAIL;
+						return FILTER_ERR_ABORT;
 					}
 
 					curPos+=src->data.size();
@@ -1062,10 +1048,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 		progress.step=2;
 		progress.filterProgress=0;
 		progress.stepName=TRANS("Shuffle");
-		if(!(*callback)(true))
+		if(*Filter::wantAbort)
 		{
 			delete d;
-			return ERR_CALLBACK_FAIL;
+			return FILTER_ERR_ABORT;
 		}
 		//Shuffle the value data.TODO: callback functor	
 
@@ -1077,19 +1063,19 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 		r.seed(time(0));
 		std::shuffle(massData.begin(),massData.end(),r);
 #endif
-		if(!(*callback)(true))
+		if(*Filter::wantAbort)
 		{
 			delete d;
-			return ERR_CALLBACK_FAIL;
+			return FILTER_ERR_ABORT;
 		}
 
 		progress.step=3;
 		progress.filterProgress=0;
 		progress.stepName=TRANS("Splice");
-		if(!(*callback)(true))
+		if(*Filter::wantAbort)
 		{
 			delete d;
-			return ERR_CALLBACK_FAIL;
+			return FILTER_ERR_ABORT;
 		}
 		
 		
@@ -1100,10 +1086,10 @@ unsigned int TransformFilter::refresh(const std::vector<const FilterStreamData *
 		for(size_t uj=0;uj<totalSize;uj++)
 			d->data[uj].setMassToCharge(massData[uj]);
 		
-		if(!(*callback)(true))
+		if(*Filter::wantAbort)
 		{
 			delete d;
-			return ERR_CALLBACK_FAIL;
+			return FILTER_ERR_ABORT;
 		}
 
 		massData.clear();
@@ -1396,7 +1382,7 @@ bool TransformFilter::setProperty(  unsigned int key,
 			break;
 		}
 		//The rotation angle, and the scale factor are both stored
-		//in scalaraparams[0]. All we need ot do is set that,
+		//in scalaraparams[0]. All we need to do is set that,
 		//as either can take any valid floating pt value
 		case KEY_ROTATE_ANGLE:
 		case KEY_SCALEFACTOR:
@@ -1486,10 +1472,9 @@ bool TransformFilter::setProperty(  unsigned int key,
 }
 
 
-std::string  TransformFilter::getErrString(unsigned int code) const
+std::string  TransformFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[] = { "",
-		"Aborted",//User aborted in a callback
 		"Unable to allocate memory"//Caught a memory issue,
 	};
 
@@ -1801,7 +1786,7 @@ bool rotateTest()
 	//OK, so now do the rotation
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 	delete f;
 
 	TEST(streamOut.size() == 1,"stream count");
@@ -1821,7 +1806,7 @@ bool rotateTest()
 
 
 	TEST((massCentre[0]-massCentre[1]).sqrMag() < 
-			2.0*sqrt(std::numeric_limits<float>::epsilon()),"mass centre invariance");
+			2.0*sqrtf(std::numeric_limits<float>::epsilon()),"mass centre invariance");
 
 	//Rotating a sphere around its centre of mass
 	// should not massively change the bounding box
@@ -1883,7 +1868,7 @@ bool translateTest()
 	
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 	delete f;
 	
 	TEST(streamOut.size() == 1,"stream count");
@@ -1903,7 +1888,7 @@ bool translateTest()
 		{
 			float f;
 			f=bc[0].getBound(ui,uj) -bc[1].getBound(ui,uj);
-			TEST(fabs(f-offsetPt[ui]) < sqrt(std::numeric_limits<float>::epsilon()), "bound translation");
+			TEST(fabs(f-offsetPt[ui]) < sqrtf(std::numeric_limits<float>::epsilon()), "bound translation");
 		}
 	}
 
@@ -1963,7 +1948,7 @@ bool scaleTest()
 
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 	delete f;
 
 	TEST(streamOut.size() == 1,"stream count");
@@ -1983,7 +1968,7 @@ bool scaleTest()
 	float volumeDelta;
 	volumeDelta=fabs(bc[1].volume()/cubeOfScale - bc[0].volume() );
 
-	TEST(volumeDelta < 100.0f*sqrt(std::numeric_limits<float>::epsilon()), "scaled volume test");
+	TEST(volumeDelta < 100.0f*sqrtf(std::numeric_limits<float>::epsilon()), "scaled volume test");
 
 	delete streamOut[0];
 	delete d;
@@ -2036,7 +2021,7 @@ bool scaleAnisoTest()
 
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 	delete f;
 
 	TEST(streamOut.size() == 1,"stream count");
@@ -2078,7 +2063,7 @@ bool shuffleTest()
 	//OK, so now run the shuffle 
 	//Do the refresh
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"refresh error code");
 	delete f;
 
 	TEST(streamOut.size() == 1,"stream count");
diff --git a/src/backend/filters/transform.h b/src/backend/filters/transform.h
index f4abac4..4625147 100644
--- a/src/backend/filters/transform.h
+++ b/src/backend/filters/transform.h
@@ -1,6 +1,6 @@
 /*
  *	transform.h - Perform geometrical transform operations on point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -65,7 +65,7 @@ class TransformFilter : public Filter
 		//update filter
 		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 					std::vector<const FilterStreamData *> &getOut, 
-					ProgressData &progress, bool (*callback)(bool));
+					ProgressData &progress);
 		//!Force a re-read of the rangefile Return value is range file reading error code
 		unsigned int updateRng();
 		virtual std::string typeString() const { return std::string(TRANS("Ion. Transform"));};
@@ -77,7 +77,7 @@ class TransformFilter : public Filter
 		bool setProperty(unsigned int key, 
 				const std::string &value, bool &needUpdate);
 		//!Get the human readable error string associated with a particular error code during refresh(...)
-		std::string getErrString(unsigned int code) const;
+		std::string getSpecificErrString(unsigned int code) const;
 		
 		//!Dump state to output stream, using specified format
 		bool writeState(std::ostream &f,unsigned int format,
diff --git a/src/backend/filters/voxelise.cpp b/src/backend/filters/voxelise.cpp
index aaf7e55..adcf39b 100644
--- a/src/backend/filters/voxelise.cpp
+++ b/src/backend/filters/voxelise.cpp
@@ -1,6 +1,6 @@
 /*
  *	voxelise.cpp - Compute 3D binning (voxelisation) of point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -50,8 +50,8 @@ enum
 	KEY_VOXEL_SLICE_INTERP,
 	
 	KEY_FILTER_MODE,
-	KEY_FILTER_BOUNDARY_MODE,
-	KEY_FILTER_BINS,
+	KEY_FILTER_RATIO,
+	KEY_FILTER_STDEV,
 	KEY_ENABLE_NUMERATOR,
 	KEY_ENABLE_DENOMINATOR
 };
@@ -71,6 +71,7 @@ enum
 {
 	VOXELISE_FILTERTYPE_NONE,
 	VOXELISE_FILTERTYPE_GAUSS,
+	VOXELISE_FILTERTYPE_LAPLACE,
 	VOXELISE_FILTERTYPE_MAX // keep this at the end so it's a bookend for the last value
 };
 
@@ -121,12 +122,8 @@ const char *REPRESENTATION_TYPE_STRING[] = {
 
 const char *VOXELISE_FILTER_TYPE_STRING[]={
 	NTRANS("None"),
-	NTRANS("Gaussian (2𝜎)"),
-	};
-
-const char *VOXELISE_FILTER_BOUND_STRING[] ={
-	NTRANS("Zero"),
-	NTRANS("Bounce")
+	NTRANS("Gaussian (blur)"),
+	NTRANS("Lapl. of Gauss. (edges)"),
 	};
 
 const char *VOXELISE_SLICE_INTERP_STRING[]={
@@ -136,7 +133,7 @@ const char *VOXELISE_SLICE_INTERP_STRING[]={
 
 //This is not a member of voxels.h, as the voxels do not have any concept of the IonHit
 int countPoints(Voxels<float> &v, const std::vector<IonHit> &points, 
-				bool noWrap,bool (*callback)(bool))
+				bool noWrap)
 {
 
 	size_t x,y,z;
@@ -148,7 +145,7 @@ int countPoints(Voxels<float> &v, const std::vector<IonHit> &points,
 	{
 		if(!downSample--)
 		{
-			if(!(*callback)(false))
+			if(*Filter::wantAbort)
 				return 1;
 			downSample=MAX_CALLBACK;
 		}
@@ -160,6 +157,7 @@ int countPoints(Voxels<float> &v, const std::vector<IonHit> &points,
 				float value;
 				value=v.getData(x,y,z)+1.0f;
 
+				ASSERT(value >= 0.0f);
 				//Prevent wrap-around errors
 				if (noWrap) {
 					if (value > v.getData(x,y,z))
@@ -179,7 +177,6 @@ VoxeliseFilter::VoxeliseFilter()
 {
 	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(NORMALISE_TYPE_STRING) ==  VOXELISE_NORMALISETYPE_MAX);
 	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(VOXELISE_FILTER_TYPE_STRING) == VOXELISE_FILTERTYPE_MAX );
-	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(VOXELISE_FILTER_BOUND_STRING) ==  VOXELISE_FILTERBOUNDMODE_MAX);
 
 	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(REPRESENTATION_TYPE_STRING) == VOXEL_REPRESENT_END);
 	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(VOXEL_REPRESENT_KEEPCACHE) == VOXEL_REPRESENT_END);
@@ -188,9 +185,8 @@ VoxeliseFilter::VoxeliseFilter()
 	rgba=ColourRGBAf(0.5,0.5,0.5,0.9f);
 	isoLevel=0.5;
 	
-	filterBins=3;
+	filterRatio=3.0;
 	filterMode=VOXELISE_FILTERTYPE_NONE;
-	filterBoundaryMode=VOXELISE_FILTERBOUNDMODE_BOUNCE;
 	gaussDev=0.5;	
 	
 	representation=VOXEL_REPRESENT_POINTCLOUD;
@@ -213,7 +209,6 @@ VoxeliseFilter::VoxeliseFilter()
 	denominatorAll = true;
 
 	sliceInterpolate=SLICE_INTERP_NONE;
-	sliceBoundMode=BOUND_MIRROR;
 	sliceAxis=0;
 	sliceOffset=0.5;
 	showColourBar=false;
@@ -236,8 +231,7 @@ Filter *VoxeliseFilter::cloneUncached() const
 	p->isoLevel=isoLevel;
 	
 	p->filterMode=filterMode;
-	p->filterBoundaryMode=filterBoundaryMode;
-	p->filterBins=filterBins;
+	p->filterRatio=filterRatio;
 	p->gaussDev=gaussDev;
 
 	p->representation=representation;
@@ -279,7 +273,6 @@ Filter *VoxeliseFilter::cloneUncached() const
 	p->colourMapBounds[1] = colourMapBounds[1];
 
 	p->sliceInterpolate = sliceInterpolate;
-	p->sliceBoundMode= sliceBoundMode;
 	p->sliceAxis = sliceAxis;
 	p->sliceOffset = sliceOffset;
 
@@ -378,7 +371,7 @@ void VoxeliseFilter::initFilter(const std::vector<const FilterStreamData *> &dat
 }
 
 unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
-		  std::vector<const FilterStreamData *> &getOut, ProgressData &progress, bool (*callback)(bool))
+		  std::vector<const FilterStreamData *> &getOut, ProgressData &progress)
 {
 	//Disallow copying of anything in the blockmask. Copy everything else
 	propagateStreams(dataIn,getOut,getRefreshBlockMask(),true);
@@ -428,9 +421,8 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 		if(minP == maxP)
 			return 0;
 	
-		//Rebuild the voxels from the point dta
+		//Rebuild the voxels from the point data
 		Voxels<float> vsDenom;
-		voxelData.setCallbackMethod(callback);
 		voxelData.init(nBins[0], nBins[1], nBins[2], bc);
 		voxelData.fill(0);
 		
@@ -438,7 +430,6 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 			normaliseType == VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL) {
 			//Check we actually have incoming data
 			ASSERT(rsdIncoming);
-			vsDenom.setCallbackMethod(callback);
 			vsDenom.init(nBins[0], nBins[1], nBins[2], bc);
 			vsDenom.fill(0);
 		}
@@ -472,7 +463,7 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 
 					if(thisIonEnabled)
 					{
-						countPoints(voxelData,is->data,true,callback);
+						countPoints(voxelData,is->data,true);
 					}
 				}
 			
@@ -492,14 +483,14 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 							thisIonEnabled=false;
 
 						if(thisIonEnabled)
-							countPoints(vsDenom,is->data,true,callback);
+							countPoints(vsDenom,is->data,true);
 					}
 				} else if (normaliseType == VOXELISE_NORMALISETYPE_ALLATOMSINVOXEL)
 				{
-					countPoints(vsDenom,is->data,true,callback);
+					countPoints(vsDenom,is->data,true);
 				}
 
-				if(!(*callback)(false))
+				if(*Filter::wantAbort)
 					return VOXELISE_ABORT_ERR;
 			}
 		
@@ -520,9 +511,9 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 				{
 					is= (const IonStreamData *)dataIn[i];
 
-					countPoints(voxelData,is->data,true,callback);
+					countPoints(voxelData,is->data,true);
 					
-					if(!(*callback)(false))
+					if(*Filter::wantAbort)
 						return VOXELISE_ABORT_ERR;
 
 				}
@@ -542,41 +533,13 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 			case VOXELISE_FILTERTYPE_NONE:
 				break;
 			case VOXELISE_FILTERTYPE_GAUSS:
+			{	
+				voxelData.isotropicGaussianSmooth(gaussDev,filterRatio);
+				break;
+			}
+			case VOXELISE_FILTERTYPE_LAPLACE:
 			{
-				Voxels<float> kernel,res;
-
-				map<unsigned int, unsigned int> modeMap;
-
-
-				modeMap[VOXELISE_FILTERBOUNDMODE_ZERO]=BOUND_ZERO;
-				modeMap[VOXELISE_FILTERBOUNDMODE_BOUNCE]=BOUND_MIRROR;
-
-				//FIXME: This will be SLOW. need to use IIR or some other
-				//fast technique
-				
-				//Construct the gaussian convolution
-				kernel.setGaussianKernelCube(gaussDev,(float)filterBins,filterBins);
-				//Normalise the kernel
-				float sum;
-				sum=kernel.getSum();
-				kernel/=sum;
-
-				cerr << "Kernel (min/max):" << kernel.min() << "," << kernel.max() << endl;
-			
-				if(res.resize(voxelData))
-					return VOXELISE_MEMORY_ERR; 
-				
-
-				cerr << "Data:" << voxelData.min() << "," << voxelData.max() << endl;
-				//Gaussian kernel is separable (rank 1)
-				if(voxelData.convolve(kernel,res,BOUND_MIRROR))
-					return VOXELISE_CONVOLVE_ERR;
-				
-				cerr << "Result (min/max):" << res.min() << "," << res.max() << endl;
-
-				voxelData.swap(res);
-
-				res.clear();
+				voxelData.laplaceOfGaussian(gaussDev,filterRatio);
 				break;
 			}
 			default:
@@ -617,7 +580,7 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 		{
 			VoxelStreamData *vs = new VoxelStreamData();
 			vs->parent=this;
-			std::swap(vs->data,voxelData);
+			std::swap(*(vs->data),voxelData);
 			vs->representationType= representation;
 			vs->splatSize = splatSize;
 			vs->isoLevel=isoLevel;
@@ -651,6 +614,7 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 			getTexturedSlice(voxelData,sliceAxis,sliceOffset,
 						sliceInterpolate,minV,maxV,*dq);
 
+			dq->setColour(1,1,1,rgba.a());
 			dq->canSelect=true;
 
 
@@ -669,7 +633,7 @@ unsigned int VoxeliseFilter::refresh(const std::vector<const FilterStreamData *>
 			
 			
 			if(showColourBar)
-				d->drawables.push_back(makeColourBar(minV,maxV,255,colourMap,1.0f));
+				d->drawables.push_back(makeColourBar(minV,maxV,255,colourMap));
 			d->cached=0;
 			d->parent=this;
 			
@@ -735,12 +699,6 @@ std::string VoxeliseFilter::getFilterTypeString(int type)
 }
 
 
-std::string VoxeliseFilter::getFilterBoundTypeString(int type) 
-{
-	ASSERT(type < VOXELISE_FILTERBOUNDMODE_MAX);
-	return std::string(TRANS(VOXELISE_FILTER_BOUND_STRING[type]));
-}
-
 void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
 {
 	FilterProperty p;
@@ -827,7 +785,7 @@ void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
 	}
 	else
 	{
-		//prevent the case where we usd to have an incoing range stream, but now we dont.
+		//prevent the case where we used to have an incoming range stream, but now we don't.
 		// selected item within choice string must still be valid
 		if(normaliseType > VOXELISE_NORMALISETYPE_VOLUME)
 			defaultChoice= VOXELISE_NORMALISETYPE_NONE;
@@ -904,7 +862,6 @@ void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
 		curGroup++;
 	}
 
-/*
 	//Start a new set for filtering
 	//----
 	//TODO: Other filtering? threshold/median? laplacian? etc
@@ -917,6 +874,7 @@ void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
 		choices.push_back(make_pair(ui,tmpStr));
 	}
 	tmpStr= choiceString(choices,filterMode);
+	choices.clear();
 
 	p.name=TRANS("Filtering");
 	p.data=tmpStr;
@@ -930,33 +888,29 @@ void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
 	{
 
 		//Filter size
-		stream_cast(tmpStr,filterBins);
-		p.name=TRANS("Kernel Bins");
+		stream_cast(tmpStr,gaussDev);
+		p.name=TRANS("Standard Dev");
 		p.data=tmpStr;
-		p.key=KEY_FILTER_BINS;
-		p.type=PROPERTY_TYPE_INTEGER;
-		p.helpText=TRANS("Number of bins in convolution kernel");
+		p.key=KEY_FILTER_STDEV;
+		p.type=PROPERTY_TYPE_REAL;
+		p.helpText=TRANS("Filtering Scale");
 		propertyList.addProperty(p,curGroup);
-		//Boundary wrapping mode selection
-		choices.clear();
-		for(unsigned int ui=0;ui<VOXELISE_FILTERBOUNDMODE_MAX; ui++)
-		{
-			tmpStr=getFilterBoundTypeString(ui);
-			choices.push_back(make_pair(ui,tmpStr));
-		}
+
 		
-		tmpStr= choiceString(choices,filterBoundaryMode);
-		p.name=TRANS("Exterior values");
+		//Filter size
+		stream_cast(tmpStr,filterRatio);
+		p.name=TRANS("Kernel Size");
 		p.data=tmpStr;
-		p.type=PROPERTY_TYPE_CHOICE;
-		p.helpText=TRANS("Method to use to treat boundaries of voxel data for convolution");
-		p.key=KEY_FILTER_BOUNDARY_MODE;
+		p.key=KEY_FILTER_RATIO;
+		p.type=PROPERTY_TYPE_REAL;
+		p.helpText=TRANS("Filter radius, in multiples of std. dev. Larger -> slower, more accurate");
 		propertyList.addProperty(p,curGroup);
+
 	}
 	propertyList.setGroupTitle(curGroup,TRANS("Filtering"));
 	curGroup++;
 	//----
-*/
+
 	//start a new group for the visual representation
 	//----------------------------
 	choices.clear();
@@ -1096,6 +1050,14 @@ void VoxeliseFilter::getProperties(FilterPropGroup &propertyList) const
 			p.helpText=TRANS("Colour scheme used to assign points colours by value");
 			p.key=KEY_VOXEL_COLOURMODE;
 			propertyList.addProperty(p,curGroup);
+			
+			stream_cast(tmpStr,1.0-rgba.a());
+			p.name=TRANS("Transparency");
+			p.data=tmpStr;
+			p.type=PROPERTY_TYPE_REAL;
+			p.helpText=TRANS("How \"see through\" each facet is (0 - opaque, 1 - invisible)");
+			p.key=KEY_TRANSPARENCY;
+			propertyList.addProperty(p,curGroup);
 
 			tmpStr=boolStrEnc(showColourBar);
 			p.name=TRANS("Show Bar");
@@ -1373,35 +1335,34 @@ bool VoxeliseFilter::setProperty(unsigned int key,
 			}
 			break;
 		}
-		case KEY_FILTER_BOUNDARY_MODE:
+		case KEY_FILTER_RATIO:
 		{
-			 unsigned int i;
-			for (i = 0; i < VOXELISE_FILTERBOUNDMODE_MAX; i++)
-				if (value == getFilterBoundTypeString(i)) break;
-			if (i == VOXELISE_FILTERTYPE_MAX)
+			float i;
+			if(stream_cast(i,value))
 				return false;
-			
-			if(i != filterBoundaryMode)
+			//forbid negative sizes
+			if(i <= 0)
+				return false;
+			if(i != filterRatio)
 			{
-				filterBoundaryMode=i;
 				needUpdate=true;
+				filterRatio=i;
 				clearCache();
 			}
 			break;
 		}
-		case KEY_FILTER_BINS:
+		case KEY_FILTER_STDEV:
 		{
-			 unsigned int i;
+			float i;
 			if(stream_cast(i,value))
 				return false;
-
-			//FIXME: Min restriction is artificial and imposed due to incomplete separable convolution filter implementation
-			if(i == 0 || i > std::min(nBins[0],std::min(nBins[1],nBins[2])))
+			//forbid negative sizes
+			if(i <= 0)
 				return false;
-			if(i != filterBins)
+			if(i != gaussDev)
 			{
 				needUpdate=true;
-				filterBins=i;
+				gaussDev=i;
 				clearCache();
 			}
 			break;
@@ -1599,7 +1560,7 @@ bool VoxeliseFilter::setProperty(unsigned int key,
 	return true;
 }
 
-std::string  VoxeliseFilter::getErrString(unsigned int code) const
+std::string  VoxeliseFilter::getSpecificErrString(unsigned int code) const
 {
 	const char *errStrs[]={
 	 	"",
@@ -1945,7 +1906,7 @@ void VoxeliseFilter::getTexturedSlice(const Voxels<float> &v,
 
 	ASSERT(offset >=0 && offset <=1.0f);
 
-	v.getSlice(axis,offset,data,interpolateMode,sliceBoundMode);
+	v.getInterpSlice(axis,offset,data,interpolateMode);
 
 	if(autoColourMap)
 	{
@@ -2005,8 +1966,6 @@ void VoxeliseFilter::getTexturedSlice(const Voxels<float> &v,
 	texQ.setVertices(verts);
 	//--
 
-	//Upload the texture to the video card
-	texQ.rebindTexture();
 
 }
 
@@ -2046,7 +2005,7 @@ bool voxelSingleCountTest()
 	streamIn.push_back(ionData);
 
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 	delete f;
 
 	TEST(streamOut.size() == 1,"stream count");
@@ -2055,16 +2014,16 @@ bool voxelSingleCountTest()
 
 	const VoxelStreamData *v= (const VoxelStreamData*)streamOut[0];
 
-	TEST(v->data.max() <=numIons,
+	TEST(v->data->max() <=numIons,
 			"voxel max less than input stream")
 
-	TEST(v->data.min() >= 0.0f,"voxel counting minimum sanity");
+	TEST(v->data->min() >= 0.0f,"voxel counting minimum sanity");
 
 	
 	float dataSum;
-	sumVoxels(v->data,dataSum);
+	sumVoxels(*(v->data),dataSum);
 	TEST(fabs(dataSum - (float)numIons ) < 
-		sqrt(std::numeric_limits<float>::epsilon()),"voxel counting all input ions ");
+		sqrtf(std::numeric_limits<float>::epsilon()),"voxel counting all input ions ");
 
 	delete ionData;
 	delete streamOut[0];
@@ -2093,6 +2052,10 @@ bool voxelMultiCountTest()
 
 	RGBf col; col.red=col.green=col.blue=1.0f;
 
+	//create several input ion streams, each
+	//containing the above data, but with differeing
+	//mass to charge values.
+	// - we range this data though!
 	const unsigned int MAX_NUM_RANGES=2;
 	for(unsigned int ui=0;ui<MAX_NUM_RANGES;ui++)
 	{
@@ -2140,7 +2103,7 @@ bool voxelMultiCountTest()
 				"Set normalise mode");
 
 	ProgressData p;
-	TEST(!f->refresh(streamIn,streamOut,p,dummyCallback),"Refresh error code");
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
 	delete f;
 	for(unsigned int ui=0;ui<MAX_NUM_RANGES;ui++)
 		delete streamIn[ui];
@@ -2149,16 +2112,17 @@ bool voxelMultiCountTest()
 	
 	const VoxelStreamData *v= (const VoxelStreamData*)streamOut[1];
 
-	TEST(v->data.max() <=1.0f,
+	TEST(v->data->max() <=1.0f,
 			"voxel max less than input stream")
-	TEST(v->data.min() >= 0.0f,"voxel counting minimum sanity");
+	TEST(v->data->min() >= 0.0f,"voxel counting minimum sanity");
 
 
-	for(unsigned int ui=0;ui<v->data.getSize();ui++)
+	//all data should lie between 0 and 1
+	for(unsigned int ui=0;ui<v->data->getSize();ui++)
 	{
-		float delta;
-		delta=(v->data.getData(ui) - v->data.getData(0) );
-		ASSERT( v->data.getData(ui) == 0 || delta < std::numeric_limits<float>::epsilon());
+		float val;
+		val=v->data->getData(ui);
+		ASSERT(  val >= 0 && val <= 1.0f); 
 	}
 
 	delete v;
diff --git a/src/backend/filters/voxelise.h b/src/backend/filters/voxelise.h
index 25b6f24..ba96bbd 100644
--- a/src/backend/filters/voxelise.h
+++ b/src/backend/filters/voxelise.h
@@ -1,6 +1,6 @@
 /*
  *	voxelise.h - Compute 3D binning (voxelisation) of point clouds
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -18,6 +18,9 @@
 #ifndef VOXELISE_H
 #define VOXELISE_H
 #include "../filter.h"
+
+#include "common/voxels.h"
+
 #include "../../common/translation.h"
 
 //!Filter that does voxelisation for various primitives (copied from CompositionFilter)
@@ -56,8 +59,8 @@ private:
 	//!How do we treat boundaries when applying filters
 	unsigned int filterBoundaryMode;
 
-	//!Filter size
-	unsigned int filterBins;
+	//!Filter size, in units of gaussDevs 
+	float filterRatio;
 
 	//!Gaussian filter standard deviation
 	float gaussDev;
@@ -84,8 +87,6 @@ private:
 
 	//Interpolation mode to use when slicing	
 	size_t sliceInterpolate;
-	//Interpolation boundary handling mode
-	size_t sliceBoundMode;
 	//Axis that is normal to the slice 0,1,2 => x,y,z
 	size_t sliceAxis;
 	//Fractional offset from lower bound of data cube [0,1]
@@ -116,7 +117,7 @@ public:
 	//!update filter
 	unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
 						 std::vector<const FilterStreamData *> &getOut, 
-						 ProgressData &progress, bool (*callback)(bool));
+						 ProgressData &progress);
 	
 	virtual std::string typeString() const { return std::string(TRANS("Voxelisation"));};
 
@@ -136,7 +137,7 @@ public:
 	bool setProperty(unsigned int key, 
 					 const std::string &value, bool &needUpdate);
 	//!Get the human readable error string associated with a particular error code during refresh(...)
-	std::string getErrString(unsigned int code) const;
+	std::string getSpecificErrString(unsigned int code) const;
 	
 	//!Dump state to output stream, using specified format
 	bool writeState(std::ostream &f,unsigned int format, 
diff --git a/src/backend/filtertree.cpp b/src/backend/filtertree.cpp
index 34a3e8e..b2449c3 100644
--- a/src/backend/filtertree.cpp
+++ b/src/backend/filtertree.cpp
@@ -1,6 +1,6 @@
 /*
  * 	filtertree.cpp - Filter tree topology and data propagation handling
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -32,6 +32,15 @@ enum
 	CACHE_NEVER,
 };
 
+//Unlock helper class for toggling a  boolean value at exit
+class AutoUnlocker
+{
+	private:
+		bool *lockBool;
+	public:
+		AutoUnlocker(bool *b) { lockBool= b; *lockBool=true;}
+		~AutoUnlocker() { *lockBool=false;}
+};
 
 
 
@@ -68,7 +77,10 @@ class FilterRefreshCollector
 		void collectAll();
 		unsigned int getLevel() const { return nodes.size();}
 		void collectToLevel(unsigned int level);
-	
+
+#ifdef DEBUG
+		bool  isTracked(const FilterStreamData *ptr) const;
+#endif	
 };
 
 #ifdef DEBUG
@@ -93,6 +105,21 @@ void FilterRefreshCollector::checkSanity()
 	s.clear();
 
 }
+
+bool FilterRefreshCollector::isTracked(const FilterStreamData *ptr) const
+{
+	for(unsigned int ui=0;ui<nodes.size();ui++)
+	{
+		for(list<const FilterStreamData *>::const_iterator it=nodes[ui].begin();
+			it!=nodes[ui].end(); ++it)
+		{
+			if(*it == ptr)
+				return true;
+		}
+	}
+
+	return false;
+}
 #endif
 
 FilterRefreshCollector::~FilterRefreshCollector()
@@ -208,6 +235,7 @@ FilterTree::FilterTree()
 {
 	maxCachePercent=DEFAULT_MAX_CACHE_PERCENT;
 	cacheStrategy=CACHE_DEPTH_FIRST;
+	amRefreshing=false;
 }
 
 FilterTree::~FilterTree()
@@ -224,6 +252,8 @@ FilterTree::FilterTree(const FilterTree &orig) :
 	for(tree<Filter *>::pre_order_iterator it=filters.begin();
 		it!=filters.end();++it)
 		(*it)=(*it)->cloneUncached();
+	
+	amRefreshing=false;
 }
 
 size_t FilterTree::depth(const tree<Filter*>::pre_order_iterator &it) const
@@ -241,6 +271,7 @@ void FilterTree::swap(FilterTree &other)
 
 const FilterTree &FilterTree::operator=(const FilterTree &orig)
 {
+	ASSERT(!amRefreshing)
 	clear();
 
 	cacheStrategy=orig.cacheStrategy;
@@ -548,11 +579,62 @@ void FilterTree::getFilterRefreshStarts(vector<tree<Filter *>::iterator > &propS
 
 }
 
+void FilterTree::getConsoleMessagesToNodes(std::vector<tree<Filter *>::iterator > &nodes,
+			std::vector<pair<const Filter*,string> > &messages) const
+{
+
+
+	//obtain a unique list of all filters who are parents of the nodes
+	if(!nodes.size())
+		return;
+
+	std::set<Filter*> filterSet;
+	for(unsigned int ui=0;ui<nodes.size();ui++)
+	{
+		tree<Filter*>::iterator it;
+		it=nodes[ui];
+
+		if(it==filters.begin())
+			filterSet.insert(*it);
+		
+		while(filters.is_valid(it) && 
+			filters.parent(it)!= filters.begin())
+		{
+			filterSet.insert(*it);	
+			it=filters.parent(it);
+		}
+	}
+
+	filterSet.insert(*(filters.begin()));
+
+
+	//now loop through the filters and obtain the console messages
+	for(set<Filter *>::iterator it=filterSet.begin();it!=filterSet.end();it++)
+	{
+		vector<string> tmpMsgs;
+		(*it)->getConsoleStrings(tmpMsgs);
+
+		for(size_t ui=0;ui<tmpMsgs.size();ui++)
+		{
+			messages.push_back(make_pair(*it,tmpMsgs[ui]));
+		}
+	}
+}
+
 unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData, 
 		std::vector<SelectionDevice *> &devices,
 		vector<pair<const Filter* , string> > &consoleMessages,
-	       	ProgressData &curProg, bool (*callback)(bool)) const
+	       	ProgressData &curProg, ATOMIC_BOOL &abortRefresh) const
 {
+
+	//initially, we should not want to abort refreshing 
+	ASSERT(!abortRefresh);
+	//Tell the filter system about our abort flag
+	Filter::wantAbort=&abortRefresh;
+
+
+	//Lock the refresh state.
+	AutoUnlocker unlocker(&amRefreshing);
 	
 	unsigned int errCode=0;
 
@@ -608,7 +690,6 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 	curProg.totalNumFilters=countChildFilters(filters,baseTreeNodes)+baseTreeNodes.size();
 
 
-	(*callback)(false);
 
 	for(unsigned int itPos=0;itPos<baseTreeNodes.size(); itPos++)
 	{
@@ -633,7 +714,7 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 			//Step 0 : Pop the cache until we reach our current level, 
 			//	delete any pointers that would otherwise be lost.
 			//	Recall that the zero size in the stack may not correspond to the
-			//	tree root, but rather corresponds to our insertion level
+			//	tree root, but rather corresponds to the filter we started refreshing from
 			//---
 			size_t popLevel;
 			popLevel=filters.depth(filtIt) - filters.depth(baseTreeNodes[itPos])+1;
@@ -675,7 +756,7 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 							ramFreeForUse= maxCachePercent/(float)100.0f*getAvailRAM();
 
 							bool cache;
-							cache=(cacheBytes/(1024*1024) ) < ramFreeForUse;
+							cache=((float)cacheBytes/(1024*1024) ) < ramFreeForUse;
 
 							currentFilter->setCaching( cache);
 							break;
@@ -693,7 +774,6 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 			//	This is the guts of the system.
 			//---
 			//
-			(*callback)(false);
 
 			if(!currentFilter->haveCache())
 				currentFilter->clearConsole();
@@ -702,11 +782,7 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 			try
 			{
 
-				errCode=currentFilter->refresh(inDataStack.top(),
-							curData,curProg,callback);
-
-				//error codes above this value are reserved
-				ASSERT(errCode <=FILTERTREE_REFRESH_ERR_BEGIN);
+				errCode=currentFilter->refresh(inDataStack.top(),curData,curProg);
 			}
 			catch(std::bad_alloc)
 			{
@@ -719,11 +795,15 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 			//Perform sanity checks on filter output
 			checkRefreshValidity(curData,currentFilter);
 			ASSERT(curProg.step == curProg.maxStep || errCode);
+			//when completing, we should have full progress 
+			std::string progWarn = std::string("Progress did not reach 100\% for filter: ");
+			progWarn+=currentFilter->getUserString();
+			
+			WARN( (curProg.filterProgress == 100 || errCode),progWarn.c_str());
 #endif
 			//Ensure that (1) yield is called, regardless of what filter does
 			//(2) yield is called after 100% update	
 			curProg.filterProgress=100;	
-			(*callback)(false);
 
 
 			vector<SelectionDevice *> curDevices;
@@ -745,30 +825,40 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 				consoleMessages.push_back(make_pair(currentFilter,tmpMessages[ui]));
 
 			//check for any error in filter update (including user abort)
-			if(errCode)
+			if(errCode || abortRefresh)
 			{
 				//clear any intermediary pointers
 				popPointerStack(inDataStack,0);
 				ASSERT(inDataStack.empty());
-				//Clean up the output that we didn't use
+				
+				//remove duplicates, as more than one output data may
+				// output the same pointer
+				std::set<const FilterStreamData *> uniqSet;
 				for(list<FILTER_OUTPUT_DATA>::iterator it=outData.begin();
 						it!=outData.end();++it)
 				{
 					for(size_t ui=0;ui<it->second.size();ui++)
-					{
-						const FilterStreamData *data;
-						data = it->second[ui];
+						uniqSet.insert(it->second[ui]);
+				}
 
-						//Output data is uncached - delete it
-						if(!data->cached)
-							delete data;
-					}
+	
+				//Clean up the output that we didn't use
+				for(std::set<const FilterStreamData *>::iterator it=uniqSet.begin();
+					it!=uniqSet.end(); ++it)
+				{
+					const FilterStreamData *data;
+					data = *it;
+					//Output data is uncached - it is our job to delete it
+					if(!data->cached)
+						delete data;
 				}
+				if(abortRefresh)
+					return FILTER_ERR_ABORT;
 				return errCode;
 			}
 
 
-			//Update the filter output statistics, eg num objects of each type output 
+			//Update the filter output statistics, e.g. num objects of each type output 
 			currentFilter->updateOutputInfo(curData);
 			
 			
@@ -809,19 +899,19 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 	// minimises unnecessary output)
 	//Construct a single list of all pointers in output,
 	//checking for uniqueness. Delete duplicates
-	list<const FilterStreamData *> flatList;
 	
 	for(list<FILTER_OUTPUT_DATA>::iterator it=outData.begin();it!=outData.end(); )
 	{
-		vector<const FilterStreamData *>::iterator itJ;
+		std::set<const FilterStreamData *> uniqueSet;
 
+		vector<const FilterStreamData *>::iterator itJ;
 		itJ=it->second.begin();
 		while(itJ!=it->second.end()) 
 		{
 			//Each stream data pointer should only occur once in the entire lot.
-			if(find(flatList.begin(),flatList.end(),*itJ) == flatList.end())
+			if(uniqueSet.find(*itJ) == uniqueSet.end())
 			{
-				flatList.push_back(*itJ);
+				uniqueSet.insert(*itJ);
 				++itJ;
 			}
 			else
@@ -835,10 +925,6 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 		else
 			++it;
 	}
-
-	//outData List is complete, and contains only unique entries. clear the checking list.
-	flatList.clear();
-
 	//======
 
 
@@ -848,7 +934,7 @@ unsigned int FilterTree::refreshFilterTree(list<FILTER_OUTPUT_DATA > &outData,
 
 string FilterTree::getRefreshErrString(unsigned int code)
 {
-	ASSERT(code >FILTERTREE_REFRESH_ERR_BEGIN && code < FILTERTREE_REFRESH_ERR_ENUM_END);
+	
 	const char *REFRESH_ERR_STRINGS[] = {"",
 		"Insufficient memory for refresh",
 		};
@@ -1016,7 +1102,7 @@ unsigned int FilterTree::loadXML(const xmlNodePtr &treeParent, std::ostream &err
 		newFilt=0;
 		nodeUnderstood=true; //assume by default we understand, and set false if not
 
-		//If we encounter a "children" node. then we need to look at the children of this filter
+		//If we encounter a "children" node. Then we need to look at the children of this filter
 		if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"children"))
 		{
 			//Can't have children without parent
@@ -1658,7 +1744,7 @@ void FilterTree::removeSubtree(Filter *removeFilt)
 	initFilterTree();
 }
 
-void FilterTree::cloneSubtree(FilterTree &f,Filter *targetFilt) const
+void FilterTree::cloneSubtree(FilterTree &f,const Filter *targetFilt) const
 {
 	ASSERT(!f.filters.size()); //Should only be passing empty trees
 	
diff --git a/src/backend/filtertree.h b/src/backend/filtertree.h
index ca57afd..1d1be15 100644
--- a/src/backend/filtertree.h
+++ b/src/backend/filtertree.h
@@ -1,6 +1,6 @@
 /*
  * 	filtertree.h - Filter tree topology and data propagation handling
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -23,9 +23,10 @@
 #include "filter.h"
 
 #include <map>
+#include <string>
 #include <utility>
 
-typedef std::pair<Filter *,vector<const FilterStreamData * > > FILTER_OUTPUT_DATA;
+typedef std::pair<Filter *,std::vector<const FilterStreamData * > > FILTER_OUTPUT_DATA;
 
 
 
@@ -44,8 +45,9 @@ enum
 class FilterTree
 {
 	private:
+		//!Are we currently refreshing?
+		mutable bool amRefreshing;
 
-		
 		//!Caching strategy
 		unsigned int cacheStrategy;
 		
@@ -58,13 +60,15 @@ class FilterTree
 			
 		//!Get the filter refresh seed points in tree, by examination of tree caches, block/emit of filters
 		//and tree topology
-		void getFilterRefreshStarts(vector<tree<Filter *>::iterator > &propStarts) const;
-		
-			
+		void getFilterRefreshStarts(std::vector<tree<Filter *>::iterator > &propStarts) const;
+	
+		//!Obtain the tree nodes up until (but excluding) these nodes
+		void getConsoleMessagesToNodes(std::vector<tree<Filter *>::iterator> &nodes, 
+				std::vector<std::pair< const Filter*,std::string> > &messages) const;	
 		
 #ifdef DEBUG
 		//!Check that the output of filter refresh functions
-		void checkRefreshValidity(const vector< const FilterStreamData *> &curData, 
+		void checkRefreshValidity(const std::vector< const FilterStreamData *> &curData, 
 					const Filter *refreshFilter) const;
 #endif
 		
@@ -79,7 +83,7 @@ class FilterTree
 
 
 		static size_t countChildFilters(const tree<Filter *> &treeInst,
-					const vector<tree<Filter *>::iterator> &nodes);
+					const std::vector<tree<Filter *>::iterator> &nodes);
 	public:
 		FilterTree();
 		~FilterTree();
@@ -118,15 +122,16 @@ class FilterTree
 
 		bool setFilterProperty(Filter *f, unsigned int key,
 				const std::string &value, bool &needUpdate);
+
 		
 		//!Refresh the entire filter tree. Whilst this is public, great care must be taken in
 		// deleting the filterstream data correctly. To do this, use the "safeDeleteFilterList" function.
 		unsigned int refreshFilterTree(	
 			std::list<FILTER_OUTPUT_DATA> &outData,
-			std::vector<SelectionDevice *> &devices,std::vector<std::pair<const Filter *,string> > &consoleMessages,
-						ProgressData &curProg, bool (*callback)(bool)) const;
+			std::vector<SelectionDevice *> &devices,std::vector<std::pair<const Filter *,std::string> > &consoleMessages,
+						ProgressData &curProg, ATOMIC_BOOL &abortRefresh) const;
 
-		static string getRefreshErrString(unsigned int errCode);
+		static std::string getRefreshErrString(unsigned int errCode);
 		
 		//!Safely delete data generated by refreshFilterTree(...). 
 		//a mask can be used to *prevent* STREAM_TYPE_blah from being deleted. Deleted items are removed from the list.
@@ -138,7 +143,10 @@ class FilterTree
 		//  can be emitted from each filter. It is not possible to
 		//  emit types not in the mask
 		// For blocking, give the types that cannot reach the tree output (leaf exit)
-		void getAccumulatedPropagationMaps(map<Filter*, size_t> &emitTypes, map<Filter*,size_t> &blockTypes) const;
+		void getAccumulatedPropagationMaps(std::map<Filter*, size_t> &emitTypes, std::map<Filter*,size_t> &blockTypes) const;
+
+		bool isRefreshing() const { return amRefreshing;}
+
 		//---
 
 
@@ -148,14 +156,14 @@ class FilterTree
 
 
 		//Write out the filters into their XML representation
-		bool saveXML(std::ofstream &f, std::map<string,string> &fileMapping,
+		bool saveXML(std::ofstream &f, std::map<std::string,std::string> &fileMapping,
 					bool writePackage, bool useRelativePaths, unsigned int minTabDepth=0) const;
 
 
 		//!Convert tree to a series of flat strings representing the topology
 		//TODO: COnvert to bimap
-		void serialiseToStringPaths(std::map<const Filter *,string > &serialisedPaths) const;
-		void serialiseToStringPaths(std::map<string,const Filter *> &serialisedPaths) const;
+		void serialiseToStringPaths(std::map<const Filter *,std::string > &serialisedPaths) const;
+		void serialiseToStringPaths(std::map<std::string,const Filter *> &serialisedPaths) const;
 
 
 		//Topological alteration  & examination functions
@@ -175,8 +183,9 @@ class FilterTree
 		//!Duplicate a branch of the tree to a new position. Do not copy cache,
 		bool copyFilter(Filter *id, const Filter *newParent);
 
-		//Obtain a copy of the filters in the specified subtree 
-		void cloneSubtree(FilterTree &f,Filter *targetFilt) const;
+		//!Obtain a copy of the filters from the specified subtree,
+		// including the target filter and its descendants
+		void cloneSubtree(FilterTree &f,const Filter *targetFilt) const;
 		//---------
 	
 
@@ -213,8 +222,6 @@ class FilterTree
 		void purgeCache();
 	
 
-		//!Returns true if any of the filters (incl. stash)
-		//return a state override (i.e. refer to external entities, such as files)
 		bool hasStateOverrides() const ;
 
 		bool hasUpdates() const ;
@@ -225,7 +232,7 @@ class FilterTree
 		
 		//Overwrite the contents of the pointed-to range files with
 		// the map contents
-		void modifyRangeFiles(const map<const RangeFile *, const RangeFile *> &toModify);
+		void modifyRangeFiles(const std::map<const RangeFile *, const RangeFile *> &toModify);
 
 
 		size_t cacheCount(unsigned int typeMask = STREAMTYPE_MASK_ALL) const;
diff --git a/src/backend/filtertreeAnalyse.cpp b/src/backend/filtertreeAnalyse.cpp
index 7500c2b..fcbda53 100644
--- a/src/backend/filtertreeAnalyse.cpp
+++ b/src/backend/filtertreeAnalyse.cpp
@@ -1,6 +1,6 @@
 /*
  *	filtertreeAnalyse.cpp - Performs correctness checking of filter trees
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -132,6 +132,24 @@ bool needsRangeParent(const Filter *f)
 
 }
 
+unsigned int needsUnrangedData(const Filter *f)
+{
+	if(f->getType() == FILTER_TYPE_IONINFO)
+	{
+		const IonInfoFilter *fInfo = (const IonInfoFilter *)f;
+
+		return fInfo->needsUnrangedData();
+	}
+
+	if(f->getType() == FILTER_TYPE_SPECTRUMPLOT)
+	{
+		const SpectrumPlotFilter *fSp = (const SpectrumPlotFilter *)f;
+
+		return fSp->needsUnrangedData(); 
+	}
+	return false;
+}
+
 void FilterTreeAnalyse::getAnalysisResults(std::vector<FILTERTREE_ERR> &errs) const
 {
 	errs.resize(analysisResults.size());
@@ -140,6 +158,8 @@ void FilterTreeAnalyse::getAnalysisResults(std::vector<FILTERTREE_ERR> &errs) co
 
 void FilterTreeAnalyse::analyse(const FilterTree &f)
 {
+	clear();
+
 	f.getAccumulatedPropagationMaps(emitTypes,blockTypes);
 
 	//Check for a data pair where the output is entirely blocked,
@@ -155,6 +175,10 @@ void FilterTreeAnalyse::analyse(const FilterTree &f)
 	//Check for filters that do not have a parent, which is required
 	checkRequiredParent(f);
 
+	//check for unranged data required by child
+	checkUnrangedData(f);
+
+
 	emitTypes.clear();
 	blockTypes.clear();
 
@@ -213,7 +237,7 @@ void FilterTreeAnalyse::blockingPairError(const FilterTree &f)
 			
 				analysisResults.push_back(treeErr);
 			}
-			//If the parent does not emit a useable objects 
+			//If the parent does not emit a usable objects 
 			//for the child filter, this is bad too.
 			// - else if, so we don't double up on warnings
 			else if( !(parentEmit & curUse) && !childFilter->isUsefulAsAppend())
@@ -356,7 +380,7 @@ void FilterTreeAnalyse::checkRequiredParent(const FilterTree &f)
 			}
 		}
 
-		//If we couldnt find a parent, then this is an error.
+		//If we couldn't find a parent, then this is an error.
 		// let the user know
 		if(!foundParent)
 		{
@@ -380,6 +404,40 @@ void FilterTreeAnalyse::checkRequiredParent(const FilterTree &f)
 	}
 }
 
+void FilterTreeAnalyse::checkUnrangedData(const FilterTree &f)
+{
+	const tree<Filter *> &treeFilt=f.getTree();
+	for(tree<Filter*>::pre_order_iterator it(treeFilt.begin()); it!=treeFilt.end(); ++it)
+	{
+		//Check to see if we have a filter that can be affected by unranged data, missing or present
+
+		if((*it)->getType() == FILTER_TYPE_RANGEFILE)
+		{
+			const RangeFileFilter *rngF = (const RangeFileFilter *)(*it);
+
+			//we only need to investigate filters which drop data
+			if( !rngF->getDropUnranged() )
+				continue;
+
+			for(tree<Filter*>::pre_order_iterator itJ(it); itJ!=treeFilt.end();++itJ)
+			{
+				//we need ranged data, but don't have it. Warn 
+				if(needsUnrangedData(*itJ))
+				{
+					FILTERTREE_ERR treeErr;
+					treeErr.reportedFilters.push_back(*it);
+					treeErr.reportedFilters.push_back(*itJ);
+					treeErr.shortReportMessage=TRANS("Bad range filter settings");
+					treeErr.verboseReportMessage=TRANS("Rangefile set to drop unranged data, however a child filter requires it.");						
+					treeErr.severity=ANALYSE_SEVERITY_WARNING;
+
+					analysisResults.push_back(treeErr);
+				} 
+			}
+
+		}
+	} 
+}
 
 bool filterAltersComposition(const Filter *f)
 {
diff --git a/src/backend/filtertreeAnalyse.h b/src/backend/filtertreeAnalyse.h
index 829fdcd..91e0630 100644
--- a/src/backend/filtertreeAnalyse.h
+++ b/src/backend/filtertreeAnalyse.h
@@ -1,6 +1,6 @@
 /*
  * 	filtertree.h - Filter tree topology and data propagation handling
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -61,9 +61,15 @@ class FilterTreeAnalyse
 		// are being used with sampling.
 		void spatialSampling(const FilterTree &f);
 
+		//check to see if there is a filter who is biasing composition
 		void compositionAltered(const FilterTree &f);
 
+		//check to see if there is a filter that needs a particular parent
 		void checkRequiredParent(const FilterTree &f);
+		
+		//check to see if there is a filter that needs unranged data to work,
+		// but does not have it 
+		void checkUnrangedData(const FilterTree &f);
 	public:
 		void analyse(const FilterTree &f);
 
diff --git a/src/backend/plot.cpp b/src/backend/plot.cpp
index a6c5e99..bd6065f 100644
--- a/src/backend/plot.cpp
+++ b/src/backend/plot.cpp
@@ -1,6 +1,6 @@
 /*
  *	plot.cpp - mathgl plot wrapper class
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -42,6 +42,7 @@ const char *plotTypeStrings[]= {
 
 using std::string;
 using std::pair;
+using std::make_pair;
 using std::vector;
 
 //Axis min/max bounding box is disallowed to be exactly zero on any given axis
@@ -56,7 +57,7 @@ bool mglFloatTooClose(float a, float b)
 {
 	//For small numbers an absolute delta can catch
 	// too close values
-	if(fabs(a-b) < sqrt(std::numeric_limits<float>::epsilon()))
+	if(fabs(a-b) < sqrtf(std::numeric_limits<float>::epsilon()))
 		return true;
 
 	const int FLOAT_ACC_MASK=0xffff0000;
@@ -75,7 +76,7 @@ bool mglFloatTooClose(float a, float b)
 	u.i&=FLOAT_ACC_MASK;
 	b=u.f;
 
-	if(fabs(a-b) < sqrt(std::numeric_limits<float>::epsilon()))
+	if(fabs(a-b) < sqrtf(std::numeric_limits<float>::epsilon()))
 		return true;
 
 	return false;
@@ -149,55 +150,6 @@ string plotErrmodeString(unsigned int plotID)
 	return errModeStrings[plotID];
 }
 
-
-void genErrBars(const std::vector<float> &x, const std::vector<float> &y, 
-			std::vector<float> &errBars, const PLOT_ERROR &errMode)
-{
-	switch(errMode.mode)
-	{
-		case PLOT_ERROR_NONE:
-			return;	
-		case PLOT_ERROR_MOVING_AVERAGE:
-		{
-			ASSERT(errMode.movingAverageNum);
-			errBars.resize(y.size());
-			for(int ui=0;ui<(int)y.size();ui++)
-			{
-				float mean;
-				mean=0.0f;
-
-				//Compute the local mean
-				for(int uj=0;uj<(int)errMode.movingAverageNum;uj++)
-				{
-					//TODO: Why are we using (int) here?
-					int idx;
-					idx= std::max(ui+uj-(int)errMode.movingAverageNum/2,0);
-					idx=std::min(idx,(int)(y.size()-1));
-					ASSERT(idx<(int)y.size());
-					mean+=y[idx];
-				}
-
-				mean/=(float)errMode.movingAverageNum;
-
-				//Compute the local stddev
-				float stddev;
-				stddev=0;
-				for(int uj=0;uj<(int)errMode.movingAverageNum;uj++)
-				{
-					int idx;
-					idx= std::max(ui+uj-(int)errMode.movingAverageNum/2,0);
-					idx=std::min(idx,(int)(y.size()-1));
-					stddev+=(y[idx]-mean)*(y[idx]-mean);
-				}
-
-				stddev=sqrtf(stddev/(float)errMode.movingAverageNum);
-				errBars[ui]=stddev;
-			}
-			break;
-		}
-	}
-
-}
 //===
 
 PlotRegion::PlotRegion()
@@ -854,7 +806,21 @@ void PlotWrapper::drawPlot(mglGraph *gr, bool &haveUsedLog) const
 			// mathgl does not like a zero coordinate for plotting
 			if(min.y == 0 && useLogPlot)
 			{
-				min.y=0.1;
+				float minYVal=0.1;
+				for(size_t ui=0;ui<plottingData.size();ui++)
+				{
+					if(!plottingData[ui]->visible || plottingData[ui]->getType() !=PLOT_MODE_1D)
+						continue;
+
+					float tmp ;
+					tmp=((Plot1D*)plottingData[ui])->getSmallestNonzero();
+					if(tmp >0)
+						minYVal=std::min(tmp,minYVal);
+
+
+				}
+				ASSERT(minYVal > 0);
+				min.y=minYVal;
 			}
 			
 
@@ -862,9 +828,9 @@ void PlotWrapper::drawPlot(mglGraph *gr, bool &haveUsedLog) const
 			gr->SetRanges(min,max);
 			gr->SetOrigin(min);
 
-			WARN((fabs(min.x-max.x) > sqrt(std::numeric_limits<float>::epsilon())), 
+			WARN((fabs(min.x-max.x) > sqrtf(std::numeric_limits<float>::epsilon())), 
 					"WARNING: Mgl limits (X) too Close! Due to limitiations in MGL, This may inf. loop!");
-			WARN((fabs(min.y-max.y) > sqrt(std::numeric_limits<float>::epsilon())), 
+			WARN((fabs(min.y-max.y) > sqrtf(std::numeric_limits<float>::epsilon())), 
 					"WARNING: Mgl limits (Y) too Close! Due to limitiations in MGL, This may inf. loop!");
 
 			if(useLogPlot)
@@ -933,9 +899,9 @@ void PlotWrapper::drawPlot(mglGraph *gr, bool &haveUsedLog) const
 					rMinX=std::max(rMinX,(float)min.x);
 					rMaxX=std::min(rMaxX,(float)max.x);
 
-					//If the region is of negligble size, don't bother drawing it
+					//If the region is of negligible size, don't bother drawing it
 					if(fabs(rMinX -rMaxX)
-						< sqrt(std::numeric_limits<float>::epsilon()))
+						< sqrtf(std::numeric_limits<float>::epsilon()))
 						continue;
 
 
@@ -1239,6 +1205,62 @@ PlotBase *Plot1D::clone() const
 }
 
 
+void Plot1D::setErrMode(PLOT_ERROR mode) 
+{
+	errMode=mode;
+	genErrBars();
+}
+
+
+void Plot1D::genErrBars() 
+{
+	switch(errMode.mode)
+	{
+		case PLOT_ERROR_NONE:
+			return;	
+		case PLOT_ERROR_MOVING_AVERAGE:
+		{
+			//FIXME: Has contiguous assumption implicit
+			ASSERT(errMode.movingAverageNum);
+			errBars.resize(yValues.size());
+			for(int ui=0;ui<(int)yValues.size();ui++)
+			{
+				float mean;
+				mean=0.0f;
+
+				//Compute the local mean
+				for(int uj=0;uj<(int)errMode.movingAverageNum;uj++)
+				{
+					//TODO: Why are we using (int) here?
+					int idx;
+					idx= std::max(ui+uj-(int)errMode.movingAverageNum/2,0);
+					idx=std::min(idx,(int)(yValues.size()-1));
+					ASSERT(idx<(int)yValues.size());
+					mean+=yValues[idx];
+				}
+
+				mean/=(float)errMode.movingAverageNum;
+
+				//Compute the local stddev
+				float stddev;
+				stddev=0;
+				for(int uj=0;uj<(int)errMode.movingAverageNum;uj++)
+				{
+					int idx;
+					idx= std::max(ui+uj-(int)errMode.movingAverageNum/2,0);
+					idx=std::min(idx,(int)(yValues.size()-1));
+					stddev+=(yValues[idx]-mean)*(yValues[idx]-mean);
+				}
+
+				stddev=sqrtf(stddev/(float)errMode.movingAverageNum);
+				errBars[ui]=stddev;
+			}
+			break;
+		}
+	}
+
+}
+
 void Plot1D::setData(const vector<float> &vX, const vector<float> &vY, 
 		const vector<float> &vErr)
 {
@@ -1589,6 +1611,21 @@ void Plot1D::drawRegions(mglGraph *gr,
 	}
 }
 
+float Plot1D::getSmallestNonzero() const
+{
+	float minNonzero=std::numeric_limits<float>::max();
+	for(size_t ui=0;ui<yValues.size();ui++)
+	{
+		if(yValues[ui] > 0)
+			minNonzero=std::min(yValues[ui] ,minNonzero);
+	}
+
+	if(minNonzero == std::numeric_limits<float>::max())
+		return 0;
+	else
+		return minNonzero;
+}
+
 //--
 
 //2D plotting code
@@ -1669,6 +1706,7 @@ void Plot2DFunc::getRawData(std::vector<std::vector<float> >  &rawData,
 Plot2DScatter::Plot2DScatter()
 {
 	plotType=PLOT_2D_SCATTER;
+	scatterIntensityLog=false;
 }
 
 void Plot2DScatter::setData(const vector<pair<float,float> > &f) 
@@ -1739,15 +1777,26 @@ void Plot2DScatter::drawPlot(mglGraph *graph) const
 	}
 	else
 	{
-		sizeDat.Set(intensity);
+		//if we have intensity data, use it
+
+		if(!scatterIntensityLog)
+			sizeDat.Set(intensity);
+		else
+		{	
+			//if plotting in log mode, do so!
+			float *bufSize = new float[intensity.size()];
+			for(unsigned int ui=0;ui<intensity.size();ui++)
+				bufSize[ui]=log10f(intensity[ui]+1.0f);
+			sizeDat.Set(bufSize,intensity.size());
+			delete[] bufSize;
+		}
+		
 	}
 	
 	string colourCode;
 	colourCode=mglColourCode(r,g,b);
 	
-	graph->SetCut(false);
-	graph->Mark(xDat,yDat,sizeDat,"+",colourCode.c_str());
-	graph->SetCut(true);
+	graph->Mark(xDat,yDat,sizeDat,"o",colourCode.c_str());
 
 	
 }
diff --git a/src/backend/plot.h b/src/backend/plot.h
index d31fd2b..fae258d 100644
--- a/src/backend/plot.h
+++ b/src/backend/plot.h
@@ -1,6 +1,6 @@
 /*
  *	plot.h - plotting wraper for mathgl
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -23,7 +23,8 @@
 #include "backend/filter.h"
 #include "common/array2D.h"
 #include <map>
-
+#include <vector>
+#include <utility>
 
 #if  defined(WIN32) || defined(WIN64)
 	//Help mathgl out a bit: we don't need the GSL on this platform
@@ -59,6 +60,7 @@ enum
 {
 	PLOT_MODE_1D,
 	PLOT_MODE_2D,
+	PLOT_MODE_COLUMN,
 	PLOT_MODE_MIXED, //special marker - different types of plots, when looking at multiple plots
 	PLOT_MODE_ENUM_END //not a plot, just end of enum
 };
@@ -96,7 +98,7 @@ class PlotRegion
 		};
 		
 		//Bounding limits for axial bind
-		vector<std::pair<float,float> > bounds;
+		std::vector<std::pair<float,float> > bounds;
 
 		//Bounding region colour
 		float r,g,b;
@@ -116,7 +118,7 @@ class PlotRegion
 		void setUpdateMethod(size_t updateMethod, void *parentObject);
 
 		//Update the parent object using the curretn update method
-		void updateParent(size_t method, const vector<float> &newBounds, bool updateSelf=true);
+		void updateParent(size_t method, const std::vector<float> &newBounds, bool updateSelf=true);
 
 		//Retrieve the parent as a filter object - must be in ACCESS_MODE_FILTER
 		Filter *getParentAsFilter() const { ASSERT(accessMode==ACCESS_MODE_FILTER); return (Filter*)parentObject;};
@@ -132,14 +134,14 @@ class RegionGroup
 {
 	private:
 		//cache for region overlaps, to reduce need to search
-		mutable vector<pair<size_t,size_t> > overlapIdCache;
-		mutable vector<pair<float,float> > overlapCoordsCache;
+		mutable std::vector<std::pair<size_t,size_t> > overlapIdCache;
+		mutable std::vector<std::pair<float,float> > overlapCoordsCache;
 
 		mutable bool haveOverlapCache;
 	public:
 		RegionGroup() { haveOverlapCache=false;};
 		//!Interactive, or otherwise marked plot regions
-		vector<PlotRegion> regions;
+		std::vector<PlotRegion> regions;
 
 
 		void clear() {regions.clear(); };
@@ -164,16 +166,16 @@ class RegionGroup
 				unsigned int movementType, float &maxX, float &maxY) const;
 
 
-		void getOverlaps(vector<pair<size_t,size_t> > &ids,
-				vector< pair<float,float> > &coords) const;
+		void getOverlaps(std::vector<std::pair<size_t,size_t> > &ids,
+				std::vector< std::pair<float,float> > &coords) const;
 };
 
 struct  OVERLAY_DATA
 {
 	//Coordinate and amplitude
-	vector<pair<float, float> > coordData;
+	std::vector<std::pair<float, float> > coordData;
 	//title for all of te specified overlay data
-	string title;
+	std::string title;
 	//If the overlay is enabled or not
 	bool enabled;
 };
@@ -185,7 +187,7 @@ class PlotOverlays
 	private:
 		bool isEnabled;
 		// List of the overlays that can be shown on the given plot
-		vector<OVERLAY_DATA>  overlayData;
+		std::vector<OVERLAY_DATA>  overlayData;
 	public:
 		PlotOverlays() : isEnabled(true) {}
 		//Add a new overlay to the plot
@@ -203,7 +205,7 @@ class PlotOverlays
 		void clear() {overlayData.clear();}
 		void erase(size_t item) {ASSERT(item < overlayData.size()); overlayData.erase(overlayData.begin() + item);;}
 
-		const vector<OVERLAY_DATA> &getOverlays() const { return overlayData;};
+		const std::vector<OVERLAY_DATA> &getOverlays() const { return overlayData;};
 
 };
 
@@ -229,14 +231,14 @@ class PlotBase
 		void copyBase(PlotBase *target) const;
 
 		//Find the upper and lower limit of a given dataset
-		static void computeDataBounds(const vector<float> &d, float &minV,float &maxV) ;
+		static void computeDataBounds(const std::vector<float> &d, float &minV,float &maxV) ;
 		
 		//Find the upper and lower limit of a given dataset
-		static void computeDataBounds(const vector<float> &d, const vector<float> &errorBar,
+		static void computeDataBounds(const std::vector<float> &d, const std::vector<float> &errorBar,
 								float &minV,float &maxV);
 
 		//Find the upper and lower limit of a given dataset
-		static void computeDataBounds(const vector<pair<float,float> > &d, 
+		static void computeDataBounds(const std::vector<std::pair<float,float> > &d, 
 					float &minVx,float &maxVx,float &minVy, float &maxVy);
 	public:
 		PlotBase();
@@ -284,7 +286,7 @@ class PlotBase
 		
 
 		//Retrieve the raw data associated with this plot.
-		virtual void getRawData(vector<vector<float> > &f,
+		virtual void getRawData(std::vector<std::vector<float> > &f,
 				std::vector<std::string> &labels) const=0;
 
 		//set the plot axis strings (x,y and title)
@@ -319,19 +321,24 @@ class Plot1D : public PlotBase
 		bool logarithmic;
 		//!Data
 		std::vector<float> xValues,yValues,errBars;
-		
+
+		//Do we want to draw error bars?
+		PLOT_ERROR errMode;	
+
+		//Set the error bars for this plot
+		void genErrBars();	
 	public:
 		Plot1D();
 		virtual bool isEmpty() const;
 		virtual PlotBase *clone() const;
 			
 		//!Set the plot data from a pair and symmetric Y error
-		void setData(const vector<std::pair<float,float> > &v);
-		void setData(const vector<std::pair<float,float> > &v,const vector<float> &symYErr);
+		void setData(const std::vector<std::pair<float,float> > &v);
+		void setData(const std::vector<std::pair<float,float> > &v,const std::vector<float> &symYErr);
 		//!Set the plot data from two vectors and symmetric Y error
-		void setData(const vector<float> &vX, const vector<float> &vY);
-		void setData(const vector<float> &vX, const vector<float> &vY,
-							const vector<float> &symYErr);
+		void setData(const std::vector<float> &vX, const std::vector<float> &vY);
+		void setData(const std::vector<float> &vX, const std::vector<float> &vY,
+							const std::vector<float> &symYErr);
 
 		
 		//!Move a region to a new location. 
@@ -370,6 +377,13 @@ class Plot1D : public PlotBase
 		bool wantLogPlot() const { return logarithmic;};
 		void setLogarithmic(bool p){logarithmic=p;};
 
+		//obtain the smallest nonzero value, if possible (otherwise, returns 0)
+		// - used to get limits for logarithmic plots, for example
+		float getSmallestNonzero() const;
+
+		//Set the current error mode
+		void setErrMode(PLOT_ERROR newErrMode) ;  
+
 };
 
 //!2D function, f(x,y). 
@@ -399,9 +413,12 @@ class Plot2DFunc : public PlotBase
 class Plot2DScatter : public PlotBase
 {
 	private:
-		vector<pair<float,float> > points;
-		vector<float > intensity;
+		std::vector<std::pair<float,float> > points;
+		std::vector<float > intensity;
 	public:
+		//Do we want to display the points in logarithmic terms?
+		bool scatterIntensityLog;
+
 		Plot2DScatter();
 		virtual bool isEmpty() const;
 		virtual PlotBase *clone() const;
@@ -415,8 +432,8 @@ class Plot2DScatter : public PlotBase
 				std::vector<std::string> &labels) const;
 
 		//reset the data stored in the plot
-		void setData(const vector<pair<float,float> > &pts);
-		void setData(const vector<pair<float,float> > &pts ,const vector<float> &intens);
+		void setData(const std::vector<std::pair<float,float> > &pts);
+		void setData(const std::vector<std::pair<float,float> > &pts ,const std::vector<float> &intens);
 };
 
 //Wrapper class for containing multiple plots 
@@ -474,7 +491,7 @@ class PlotWrapper
 		size_t numPlots() const { return plottingData.size();}
 
 		//Retrieve the IDs for the stored plots
-		void getPlotIDs(vector<unsigned int> &ids) const ;
+		void getPlotIDs(std::vector<unsigned int> &ids) const ;
 
 		//Retrieve the title of the plot
 		std::string getTitle(size_t plotId) const;
@@ -546,7 +563,7 @@ class PlotWrapper
 		bool isPlotVisible(unsigned int plotID) const;
 		
 		
-		void getVisibleIDs(vector<unsigned int> &plotID) const;
+		void getVisibleIDs(std::vector<unsigned int> &plotID) const;
 
 		//!Disable user bounds
 		void disableUserBounds(){plotChanged=true;applyUserBounds=false;};
@@ -580,15 +597,15 @@ class PlotWrapper
 		void getRegion(unsigned int plotId, unsigned int regionId, PlotRegion &r) const;
 
 		//Get all of the (id, regions) for plots. Bool allows for only the plots that are visible to be obtained
-		void getRegions(vector<pair<size_t,vector<PlotRegion> > > &regions, bool visibleOnly=true) const;
+		void getRegions(std::vector<std::pair<size_t,std::vector<PlotRegion> > > &regions, bool visibleOnly=true) const;
 		
 		//Return the ID and coordinates of any overlapping regions
 		// - this only returns overlaps for individual plots - not between plots
-		void getRegionOverlaps(std::vector<pair<size_t,size_t> > &ids,
-							std::vector< pair<float,float> > &coords) const;
+		void getRegionOverlaps(std::vector<std::pair<size_t,size_t> > &ids,
+							std::vector< std::pair<float,float> > &coords) const;
 
 		//!Retrieve the raw data associated with the selected plots.
-		void getRawData(vector<vector<vector<float> > >  &data, std::vector<std::vector<std::string> >  &labels) const;
+		void getRawData(std::vector<std::vector<std::vector<float> > >  &data, std::vector<std::vector<std::string> >  &labels) const;
 	
 
 		//!obtain the type of a plot, given the plot's uniqueID
@@ -614,7 +631,7 @@ class PlotWrapper
 
 		//TODO: convert to serialised parent path
 		//Override the last-visible selection. This allows for overriding which plots were selected, which is normally handled internally
-		void overrideLastVisible(vector< pair<const void *,unsigned int>  > &overridden); 
+		void overrideLastVisible(std::vector< std::pair<const void *,unsigned int>  > &overridden); 
 };
 
 #endif
diff --git a/src/backend/state.cpp b/src/backend/state.cpp
index be6f70a..2c8977a 100644
--- a/src/backend/state.cpp
+++ b/src/backend/state.cpp
@@ -1,6 +1,6 @@
 /*
  *	state.cpp - user session state handler
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -25,13 +25,37 @@
 const unsigned int MAX_UNDO_SIZE=10;
 
 #include <unistd.h>
+#include <stack>
+
+using std::vector;
+using std::string;
+using std::pair;
+using std::make_pair;
+using std::map;
+using std::endl; //TODO: Remove me?
+
+//FIXME: Global - need to make part of AnalysisState.
+//	 then provide references as needed
+//true if modification to state has occurred
+int stateModifyLevel=STATE_MODIFIED_NONE;
+
+void setStateModifyLevel(int newLevel)
+{
+	stateModifyLevel=std::max(newLevel,stateModifyLevel);
+}
+
+int getStateModifyLevel()
+{
+	return stateModifyLevel;
+}
 
 
 AnalysisState::AnalysisState()
 {
-	modifyLevel=STATE_MODIFIED_NONE;
 	useRelativePathsForSave=false;
 	activeCamera=0;
+	
+	savedCameras.push_back(new CameraLookAt);	
 
 	plotLegendEnable=true;
 
@@ -43,8 +67,8 @@ void AnalysisState::operator=(const AnalysisState &oth)
 {
 	clear();
 
-	activeTree=oth.activeTree;
-	
+	treeState=oth.treeState;	
+
 	stashedTrees=oth.stashedTrees;
 
 	effects.resize(oth.effects.size());
@@ -58,8 +82,6 @@ void AnalysisState::operator=(const AnalysisState &oth)
 	enabledStartupPlots=oth.enabledStartupPlots;
 	plotLegendEnable=oth.plotLegendEnable;
 	
-	undoTrees=oth.undoTrees; 
-	redoTrees=oth.redoTrees;
 
 	fileName=oth.fileName;
 	workingDir=oth.workingDir;
@@ -72,11 +94,6 @@ void AnalysisState::operator=(const AnalysisState &oth)
 	worldAxisMode=oth.worldAxisMode;
 	activeCamera=oth.activeCamera;
 
-	modifyLevel=oth.modifyLevel;
-
-
-	redoFilterStack=oth.redoFilterStack;
-	undoFilterStack=oth.undoFilterStack;
 
 	animationState=oth.animationState;
 	animationPaths=oth.animationPaths;
@@ -91,7 +108,7 @@ AnalysisState::~AnalysisState()
 
 void AnalysisState::clear()
 {
-	activeTree.clear();
+	treeState.clear();
 	
 	stashedTrees.clear();
 
@@ -99,9 +116,6 @@ void AnalysisState::clear()
 
 	clearEffects();
 
-	undoTrees.clear(); 
-	redoTrees.clear();
-
 
 	enabledStartupPlots.clear();
 	
@@ -125,7 +139,7 @@ void AnalysisState::clearEffects()
 
 }
 bool AnalysisState::save(const char *cpFilename, std::map<string,string> &fileMapping,
-		bool writePackage) const
+		bool writePackage, bool setStateModifyLevel) const
 {
 	//Open file for output
 	std::ofstream f(cpFilename);
@@ -194,7 +208,7 @@ bool AnalysisState::save(const char *cpFilename, std::map<string,string> &fileMa
 
 	//Write filter tree
 	//---------------
-	if(!activeTree.saveXML(f,fileMapping,writePackage,useRelativePathsForSave))
+	if(!treeState.getTreeRef().saveXML(f,fileMapping,writePackage,useRelativePathsForSave))
 		return  false;
 	//---------------
 
@@ -265,13 +279,27 @@ bool AnalysisState::save(const char *cpFilename, std::map<string,string> &fileMa
 	//Debug check to ensure we have written a valid xml file
 	ASSERT(isValidXML(cpFilename));
 
-	modifyLevel=STATE_MODIFIED_NONE;
+	if(setStateModifyLevel)
+		stateModifyLevel=STATE_MODIFIED_NONE;
 
 	return true;
 }
 
-bool AnalysisState::load(const char *cpFilename, std::ostream &errStream ) 
+bool AnalysisState::load(const char *cpFilename, bool doMerge,std::ostream &errStream ) 
 {
+	if(doMerge)
+	{
+		//create another state, and then perform merge
+		AnalysisState otherState;
+		bool loadOK;
+		loadOK=	otherState.load(cpFilename,false,errStream);
+	
+		if(!loadOK)	
+			return loadOK;
+		this->merge(otherState);
+		return true;
+	}
+
 	clear();
 
 	//Load the state from an XML file
@@ -565,12 +593,20 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 				newCameraVec.push_back(thisCam);	
 			}
 
-			//Enforce active cam value validity
-			if(newCameraVec.size() < activeCamera)
-				activeCamera=0;
 
 		}
+
 		
+		//Enforce active cam value validity.
+		if(newCameraVec.empty())	
+			newCameraVec.push_back(new CameraLookAt);
+
+		if(newCameraVec.size() < activeCamera)
+			activeCamera=0;
+
+		//Now the cameras are loaded into a temporary vector. We will 
+		// copy them into the scene soon
+
 		nodePtr=nodeStack.top();
 		nodeStack.pop();
 		
@@ -647,7 +683,7 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 				e = makeEffect(tmpStr);
 				if(!e)
 				{
-					errStream << TRANS("Unrecognised effect :") << tmpStr << endl;
+					errStream << TRANS("Unrecognised effect :") << tmpStr << std::endl;
 					throw 1;
 				}
 
@@ -657,7 +693,7 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 					if(newEffectVec[ui]->getType()== e->getType())
 					{
 						delete e;
-						errStream << TRANS("Duplicate effect found") << tmpStr << TRANS(" cannot use.") << endl;
+						errStream << TRANS("Duplicate effect found") << tmpStr << TRANS(" cannot use.") << std::endl;
 						throw 1;
 					}
 
@@ -770,11 +806,9 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 	}
 
 	//Now replace it with the new data
-	activeTree.swap(newFilterTree);
+	treeState.swapFilterTree(newFilterTree);
 	std::swap(stashedTrees,newStashes);
 
-	activeTree.initFilterTree();
-	
 	//Wipe the existing cameras, and then put the new cameras in place
 	savedCameras.clear();
 	
@@ -783,7 +817,7 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 	Camera *c=new CameraLookAt();
 	savedCameras.push_back(c);
 	activeCamera=0;
-
+	bool defaultSet = false;
 	//spin through
 	for(unsigned int ui=0;ui<newCameraVec.size();ui++)
 	{
@@ -791,16 +825,18 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 		// camera (one that does not show up to the users,
 		// and cannot be erased from the scene)
 		// set it directly. Otherwise, its a user camera.
-		if(newCameraVec[ui]->getUserString().size())
+
+		//if there are multiple without a string, only use the first
+		if(newCameraVec[ui]->getUserString().size() && !defaultSet)
 		{
 			savedCameras.push_back(newCameraVec[ui]);
-			activeCamera=ui;
 		}
 		else
 		{
 			ASSERT(savedCameras.size());
 			delete savedCameras[0];
 			savedCameras[0]=newCameraVec[ui];
+			defaultSet=true;
 		}
 
 	}
@@ -835,18 +871,17 @@ bool AnalysisState::load(const char *cpFilename, std::ostream &errStream )
 		free(wd);
 	}
 
-	//If we are merging then the default state has been altered
-	// if we are not merging, then it is overwritten
-	setModifyLevel(STATE_MODIFIED_NONE);
+	// state is overwritten
+	setStateModifyLevel(STATE_MODIFIED_NONE);
 
 	//Perform sanitisation on results
 	return true;
 }
 
-bool AnalysisState::merge(const AnalysisState &otherState)
+void AnalysisState::merge(const AnalysisState &otherState)
 {
 
-	setModifyLevel(STATE_MODIFIED_DATA);
+	setStateModifyLevel(STATE_MODIFIED_DATA);
 
 	//If we are merging, then there is a chance
 	//of a name-clash. We avoid this by trying to append -merge continuously
@@ -870,12 +905,13 @@ bool AnalysisState::merge(const AnalysisState &otherState)
 		}
 	}
 	
-	FilterTree f = otherState.activeTree;
-	activeTree.addFilterTree(f,0);
+	FilterTree f = otherState.treeState.getTreeRef();
+
+	//wipe treestate's undo/redo trees, as we can no longer rely on them
+	treeState.clearUndoRedoStacks();	
+
+	treeState.addFilterTree(f,0);
 	
-	//wipe our undo/redo trees, as we can no longer rely on them
-	undoTrees.clear();
-	redoTrees.clear();
 
 	const vector<Camera *> &newCameraVec = otherState.savedCameras;	
 	for(unsigned int ui=0;ui<newCameraVec.size();ui++)
@@ -910,11 +946,6 @@ bool AnalysisState::camNameExists(const std::string &s) const
 	return false;
 }
 
-void AnalysisState::copyFilterTree(FilterTree &f) const
-{
-	f = activeTree;
-}
-
 int AnalysisState::getWorldAxisMode() const 
 {
 	return worldAxisMode;
@@ -943,6 +974,8 @@ const Camera *AnalysisState::getCam(size_t offset) const
 
 void AnalysisState::removeCam(size_t offset)
 {
+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
+
 	ASSERT(offset < savedCameras.size());
 	savedCameras.erase(savedCameras.begin()+offset);
 	if(activeCamera >=savedCameras.size())
@@ -951,19 +984,36 @@ void AnalysisState::removeCam(size_t offset)
 
 void AnalysisState::addCamByClone(const Camera *c)
 {
-	setModifyLevel(STATE_MODIFIED_ANCILLARY);
+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
 	savedCameras.push_back(c->clone());
 }
 
+void AnalysisState::addCam(const std::string &camName, bool makeActive)
+{
+	//Duplicate the current camera, and give it a new name
+	Camera *c=getCam(getActiveCam())->clone();
+	c->setUserString(camName);
+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
+	savedCameras.push_back(c);
+
+	if(makeActive)
+		activeCamera=savedCameras.size()-1;
+}
+
 bool AnalysisState::setCamProperty(size_t offset, unsigned int key, const std::string &str)
 {
 	if(offset == activeCamera)
-		setModifyLevel(STATE_MODIFIED_VIEW);
+		setStateModifyLevel(STATE_MODIFIED_VIEW);
 	else
-		setModifyLevel(STATE_MODIFIED_ANCILLARY);
+		setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
 	return savedCameras[offset]->setProperty(key,str);
 }
 
+std::string AnalysisState::getCamName(size_t offset) const
+{
+	return  savedCameras[offset]->getUserString();
+}
+
 bool AnalysisState::getUseRelPaths() const 
 {
 	return useRelativePathsForSave;
@@ -994,7 +1044,7 @@ void AnalysisState::copyEffects(vector<Effect *> &e) const
 void AnalysisState::setBackgroundColour(float r, float g, float b)
 {
 	if(rBack != r || gBack!=g || bBack!=b)
-		setModifyLevel(STATE_MODIFIED_VIEW);
+		setStateModifyLevel(STATE_MODIFIED_VIEW);
 	rBack=r;
 	gBack=g;
 	bBack=b;
@@ -1004,13 +1054,13 @@ void AnalysisState::setBackgroundColour(float r, float g, float b)
 void AnalysisState::setWorldAxisMode(unsigned int mode)
 {
 	if(mode)
-		setModifyLevel(STATE_MODIFIED_VIEW);
+		setStateModifyLevel(STATE_MODIFIED_VIEW);
 	worldAxisMode=mode;
 }
 
 void AnalysisState::setCamerasByCopy(vector<Camera *> &c, unsigned int active)
 {
-	setModifyLevel(STATE_MODIFIED_DATA);
+	setStateModifyLevel(STATE_MODIFIED_DATA);
 	clearCams();
 
 	savedCameras.swap(c);
@@ -1024,14 +1074,14 @@ void AnalysisState::setCameraByClone(const Camera *c, unsigned int offset)
 	savedCameras[offset]=c->clone(); 
 
 	if(offset == activeCamera)
-		setModifyLevel(STATE_MODIFIED_VIEW);
+		setStateModifyLevel(STATE_MODIFIED_VIEW);
 	else
-		setModifyLevel(STATE_MODIFIED_ANCILLARY);
+		setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
 }
 
 void AnalysisState::setEffectsByCopy(const vector<const Effect *> &e)
 {
-	setModifyLevel(STATE_MODIFIED_VIEW);
+	setStateModifyLevel(STATE_MODIFIED_VIEW);
 	clearEffects();
 
 	effects.resize(e.size());
@@ -1048,29 +1098,35 @@ void AnalysisState::setUseRelPaths(bool useRel)
 void AnalysisState::setWorkingDir(const std::string &work)
 {
 	if(work!=workingDir)
-		setModifyLevel(STATE_MODIFIED_DATA);
+		setStateModifyLevel(STATE_MODIFIED_DATA);
 
 	workingDir=work;
 }
 
-void AnalysisState::setFilterTreeByClone(const FilterTree &f)
-{
-	setModifyLevel(STATE_MODIFIED_DATA);
-	activeTree=f;
-}
-
 void AnalysisState::setStashedTreesByClone(const vector<std::pair<string,FilterTree> > &s)
 {
-	setModifyLevel(STATE_MODIFIED_ANCILLARY);
+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
 	stashedTrees=s;
 }
 
 void AnalysisState::addStashedTree(const std::pair<string,FilterTree> &s)
 {
-	setModifyLevel(STATE_MODIFIED_ANCILLARY);
+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
 	stashedTrees.push_back(s);
 }
 
+void AnalysisState::addStashedToFilters(const Filter *parentFilter, unsigned int stashOffset)
+{
+	//Save current filter state to undo stack
+	treeState.pushUndoStack();
+
+	//Retrieve the specified stash
+	pair<string,FilterTree> f;
+	copyStashedTree(stashOffset,f);
+
+	treeState.addFilterTree(f.second,parentFilter);
+}
+
 void AnalysisState::copyStashedTrees(std::vector<std::pair<string,FilterTree > > &s) const
 {
 	s=stashedTrees;
@@ -1078,27 +1134,329 @@ void AnalysisState::copyStashedTrees(std::vector<std::pair<string,FilterTree > >
 
 void AnalysisState::copyStashedTree(size_t offset,std::pair<string,FilterTree> &s) const
 {
-	s.first=stashedTrees[offset].first;
-	s.second=stashedTrees[offset].second;
+	s=stashedTrees[offset];
 }
 
+void AnalysisState::copyStashedTree(size_t offset,FilterTree &s) const
+{
+	s=stashedTrees[offset].second;
+}
 
-void AnalysisState::pushUndoStack()
+void AnalysisState::stashFilters(unsigned int filterId, const char *stashName)
+{
+	//Obtain the parent filter that we 
+	const Filter *target = treeState.getFilterById(filterId);
+	
+	FilterTree newTree;
+	const FilterTree &curTree = treeState.getTreeRef();
+	curTree.cloneSubtree(newTree,target);
+
+	addStashedTree(std::make_pair(string(stashName),newTree));
+}
+
+//Get the stash name
+std::string AnalysisState::getStashName(size_t offset) const
+{
+	ASSERT(offset < stashedTrees.size());
+	return  stashedTrees[offset].first;
+}
+
+
+
+void AnalysisState::eraseStash(size_t offset)
+{
+	ASSERT(offset < stashedTrees.size());
+	setStateModifyLevel(STATE_MODIFIED_ANCILLARY);
+	stashedTrees.erase(stashedTrees.begin() + offset);
+}
+
+bool AnalysisState::hasStateOverrides() const
+{
+	if(treeState.hasStateOverrides())
+		return true;
+
+	for(size_t ui=0;ui<stashedTrees.size();ui++)
+	{
+		if(stashedTrees[ui].second.hasStateOverrides())
+			return true;
+	}
+
+	return false;
+}
+
+void TreeState::operator=(const TreeState &oth) 
+{
+#ifdef DEBUG
+	//Should not be refreshing
+	wxMutexLocker lock(amRefreshing);
+	ASSERT(!lock.IsOk());
+#endif
+	filterTree=oth.filterTree;	
+	
+	fta=oth.fta;
+	filterMap=oth.filterMap;	
+	redoFilterStack=oth.redoFilterStack;
+	undoFilterStack=oth.undoFilterStack;
+	
+	fta=oth.fta;
+	selectionDevices=oth.selectionDevices;
+	pendingUpdates=oth.pendingUpdates;
+	
+}
+
+void TreeState::addFilter(Filter *f, bool isBase,size_t parentId)
+{ 
+	pushUndoStack();
+	if(!isBase)
+		filterTree.addFilter(f,filterMap[parentId]);
+	else
+		filterTree.addFilter(f,0);
+
+
+	vector<bool> idInUse(filterMap.size()+1,false);
+	for(map<size_t,Filter*>::const_iterator it=filterMap.begin();it!=filterMap.end();++it)
+	{
+		idInUse[it->first] = true; 
+	}
+
+	//Find which ID we can use for inserting stuff into the filtermap
+	size_t idToUse;
+	for(size_t ui=0;ui<filterMap.size();ui++)
+	{
+		if(!idInUse[ui])
+		{
+			idToUse=ui;
+			continue;
+		}
+	}	
+	
+
+	//Add new entry to filer map
+	filterMap[idToUse] = f;
+}
+
+void TreeState::addFilterTree(FilterTree &f, bool isBase,size_t parentId)
+{ 
+	ASSERT(!(isBase && parentId==(unsigned int)-1));
+
+	if(isBase)
+		filterTree.addFilterTree(f,0);
+	else
+		filterTree.addFilterTree(f,filterMap[parentId]);
+
+	//FIXME: This technically does not need to be cleared. We can
+	// tweak the filter map to remain valid. It is the caller's problem
+	// to rebuild as needed
+
+	//The filter map is now invalid, as we added an element to the tree,
+	//and don't have a unique value for it. we need to relayout.
+	filterMap.clear();
+}
+
+void TreeState::switchoutFilterTree(FilterTree &f)
+{
+	//Create a clone of the internal tree
+	f=filterTree;
+
+	//Fix up the internal filterMap to reflect the contents of the new tree
+	//---
+	//
+	//Build a map from old filter*->new filter *
+	tree<Filter*>::pre_order_iterator itB;
+	itB=filterTree.depthBegin();
+	std::map<Filter*,Filter*> filterRemap;
+	for(tree<Filter*>::pre_order_iterator itA=f.depthBegin(); itA!=f.depthEnd(); ++itA)
+	{
+		ASSERT(itB != filterTree.depthEnd());
+		filterRemap[*itA]=*itB;	
+		++itB;
+	}
+
+	//Overwrite the internal map
+	for(map<size_t,Filter*>::iterator it=filterMap.begin();it!=filterMap.end();++it)
+		it->second=filterRemap[it->second];
+
+
+	//Swap the internal tree with our clone
+	f.swap(filterTree);
+	
+}
+
+//!Duplicate a branch of the tree to a new position. Do not copy cache,
+bool TreeState::copyFilter(size_t toCopy, size_t newParent,bool copyToRoot) 
+{
+	pushUndoStack();
+	
+	bool ret;
+	if(copyToRoot)
+		ret=filterTree.copyFilter(filterMap[toCopy],0);
+	else
+	
+		ret=filterTree.copyFilter(filterMap[toCopy],filterMap[newParent]);
+
+	if(ret)
+	{
+		//Delete the filtermap, as the current data is not valid anymore
+		filterMap.clear();
+	}
+
+	return ret;
+}
+
+
+const Filter* TreeState::getFilterById(size_t filterId) const 
+{
+	//If triggering this assertion, check that
+	//::updateWxTreeCtrl called after calling addFilterTree.
+	ASSERT(filterMap.size());
+
+	//Check that the mapping exists
+	ASSERT(filterMap.find(filterId)!=filterMap.end());
+	return filterMap.at(filterId);
+}
+
+
+size_t TreeState::getIdByFilter(const Filter* f) const
+{
+	for(map<size_t,Filter*>::const_iterator it=filterMap.begin(); it!=filterMap.end();++it)
+	{
+		if(it->second == f)
+			return it->first;
+	}	
+
+	ASSERT(false);
+	return (size_t)-1;
+}
+void TreeState::getFiltersByType(std::vector<const Filter *> &filters, unsigned int type)  const
+{
+	filterTree.getFiltersByType(filters,type);
+}
+
+
+void TreeState::setCachePercent(unsigned int newPct)
+{
+	filterTree.setCachePercent(newPct);
+}
+
+
+void TreeState::removeFilterSubtree(size_t filterId)
+{
+	//Save current filter state to undo stack
+	pushUndoStack();
+       	filterTree.removeSubtree(filterMap[filterId]);
+
+	//FIXME: Faster implementation involving removal from map
+	//--
+	map<size_t,Filter*> newMap;
+	for(map<size_t,Filter*>::iterator it=filterMap.begin(); it!=filterMap.end();++it)
+	{
+		if(filterTree.contains(it->second))
+			newMap[it->first] = it->second;
+	}
+	newMap.swap(filterMap);
+	//--
+
+}
+
+bool TreeState::reparentFilter(size_t filter, size_t newParent)
+{
+	//Save current filter state to undo stack
+	pushUndoStack();
+
+	//Try to reparent this filter. It might not work, if, for example
+	// the new parent is actually a child of the filter we are trying to
+	// assign the parent to. 
+	if(!filterTree.reparentFilter(filterMap[filter],filterMap[newParent]))
+	{
+		//Didn't work. Pop the undo stack, to reverse our 
+		//push, but don't restore it,
+		// as this would cost us our filter caches
+		popUndoStack(false);
+		return false;
+	}
+	
+	return true;
+}
+
+bool TreeState::setFilterProperty(size_t filterId, 
+				unsigned int key, const std::string &value, bool &needUpdate)
+{
+	//Save current filter state to undo stack
+	//for the case where the property change is good
+	pushUndoStack();
+	bool setOK;
+	setOK=filterTree.setFilterProperty(filterMap[filterId],key,value,needUpdate);
+
+	if(!setOK)
+	{
+		//Didn't work, so we need to discard the undo
+		//Pop the undo stack, but don't restore it -
+		// restoring would destroy the cache
+		popUndoStack(false);
+	}
+
+	return setOK;
+}
+
+void TreeState::setFilterString(size_t filterId, const std::string &s)
+{
+	Filter *f;
+	f = filterMap[filterId];
+	
+	f->setUserString(s);
+}
+
+unsigned int TreeState::refresh(std::list<FILTER_OUTPUT_DATA> &refreshData,
+				std::vector<std::pair<const Filter*, std::string> > &consoleMessages, ProgressData &curProg)
+{
+	//Attempt to acquire a lock. Return -1 if locking fails
+	wxMutexLocker lock(amRefreshing);
+	if(!lock.IsOk())
+	{
+		ASSERT(false); // should not get here. Caller should not
+				// try to double-refresh
+		return -1;
+	}
+	ASSERT(refreshData.empty())
+
+	//Analyse the filter tree structure for any errors
+	//--	
+	fta.analyse(filterTree);
+	//--
+
+	//Reset the progress back to zero	
+	curProg.reset();
+	//clear old devices
+	selectionDevices.clear();
+	//Remove any updates
+	pendingUpdates=false;
+	wantAbort=false;
+
+	//Run the tree refresh system.
+	unsigned int errCode;
+	errCode=filterTree.refreshFilterTree(refreshData,selectionDevices,
+			consoleMessages,curProg,wantAbort);
+
+	//return error code, if any
+	return errCode;
+}
+
+void TreeState::pushUndoStack()
 {
 	if(undoFilterStack.size() > MAX_UNDO_SIZE)
 		undoFilterStack.pop_front();
 
-	undoFilterStack.push_back(activeTree);
+	undoFilterStack.push_back(filterTree);
 	redoFilterStack.clear();
 }
 
-void AnalysisState::popUndoStack(bool restorePopped)
+void TreeState::popUndoStack(bool restorePopped)
 {
 	ASSERT(undoFilterStack.size());
 
 	//Save the current filters to the redo stack.
 	// note that the copy constructor will generate a clone for us.
-	redoFilterStack.push_back(activeTree);
+	redoFilterStack.push_back(filterTree);
 
 	if(redoFilterStack.size() > MAX_UNDO_SIZE)
 		redoFilterStack.pop_front();
@@ -1106,39 +1464,94 @@ void AnalysisState::popUndoStack(bool restorePopped)
 	if(restorePopped)
 	{
 		//Swap the current filter cache out with the undo stack result
-		std::swap(activeTree,undoFilterStack.back());
+		filterTree.swap(undoFilterStack.back());
 		
 	}
 
 	//Pop the undo stack
 	undoFilterStack.pop_back();
 
-	setModifyLevel(STATE_MODIFIED_DATA);
+	setStateModifyLevel(STATE_MODIFIED_DATA);
 }
 
-void AnalysisState::popRedoStack()
+void TreeState::popRedoStack()
 {
 	ASSERT(undoFilterStack.size() <=MAX_UNDO_SIZE);
-	undoFilterStack.push_back(activeTree);
+	undoFilterStack.push_back(filterTree);
 
-	activeTree.clear();
 	//Swap the current filter cache out with the redo stack result
-	activeTree.swap(redoFilterStack.back());
+	filterTree.swap(redoFilterStack.back());
 	
 	//Pop the redo stack
 	redoFilterStack.pop_back();
 
-	setModifyLevel(STATE_MODIFIED_DATA);
+	setStateModifyLevel(STATE_MODIFIED_DATA);
 }
 
+void TreeState::applyBindings(const std::vector<std::pair<const Filter *,SelectionBinding> > &bindings)
+{
+	if(!bindings.size())
+		return;
+	pushUndoStack();
 
-void AnalysisState::eraseStash(size_t offset)
+	for(unsigned int ui=0;ui<bindings.size();ui++)
+	{
+#ifdef DEBUG
+		bool haveBind;
+		haveBind=false;
+#endif
+		for(tree<Filter *>::iterator it=filterTree.depthBegin(); 
+				it!=filterTree.depthEnd();++it)
+		{
+			if(*it  == bindings[ui].first)
+			{
+				//We are modifying the contents of
+				//the filter, this could make a change that
+				//modifies output so we need to clear 
+				//all subtree caches to force reprocessing
+
+				filterTree.clearCache(*it,false);
+
+				(*it)->setPropFromBinding(bindings[ui].second);
+#ifdef DEBUG
+				haveBind=true;
+#endif
+				break;
+			}
+		}
+
+		ASSERT(haveBind);
+
+	}
+
+}
+
+void TreeState::applyBindingsToTree()
 {
-	ASSERT(offset < stashedTrees.size());
-	setModifyLevel(STATE_MODIFIED_ANCILLARY);
-	stashedTrees.erase(stashedTrees.begin() + offset);
+	//Clear any updates
+	pendingUpdates=false;
+		
+	//Retrieve all the modified bindings
+	vector<pair<const Filter *,SelectionBinding> > bindings;
+	for(unsigned int ui=0;ui<selectionDevices.size();ui++)
+		selectionDevices[ui]->getModifiedBindings(bindings);
+
+	applyBindings(bindings);	
+
+	//Clear the modifications to the selection devices
+	for(unsigned int ui=0;ui<selectionDevices.size();ui++)
+		selectionDevices[ui]->resetModifiedBindings();
+
+
 }
 
+bool TreeState::hasUpdates() const
+{
+	return pendingUpdates;
+}
+
+
+
 #ifdef DEBUG
 
 #include "./filters/ionDownsample.h"
@@ -1159,9 +1572,11 @@ bool testStateReload()
 	FilterTree tree;
 	IonDownsampleFilter *f = new IonDownsampleFilter;
 	tree.addFilter(f,NULL);
+	ASSERT(tree.size());
 
-	someState.setFilterTreeByClone(tree);
 	someState.addStashedTree(make_pair("someStash",tree));
+	ASSERT(tree.size());
+	someState.treeState.swapFilterTree(tree);
 
 	std::string saveString;
 	genRandomFilename(saveString);
@@ -1175,15 +1590,13 @@ bool testStateReload()
 	someState.clear();
 
 	std::ofstream strm;
-	TEST(someState.load(saveString.c_str(),strm),"State load");
+	TEST(someState.load(saveString.c_str(),false,strm),"State load");
 
 	TEST(someState.getStashCount() == 1,"Stash save+load");
 	std::pair<string,FilterTree> stashOut;
 	someState.copyStashedTree(0,stashOut);
 	TEST(stashOut.first == "someStash","Stash name conservation");
 
-	TEST(stashOut.second.size() == tree.size(),"reloaded stash count");
-	
 	rmFile(saveString);
 
 	return true;
diff --git a/src/backend/state.h b/src/backend/state.h
index 3ebcb9e..275eece 100644
--- a/src/backend/state.h
+++ b/src/backend/state.h
@@ -1,6 +1,6 @@
 /*
  *	state.h - user session state handler
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#ifndef STATE_H
+#define STATE_H
 
 #include <string>
 #include <vector>
@@ -23,9 +25,11 @@
 #include "gl/cameras.h"
 #include "gl/effect.h"
 
+#include <wx/thread.h>
 
 #include "tree.hh"
 #include "filtertree.h"
+#include "filtertreeAnalyse.h"
 
 #include "animator.h"
 
@@ -34,17 +38,8 @@
 bool runStateTests();
 #endif
 
-
-struct FilterTreeState
-{
-	//The tree of filters in the current state
-	FilterTree filterTree;
-	//True for items that can be seen by the user
-	tree<bool> visibility;
-
-	//index (BFS search wise) of the selected item
-	size_t selectedBFSIndex;
-};
+void setStateModifyLevel(int newLevel);
+int getStateModifyLevel();
 
 enum
 {
@@ -54,10 +49,175 @@ enum
 	STATE_MODIFIED_DATA // actual data output is latered
 };
 
+
+class TreeState
+{
+	private:
+		//!currently active tree
+		FilterTree filterTree;
+		
+		//!ID handler that assigns each filter its own ID that
+		// is guaranteed to be unique for the life of the filter
+		// in the filterTree. These are not guaranteed to be dense
+		//TODO: Can we const the filter pointer?
+		std::map<size_t, Filter * > filterMap;	
+
+		//!Undo/redo stack for current state
+		std::deque<FilterTree> undoFilterStack,redoFilterStack;
+	
+		FilterTreeAnalyse fta;
+
+		std::vector<SelectionDevice *> selectionDevices;
+
+		void applyBindings(const std::vector<std::pair<const Filter *,SelectionBinding> > &bindings);
+
+		//!True if there are pending updates from the user
+		bool pendingUpdates;
+
+		//re-entry catcher
+		wxMutex amRefreshing;	
+
+		//Do we want to abort the refresh? This is passed to the tree
+		// to signal if the primary thread would like to abort
+		ATOMIC_BOOL wantAbort;
+	public:
+		TreeState() {pendingUpdates=false; wantAbort=false;}
+		
+		void operator=(const TreeState &otherState);  
+
+		unsigned int refresh(std::list<FILTER_OUTPUT_DATA> &outData,
+				std::vector<std::pair<const Filter*, std::string> > &consoleMessages, ProgressData &prog);
+
+		//set the abort flag 
+		void setAbort() { wantAbort=true;}
+		//are we refreshing?
+		bool isRefreshing() const { return filterTree.isRefreshing();}
+		//!Inform that it has new updates to filters from external sources (eg bindings)
+		void setUpdates() { pendingUpdates=true;};
+
+		//!Returns true if the filter tree has updates that need to be processed
+		bool hasUpdates() const; 
+
+		//Obtain a clone of the active filter tree
+		void cloneFilterTree(FilterTree &f) const {f=filterTree;};
+
+		const FilterTree &getTreeRef() const { return filterTree ;};
+
+		//!Add a new filter to the tree. Set isbase=false and parentID for not
+		//setting a parent (ie making filter base)
+		void addFilter(Filter *f, bool isBase, size_t parentId);
+		
+		//!Add a new subtree to the tree. Note that the tree will be cleared
+		// as a result of this operation. Control of all pointers will be handled internally.
+		// Currently, If you wish to use ::getFilterById you *must* rebuild the tree control with
+		// ::updateWxTreeCtrl. This should be fixed.
+		void addFilterTree(FilterTree &f,bool isBase=true, 
+						size_t parentId=(unsigned int)-1); 
+
+		//!Grab the filter tree from the internal one, and swap the 
+		// internal with a cloned copy of the internal.
+		// Can be used eg, to steal the cache
+		// Note that the contents of the incoming filter tree will be destroyed.
+		//  -> This implies the tree comes *OUT* of viscontrol,
+		//     and a tree  cannot be inserted in via this function
+		void switchoutFilterTree(FilterTree &f);
+
+		//Perform a swap operation on the filter tree. 
+		// - *must* have same topology, or you must call updateWxTreeCtrl
+		// - can be used to *insert* a tree into this function
+		void swapFilterTree(FilterTree &f) { f.swap(filterTree);}
+
+		void swapFilterMap(std::map<size_t,Filter*> &m) { filterMap.swap(m);}
+
+		//!Duplicate a branch of the tree to a new position. Do not copy cache,
+		bool copyFilter(size_t toCopy, size_t newParent,bool copyToRoot=false) ;
+
+		//TODO: Deprecate me - filter information should not be leaking like this!
+		//Get the ID of the filter from its actual pointer
+		size_t getIdByFilter(const Filter* f) const;
+
+		const Filter* getFilterById(size_t filterId) const; 
+
+		//!Return all of a given type of filter from the filter tree. Type must be the exact type of filter - it is not a mask
+		void getFiltersByType(std::vector<const Filter *> &filters, unsigned int type)  const;
+
+		//!Return the number of filters currently in the main tree
+		size_t numFilters() const { return filterTree.size();};
+
+		//!Clear the cache for the filters
+		void purgeFilterCache() { filterTree.purgeCache();};
+
+		//!Delete a filter and all its children
+		void removeFilterSubtree(size_t filterId);
+
+		//Move a filter from one part of the tree to another
+		bool reparentFilter(size_t filterID, size_t newParentID);
+
+		//!Set the properties using a key-value result 
+		/*
+		 * The return code tells whether to reject or accept the change. 
+		 * need update tells us if the change to the filter resulted in a change to the scene
+		 */
+		bool setFilterProperty(size_t filterId,unsigned int key,
+				const std::string &value, bool &needUpdate);
+	
+		//!Set the filter's string	
+		void setFilterString(size_t id, const std::string &s);
+		//Modify rangefiles pointed to by given map to new Rangefile (second pointer)
+		void modifyRangeFiles(const std::map<const RangeFile *, const RangeFile *> &toModify) { filterTree.modifyRangeFiles(toModify);};
+		
+		//!Clear all caches
+		void clearCache();
+		
+		//!Clear all caches
+		void clearCacheByType(unsigned int type) { filterTree.clearCacheByType(type);};
+
+		void clear() { filterTree.clear();filterMap.clear() ;fta.clear(); } 
+
+		size_t size() const { return filterTree.size(); }
+
+		//Push the filter tree undo stack
+		void pushUndoStack();
+
+		//Pop the filter tree undo stack. If restorePopped is true,
+		// then the internal filter tree is updated with the stack tree
+		void popUndoStack(bool restorePopped=true);
+
+		//Pop the redo stack, this unconditionally enforces an update of the
+		// active internal tree
+		void popRedoStack();
+
+		//Obtain the size of the undo stack
+		size_t getUndoSize() const { return undoFilterStack.size();};
+		//obtain the size of the redo stack
+		size_t getRedoSize() const { return redoFilterStack.size();};
+
+		//Clear undo/redo filter tree stacks
+		void clearUndoRedoStacks() { undoFilterStack.clear(); redoFilterStack.clear();}
+
+		void stripHazardousContents() { filterTree.stripHazardousContents();}
+
+		//!Apply external filter modifications that have been changed due to bindings
+		void applyBindingsToTree();
+		
+		//!Get the analysis results for the last refresh
+		void getAnalysisResults(std::vector<FILTERTREE_ERR> &res) const { fta.getAnalysisResults(res);}
+	
+		//!Set the cache maximum ram usage (0->100) 
+		void setCachePercent(unsigned int newCache);
+			
+		bool hasStateOverrides() const { return filterTree.hasStateOverrides();}
+	
+		//Return the selection devices obtained from the last refresh
+		std::vector<SelectionDevice *> &getSelectionDevices() { return selectionDevices;};
+	
+};
+
 //The underlying data for any given state in the analysis toolchain
 class AnalysisState
 {
 	private:
+
 		//Items that should be written to file
 		// on state save
 		//===
@@ -66,17 +226,12 @@ class AnalysisState
 
 		//!Filter trees that have been designated as inactive, but
 		// user would like to have them around for use
-		std::vector<std::pair<string,FilterTree> > stashedTrees;
+		std::vector<std::pair<std::string,FilterTree> > stashedTrees;
 
-		//!Undo/redo stack for current state
-		std::vector<FilterTreeState > undoTrees,redoTrees;
 
 		//Scene modification 3D Effects 
 		std::vector<const Effect *> effects;
 
-		//!Filter analysis stree	
-		FilterTree activeTree;
-
 		//Background colours
 		float rBack,gBack,bBack;
 		
@@ -90,7 +245,7 @@ class AnalysisState
 		bool plotLegendEnable;
 
 		//Filter path and ID of plots that need to be enabled at startup 
-		vector<pair<string,unsigned int> > enabledStartupPlots;
+		std::vector<std::pair<std::string,unsigned int> > enabledStartupPlots;
 
 		//true if system should be using relative paths when
 		// saving state
@@ -100,14 +255,10 @@ class AnalysisState
 		std::string workingDir;
 		//===
 
-		//true if modification to state has occurred
-		mutable int modifyLevel;
 
 		//file to save to
 		std::string fileName;
 		
-		//!Undo filter tree stack 
-		std::deque<FilterTree> undoFilterStack,redoFilterStack;
 		
 	
 		//!User-set animation properties
@@ -115,7 +266,7 @@ class AnalysisState
 
 		//TODO: Migrte into some state wrapper class with animationState
 		//Additional state information for animation
-		vector<pair<string,size_t>  > animationPaths;
+		std::vector<std::pair<std::string,size_t>  > animationPaths;
 
 		bool camNameExists(const std::string &s)  const ;
 
@@ -125,8 +276,10 @@ class AnalysisState
 		//Clear the camera data vector
 		void clearCams();
 
-		void setModifyLevel(int newLevel) { modifyLevel=std::max(newLevel,modifyLevel);}
 	public:
+
+		TreeState treeState;
+
 		AnalysisState();
 
 		~AnalysisState();
@@ -143,21 +296,22 @@ class AnalysisState
 		// - returns true on success, false on fail
 		// - errStream will have human readable messages in 
 		//	the case that there is a failure
-		bool load(const char *cpFilename, 
+		// - merge will attempt to join the 
+		bool load(const char *cpFilename,  bool merge,
 				std::ostream &errStream);
 
 		//save an XML-ised representation of the analysis sate
 		//	- mapping provides the on-disk to local name mapping to use when saving
 		// 	- write package says if state should attempt to ensure that output
 		// 		state is fully self-contained, and locally referenced
-		bool save(const char *cpFilename, std::map<string,string> &fileMapping,
-				bool writePackage) const ;
+		bool save(const char *cpFilename, std::map<std::string,std::string> &fileMapping,
+				bool writePackage,bool setModifyLevel=true) const ;
 
 		//Combine a separate state file into this one, avoiding clashes
-		bool merge(const AnalysisState &srcState);
+		void merge(const AnalysisState &srcState);
 
 		//Return the current state's filename
-		string getFilename() const { return fileName; }
+		std::string getFilename() const { return fileName; }
 		//Return the current state's filename
 		void setFilename(std::string &s) {fileName=s; }
 	
@@ -169,9 +323,6 @@ class AnalysisState
 		//obtain the scene background colour
 		void getBackgroundColour(float &r, float &g, float &b) const;
 
-		//Copy the internal effect vector. 
-		//	-Must manually delete each pointer
-		void copyEffects(vector<Effect *> &effects) const;
 
 		//Set the background colour for the 
 		void setBackgroundColour(float r, float g, float b);
@@ -182,7 +333,7 @@ class AnalysisState
 		// === Cameras ===
 		//Set the camera vector, clearing any existing cams
 		// note that control of pointers will be taken
-		void setCamerasByCopy(vector<Camera *> &c, unsigned int active);
+		void setCamerasByCopy(std::vector<Camera *> &c, unsigned int active);
 
 
 		void setCameraByClone(const Camera *c, unsigned int offset) ;
@@ -199,12 +350,12 @@ class AnalysisState
 		const Camera *getCam(size_t offset) const;
 		//Obtain a copy of the internal camera vector.
 		// - must delete the copy manually.
-		void copyCams(vector<Camera *> &cams) const;
+		void copyCams(std::vector<Camera *> &cams) const;
 
 		//Obtain a copy of the internal camera vector.
 		// note that this reference has limited validity, and may be
 		// invalidated if the state is modified
-		void copyCamsByRef(vector<const Camera *> &cams) const;
+		void copyCamsByRef(std::vector<const Camera *> &cams) const;
 
 		size_t getNumCams() const { return savedCameras.size();}
 
@@ -212,18 +363,31 @@ class AnalysisState
 		void addCamByClone(const Camera *c);
 
 		bool setCamProperty(size_t offset, unsigned int key, const std::string &value);
+
+		std::string getCamName(size_t offset) const; 
+
+		//!Add a new camera to the scene
+		void addCam(const std::string &camName, bool makeActive=false);
 		//=====
 
+		//Effect functions
+		//===
+
 		//Set the effect vector
-		void setEffectsByCopy(const vector<const Effect *> &e);
+		void setEffectsByCopy(const std::vector<const Effect *> &e);
+
+		//Copy the internal effect vector. 
+		//	-Must manually delete each pointer
+		void copyEffects(std::vector<Effect *> &effects) const;
+		//===
 
 		//Plotting functions
 		//=======
 
 		void setPlotLegend(bool enabled) {plotLegendEnable=enabled;}
-		void setEnabledPlots(const vector<pair<string,unsigned int> > &enabledPlots) {enabledStartupPlots = enabledPlots;}
+		void setEnabledPlots(const std::vector<std::pair<std::string,unsigned int> > &enabledPlots) {enabledStartupPlots = enabledPlots;}
 
-		void getEnabledPlots(vector<pair<string,unsigned int> > &enabledPlots) const { enabledPlots=enabledStartupPlots;}
+		void getEnabledPlots(std::vector<std::pair<std::string,unsigned int> > &enabledPlots) const { enabledPlots=enabledStartupPlots;}
 
 		//Set whether to use relative paths in saved file
 		void setUseRelPaths(bool useRel);
@@ -235,66 +399,45 @@ class AnalysisState
 		//Set the working directory to be specified when using relative paths
 		std::string getWorkingDir() const { return workingDir;};
 
-		//Set the active filter tree - note that the tree is a "clone" of the
-		// original tree - i.e. the incoming tree is duplicated
-		void setFilterTreeByClone(const FilterTree &f);
-		
-		//Obtain a copy of the internal filter tree
-		// - underlying pointers will be different!
-		void copyFilterTree(FilterTree &f) const;
-
 		///Set the stashed filters to use internally
-		void setStashedTreesByClone(const vector<std::pair<string,FilterTree> > &s);
+		void setStashedTreesByClone(const std::vector<std::pair<std::string,FilterTree> > &s);
 
 		//Add an element to the stashed filters
-		void addStashedTree( const std::pair<string,FilterTree> &);
+		void addStashedTree( const std::pair<std::string,FilterTree> &);
+	
+		//!Transform the subtree at the given point into a stash, and save it
+		void stashFilters(unsigned int filterId, const char *stashName);
 
 		//Retrieve the specified stashed filter
-		void copyStashedTree(size_t offset, std::pair<string,FilterTree > &) const;
+		void copyStashedTree(size_t offset, std::pair<std::string,FilterTree > &) const;
+		void copyStashedTree(size_t offset, FilterTree &) const;
 
 		//retrieve all stashed filters
 		void copyStashedTrees(std::vector<std::pair<std::string,FilterTree> > &stashList) const;
 
+		//!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
 		void eraseStash(size_t offset);
 
 		//Return the number of stash elements
 		size_t getStashCount()  const { return stashedTrees.size();}
 
+		//Get the stash name
+		std::string getStashName(size_t offset) const;
 
-		//Push the filter tree undo stack
-		void pushUndoStack();
-
-		//Pop the filter tree undo stack. If restorePopped is true,
-		// then the internal filter tree is updated with the stack tree
-		void popUndoStack(bool restorePopped=true);
-
-		//Pop the redo stack, this unconditionally enforces an update of the
-		// active internal tree
-		void popRedoStack();
-
-		//Obtain the size of the undo stack
-		size_t getUndoSize() const { return undoFilterStack.size();};
-		//obtain the size of the redo stack
-		size_t getRedoSize() const { return redoFilterStack.size();};
-
-		//Clear undo/redo filter tree stacks
-		void clearUndoRedoStacks() { undoFilterStack.clear(); redoFilterStack.clear();}
 		
-		//true if the state has been modified since last load/save.
-		int stateModifyLevel() const { return modifyLevel;};
-
 		//Returns true if there is any data in the stash or the active tree
-		bool hasStateData() const { return (stashedTrees.size() || activeTree.size());}
+		bool hasStateData() const { return (stashedTrees.size() || treeState.size());}
+		//!Returns true if any of the filters (incl. stash)
+		//return a state override (i.e. refer to external entities, such as files)
+		bool hasStateOverrides() const ;
 
 
-		void setAnimationState(const PropertyAnimator &p,const vector<pair<string,size_t> > &animPth) {animationState=p;animationPaths=animPth;}
+		void setAnimationState(const PropertyAnimator &p,const std::vector<std::pair<std::string,size_t> > &animPth) {animationState=p;animationPaths=animPth;}
 		
-		void getAnimationState( PropertyAnimator &p, vector<pair<string,size_t> > &animPth) const; 
+		void getAnimationState( PropertyAnimator &p, std::vector<std::pair<std::string,size_t> > &animPth) const; 
 
-		//TODO: REMOVE ME - needed for viscontrol linkage
-		void setStateModified(int state) const { modifyLevel=state;}
 
 };
-
-
+#endif
diff --git a/src/backend/viscontrol.cpp b/src/backend/viscontrol.cpp
index fbd804f..2c3063e 100644
--- a/src/backend/viscontrol.cpp
+++ b/src/backend/viscontrol.cpp
@@ -1,6 +1,7 @@
 /*
- *	viscontrol.cpp - visualisation-user interface glue code
- *	Copyright (C) 2013, D Haley 
+
+ * 	viscontrol.h - Visualisation control header; "glue" between user interface and scene
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -15,385 +16,65 @@
  *	You should have received a copy of the GNU General Public License
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
-
 #include "viscontrol.h"
 
-#include "wx/wxcommon.h"
-#include "wx/wxcomponents.h"
-#include "wx/propertyGridUpdater.h"
-#include "gl/scene.h"
-
-
+#include <wx/combobox.h>
 
-#include "common/stringFuncs.h"
+#include "wx/propertyGridUpdater.h"
+#include "wx/wxcomponents.h"
+#include "common/voxels.h"
 
 using std::list;
-using std::stack;
+using std::vector;
+using std::pair;
 
-//Window that will be yielded to, when calling safeYield/ needs to be set in viscontrol
-wxWindow *yieldWindow=0;
+bool VisController::isInstantiated = false;
 
-wxStopWatch* delayTime=0;
-//Another global - whether to abort the vis control 
-// operation, or not
-bool *abortVisCtlOp=0;
-
-
-//Number of points to limit to, by default
-const unsigned int DEFAULT_POINT_OUTPUT_LIMIT = 1500000;
-
-//A callback function for yielding the window bound to viscontrol.
-// Calling the callback will only cause a yield if sufficient time has passed
-// the parameter describes whether or not to allow for overriding of the timer
-bool wxYieldCallback(bool forceYield)
+//TODO: Remove me, and refactor filters
+bool dummyRefreshCallback(bool dummy)
 {
-	const unsigned int YIELD_MS=75;
-
-	ASSERT(delayTime);
-	//Rate limit the updates
-	if(delayTime->Time() > YIELD_MS || forceYield)
-	{
-		wxSafeYield(yieldWindow);
-		delayTime->Start();
-	}
-
-	ASSERT(abortVisCtlOp);
-	return !(*abortVisCtlOp);
-}
-
-
-VisController::VisController()
-{
-	targetScene=0;
-	targetPlots=0;
-	targetRawGrid=0;
-
-
-	doProgressAbort=false;
-
-	ASSERT(!abortVisCtlOp);
-	abortVisCtlOp=&doProgressAbort;
-
-	limitIonOutput=DEFAULT_POINT_OUTPUT_LIMIT;
-
-
-	amRefreshing=false;
-	pendingUpdates=false;
-	deferClearPlotVisibility=false;
-	
-	curProg.reset();
-	//Assign global variable its init value
-	ASSERT(!delayTime); //Should not have been inited yet.
-
-	delayTime = new wxStopWatch();
-
-}
-
-VisController::~VisController()
-{
-	ASSERT(delayTime);
-	//delete global variable that visControl is responsible for
-	delete delayTime;
-	delayTime=0;
-}
-
-void VisController::abort()
-{
-	ASSERT(!doProgressAbort);
-	doProgressAbort=true;
-}
-					
-void VisController::addFilter(Filter *f, bool isBase,size_t parentId)
-{ 
-	pushUndoStack();
-	if(!isBase)
-		filterTree.addFilter(f,filterMap[parentId]);
-	else
-		filterTree.addFilter(f,0);
-
-	currentState.setFilterTreeByClone(filterTree);
-
-	//the filter map is now invalid, as we added an element to the tree,
-	//and don't have a unique value for it. We need to relayout.
-	filterMap.clear();
+	return true;
 }
 
-void VisController::addFilterTree(FilterTree &f, bool isBase,size_t parentId)
-{ 
-	ASSERT(!(isBase && parentId==(unsigned int)-1));
-
-	if(isBase)
-		filterTree.addFilterTree(f,0);
-	else
-		filterTree.addFilterTree(f,filterMap[parentId]);
-
-	currentState.setFilterTreeByClone(filterTree);
-	//the filter map is now invalid, as we added an element to the tree,
-	//and don't have a unique value for it. we need to relayout.
-	filterMap.clear();
-}
 
-void VisController::switchoutFilterTree(FilterTree &f)
+RefreshController::RefreshController(TreeState &tC)
 {
-	//Create a clone of the internal tree
-	f=filterTree;
-
-	//Fix up the internal filterMap to reflect the contents of the new tree
-	//---
-	//
-	//Build a map from old filter*->new filter *
-	tree<Filter*>::pre_order_iterator itB;
-	itB=filterTree.depthBegin();
-	std::map<Filter*,Filter*> filterRemap;
-	for(tree<Filter*>::pre_order_iterator itA=f.depthBegin(); itA!=f.depthEnd(); ++itA)
-	{
-		ASSERT(itB != filterTree.depthEnd());
-		filterRemap[*itA]=*itB;	
-		++itB;
-	}
-
-	//Overwrite the internal map
-	for(map<size_t,Filter*>::iterator it=filterMap.begin();it!=filterMap.end();++it)
-		it->second=filterRemap[it->second];
-
-
-	//Swap the internal tree with our clone
-	f.swap(filterTree);
-	
-	currentState.setFilterTreeByClone(filterTree);
+	treeState=&tC;
 }
 
-//!Duplicate a branch of the tree to a new position. Do not copy cache,
-bool VisController::copyFilter(size_t toCopy, size_t newParent,bool copyToRoot) 
+RefreshController::~RefreshController()
 {
-	pushUndoStack();
-	
-	bool ret;
-	if(copyToRoot)
-		ret=filterTree.copyFilter(filterMap[toCopy],0);
-	else
-	
-		ret=filterTree.copyFilter(filterMap[toCopy],filterMap[newParent]);
-
-	currentState.setFilterTreeByClone(filterTree);
-	if(ret)
-	{
-		//Delete the filtermap, as the current data is not valid anymore
-		filterMap.clear();
-	}
-
-	return ret;
 }
 
-
-const Filter* VisController::getFilterById(size_t filterId) const 
+unsigned int RefreshController::refresh()
 {
-	//If triggering this assertion, check that
-	//::updateWxTreeCtrl called after calling addFilterTree.
-	ASSERT(filterMap.size());
-
-	//Check that the mapping exists
-	ASSERT(filterMap.find(filterId)!=filterMap.end());
-	return filterMap.at(filterId);
+	ASSERT(treeState);
+	return treeState->refresh(refreshData,consoleMessages,curProg);
 }
 
-size_t VisController::getPlotID(size_t position) const
-{ 
-	ASSERT(plotMap.size());
-	ASSERT(plotMap.find(position)!=plotMap.end());
-	return plotMap.find(position)->second;
-}
 
-size_t VisController::getIdByFilter(const Filter *f) const 
+bool VisController::stateIsModified(unsigned int minLevel) const
 {
-	for(map<size_t,Filter *>::const_iterator it=filterMap.begin();it!=filterMap.end();++it)
-	{
-		if(it->second == f)
-			return it->first;
-
-	}
-
-	ASSERT(false);
+	return state.hasStateData() && getStateModifyLevel();
 }
 
-void VisController::getFiltersByType(std::vector<const Filter *> &filters, unsigned int type)  const
+void VisController::transferSceneCameraToState()
 {
-	filterTree.getFiltersByType(filters,type);
+	const Camera *c=scene.getActiveCam();
+	state.setCameraByClone(c,state.getActiveCam());
 }
 
-
-void VisController::removeFilterSubtree(size_t filterId)
+void VisController::setCamProperty(size_t offset, unsigned int key, const std::string &value)
 {
-	//Save current filter state to undo stack
-	pushUndoStack();
-       	filterTree.removeSubtree(filterMap[filterId]);
-	currentState.setFilterTreeByClone(filterTree);
-	//Delete the filtermap, as the current data is not valid anymore
-	filterMap.clear();
-}
-
-
-bool VisController::setFilterProperty(size_t filterId, 
-				unsigned int key, const std::string &value, bool &needUpdate)
-{
-	//Save current filter state to undo stack
-	//for the case where the property change is good
-	pushUndoStack();
-	bool setOK;
-	setOK=filterTree.setFilterProperty(filterMap[filterId],key,value,needUpdate);
-
-	if(!setOK)
-	{
-		//Didn't work, so we need to discard the undo
-		//Pop the undo stack, but don't restore it -
-		// restoring would destroy the cache
-		popUndoStack(false);
-	}
-	else
-		currentState.setFilterTreeByClone(filterTree);
-
-	return setOK;
+	state.setCamProperty(offset,key,value);
+	if(offset == state.getActiveCam())
+		scene.setActiveCamByClone(state.getCam(offset));
 }
 
-unsigned int VisController::refreshFilterTree(bool doUpdateScene)
-{
-	ASSERT(!amRefreshing);
-
-	//Analyse the filter tree structure for any errors
-	//--	
-	fta.clear();
-	fta.analyse(filterTree);
-	//--
-
-	doProgressAbort=false;
-	amRefreshing=true;
-	delayTime->Start();
-	
-	curProg.reset();
-	
-	list<FILTER_OUTPUT_DATA > refreshData;
-
-
-	//Apply any remaining updates if we have them
-	if(pendingUpdates)
-		getFilterUpdates();
-	targetScene->clearBindings();
-
-	//Run the tree refresh system.
-	unsigned int errCode;
-	vector<SelectionDevice *> devices;
-	vector<pair<const Filter*, string> > consoleMessages;
-	errCode=filterTree.refreshFilterTree(refreshData,devices,
-			consoleMessages,curProg,wxYieldCallback);
-
-
-	const Filter *lastFilt=0;
-	//Copy the console mesages to the output text box
-	for(size_t ui=0;ui<consoleMessages.size(); ui++)
-	{
-		//If the consol messages are from a new filter
-		if(lastFilt !=consoleMessages[ui].first || !ui)
-		{
-			//If we are at a new filter, which is not the first,
-			//close out the "=" line
-			if(ui)
-				textConsole->AppendText(wxT("============\n\n\n"));
-
-			lastFilt=consoleMessages[ui].first;
-			textConsole->AppendText((lastFilt->getUserString()));
-			textConsole->AppendText(wxT("\n============\n"));
-		}
-		textConsole->AppendText((consoleMessages[ui].second));
-		textConsole->AppendText(wxT("\n"));
-	}
-
-	if(consoleMessages.size())
-	{
-		textConsole->AppendText(wxT("\n============\n"));
-	}
-
-
-	if(errCode)
-	{
-		amRefreshing=false;
-		return errCode;
-	}
-	
-	//strip off the source filter information for
-	//convenience in this routine
-	list<vector<const FilterStreamData *> > outData;
-	for(list<FILTER_OUTPUT_DATA>::iterator it=refreshData.begin(); it!=refreshData.end(); ++it)
-	{
-		ASSERT(it->second.size());
-		outData.push_back(it->second);
-	}
-	
-	
-	if(doUpdateScene)
-		updateScene(outData,devices);
-	else
-	{
-		targetScene->clearObjs();
-		targetScene->clearRefObjs();
-	}
-	
-	//Stop timer
-	delayTime->Pause();
-	amRefreshing=false;
-
-	return 0;
-}
-
-unsigned int VisController::refreshFilterTree(list<FILTER_OUTPUT_DATA> &outData)
-{
-	vector<SelectionDevice *> devices;
-	vector<pair<const Filter *, string> > consoleStrs;
-	return filterTree.refreshFilterTree(outData,devices,
-			consoleStrs,curProg,wxYieldCallback);
-}
-
-void VisController::setScene(Scene *theScene)
-{
-	targetScene=theScene;
-	//Inform scene about vis control.
-	targetScene->setViscontrol(this);
-	
-	Camera *c;
-	c= targetScene->cloneActiveCam();
-	currentState.addCamByClone(c);
-	delete c;
-}
-	
-void VisController::setYieldWindow(wxWindow *newYield)
-{
-	yieldWindow=newYield;
-}
 
 void VisController::setWxTreeFilterViewPersistence(size_t filterId)
 {
-	persistentFilters.push_back(filterMap[filterId]);
-}
-
-void VisController::updateWxTreeCtrl(wxTreeCtrl *t, const Filter *visibleFilt)
-{
-	upWxTreeCtrl(filterTree,t,filterMap,persistentFilters,visibleFilt);
-}
-
-void VisController::updateFilterPropGrid(wxPropertyGrid *g,size_t filterId, const std::string &stateStr) const
-{
-
-	//The filterID can never be set to zero,
-	//except for the root item, as set by
-	//upWxTreeCtrl
-	ASSERT(filterId);
-	ASSERT(filterMap.size() == filterTree.size());
-
-	Filter *targetFilter;
-	targetFilter=filterMap.at(filterId);
-
-	ASSERT(targetFilter);
-	
-	updateFilterPropertyGrid(g,targetFilter,stateStr);
+	persistentFilters.push_back(state.treeState.getFilterById(filterId));
 }
 
 void VisController::updateCameraPropGrid(wxPropertyGrid *g, size_t camId) const
@@ -401,181 +82,63 @@ void VisController::updateCameraPropGrid(wxPropertyGrid *g, size_t camId) const
 	ASSERT(g);
 
 	const Camera *c;
-	c= currentState.getCam(camId);
+	c= state.getCam(camId);
 
 	updateCameraPropertyGrid(g,c);
 }
 
-bool VisController::setCamProperties(size_t camID,unsigned int key, const std::string &value)
-{
-	size_t offset=camID;
-
-	bool result=currentState.setCamProperty(offset,key,value);
-	if(result && offset == currentState.getActiveCam())
-	{
-		const Camera *c;
-		c=currentState.getCam(currentState.getActiveCam());
-		targetScene->setActiveCamByClone(c);
-	}
-
-	return result;
-}
-
-void VisController::getCameraUpdates()
-{
-	const Camera *c=targetScene->getActiveCam();
-	currentState.setCameraByClone(c,currentState.getActiveCam());
-}
-
-bool VisController::hasUpdates() const
-{
-	if(pendingUpdates)
-		return true;
-
-	return filterTree.hasUpdates();
-
-}
-
-void VisController::getFilterUpdates()
+void VisController::updateFilterPropGrid(wxPropertyGrid *g,size_t filterId, const std::string &stateStr) const
 {
-	vector<pair<const Filter *,SelectionBinding> > bindings;
-	targetScene->getModifiedBindings(bindings);
-
-	if(bindings.size())
-		pushUndoStack();
-
-	for(unsigned int ui=0;ui<bindings.size();ui++)
-	{
-#ifdef DEBUG
-		bool haveBind;
-		haveBind=false;
-#endif
-		for(tree<Filter *>::iterator it=filterTree.depthBegin(); 
-				it!=filterTree.depthEnd();++it)
-		{
-			if(*it  == bindings[ui].first)
-			{
-				//We are modifying the contents of
-				//the filter, this could make a change that
-				//modifies output so we need to clear 
-				//all subtree caches to force reprocessing
-
-				filterTree.clearCache(*it,false);
-
-				(*it)->setPropFromBinding(bindings[ui].second);
-#ifdef DEBUG
-				haveBind=true;
-#endif
-				break;
-			}
-		}
-
-		ASSERT(haveBind);
-
-	}
+	ASSERT(g);
 
-	targetScene->resetModifiedBindings();
+	const Filter *targetFilter;
+	targetFilter=state.treeState.getFilterById(filterId);
 
-	currentState.setFilterTreeByClone(filterTree);
-	//we have retrieved the updates.
-	pendingUpdates=false;
+	ASSERT(targetFilter);
+	
+	updateFilterPropertyGrid(g,targetFilter,stateStr);
 }
 
-//public interface to updateScene
-unsigned int VisController::doUpdateScene(list<vector<const FilterStreamData *> > &sceneData, vector<SelectionDevice *> &devices,
-		bool releaseData)
-{
-	amRefreshing=true;
-	unsigned int errCode=updateScene(sceneData,devices,releaseData);
-	amRefreshing=false;
-	return errCode;
 
-}
-
-void VisController::throttleSceneInput(list<vector<const FilterStreamData *> > &sceneData) const
+void VisController::updateScene(RefreshController *r)
 {
-	//Count the number of input ions, as we may need to perform culling,
-	if(!limitIonOutput)
-		return;
-
-	size_t inputIonCount=0;
-	for(list<vector<const FilterStreamData *> >::const_iterator it=sceneData.begin(); 
-							it!=sceneData.end(); ++it)
-		inputIonCount+=numElements(*it,STREAM_TYPE_IONS);
-
-	//If limit is higher than what we have, no culling required
-	if(limitIonOutput >=inputIonCount)
-		return;
+	list<FILTER_OUTPUT_DATA> &sceneData = r->getRefreshData();
+	list< vector<const FilterStreamData *> > dataOnly;
 
-	//Need to cull
-	float cullFraction = (float)limitIonOutput/(float)inputIonCount;
-
-	for(list<vector<const FilterStreamData *> >::iterator it=sceneData.begin(); 
-							it!=sceneData.end(); ++it)
+	for(list<FILTER_OUTPUT_DATA>::iterator it=sceneData.begin(); it!=sceneData.end();++it)
 	{
-		for(unsigned int ui=0;ui<it->size(); ui++)
-		{
-			if((*it)[ui]->getStreamType() != STREAM_TYPE_IONS)
-				continue;
-			//Obtain the ion data pointer
-			const IonStreamData *ionData;
-			ionData=((const IonStreamData *)((*it)[ui]));
-
-
-			//TODO: Is there a way we can treat cached and uncached itmes
-			// differently, without violating const-ness?
-			//Duplicate this object, then forget
-			// about the old one
-			// We can't modify the input, even when uncached,
-			// as the object is const
-			IonStreamData *newIonData;
-			newIonData=ionData->cloneSampled(cullFraction);
-
-			//Prevent leakage due to non-cached-ness
-			if(!ionData->cached)
-				delete ionData;
-
-			(*it)[ui] = newIonData;
-
-		}
-	}
-		
-
-
+		vector<const FilterStreamData *> t;
+		t.resize(it->second.size());
+		std::copy(it->second.begin(),it->second.end(),t.begin());
+		dataOnly.push_back(t);
+	}	
+	
+	updateScene(dataOnly,false);
 }
 
-
-unsigned int VisController::updateScene(list<vector<const FilterStreamData *> > &sceneData, vector<SelectionDevice *> &devices,
+void VisController::updateScene(list<vector<const FilterStreamData *> > &sceneData, 
 				bool releaseData)
 {
-	//Plot wrapper should be set
-	ASSERT(targetPlots);
 	//Plot window should be set
 	ASSERT(plotSelList)
 
-	//Should be called from a viscontrol refresh
-	ASSERT(amRefreshing);
-
 	//Lock the opengl scene interaction,
 	// to prevent user interaction (e.g. devices) during callbacks
-	targetScene->lockInteraction();
-	targetPlots->lockInteraction();
+	scene.lockInteraction();
+	targetPlots.lockInteraction();
 
 	//Buffer to transfer to scene
 	vector<DrawableObj *> sceneDrawables;
 	
-	//erase the contents of each plot 
-	if(deferClearPlotVisibility)
-		deferClearPlotVisibility=false;
-	else
-		targetPlots->clear(true); //Clear, but preserve selection information.
+	targetPlots.clear(true); //Clear, but preserve selection information.
 
 
 	//Names for plots
 	vector<std::pair<size_t,string> > plotLabels;
 
 	//rate-limit the number of drawables to show in the scene
-	throttleSceneInput(sceneData);
+	map<const IonStreamData *, const IonStreamData*> throttleMap;
+	throttleSceneInput(sceneData,throttleMap);
 
 	//-- Build buffer of new objects to send to scene
 	for(list<vector<const FilterStreamData *> > ::iterator it=sceneData.begin(); 
@@ -600,9 +163,16 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 					curIonDraw=new DrawManyPoints;
 
 
-					//Obtain the ion data pointer
+					//Obtain the ion data pointer.
+					// note that we have to use the throttled points
+					// (in throttlemap) if they are there, to prevent
+					// overloading the display system.
 					const IonStreamData *ionData;
 					ionData=((const IonStreamData *)((*it)[ui]));
+					if(throttleMap.find(ionData) != throttleMap.end())
+					{
+						ionData=throttleMap[ionData];
+					}
 
 
 					curIonDraw->resize(ionData->data.size());
@@ -612,7 +182,6 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 					#pragma omp parallel for shared(curIonDraw,ionData)
 					for(size_t ui=0;ui<ionData->data.size();ui++)
 						curIonDraw->setPoint(ui,ionData->data[ui].getPosRef());
-					(*wxYieldCallback)(true);
 					
 					//Set the colour from the ionstream data
 					curIonDraw->setColour(ionData->r,
@@ -623,8 +192,6 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 					curIonDraw->setSize(ionData->ionSize);
 					//Randomly shuffle the ion data before we draw it
 					curIonDraw->shuffle();
-					//Run callback to update as needed, as shuffle is slow.
-					(*wxYieldCallback)(true);
 				
 					//place in special holder for ions,
 					// as we need to accumulate for display-listing
@@ -655,7 +222,7 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 					plotNew->setData(plotData->xyData);
 					plotNew->setLogarithmic(plotData->logarithmic);
 					plotNew->titleAsRawDataLabel=plotData->useDataLabelAsYDescriptor;
-					
+					plotNew->setErrMode(plotData->errDat);
 					//Construct any regions that the plot may have
 					for(unsigned int ui=0;ui<plotData->regions.size();ui++)
 					{
@@ -683,7 +250,7 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 					plotNew->parentObject=plotData->parent;
 					plotNew->parentPlotIndex=plotData->index;
 					
-					plotID=targetPlots->addPlot(plotNew);
+					plotID=targetPlots.addPlot(plotNew);
 
 					plotLabels.push_back(make_pair(plotID,plotData->dataLabel));
 					
@@ -719,14 +286,16 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 						case PLOT_2D_SCATTER:
 						{
 							//Create a 2D plot
-							plotNew= new Plot2DScatter;
-
+							Plot2DScatter *p = new Plot2DScatter;
 							//set the plot info
 							if(plotData->scatterIntensity.size())
-								((Plot2DScatter*)plotNew)->setData(plotData->scatterData,plotData->scatterIntensity);
+								p->setData(plotData->scatterData,plotData->scatterIntensity);
 							else
-								((Plot2DScatter*)plotNew)->setData(plotData->scatterData);
-							//FIXME: scatter intesity data??
+								p->setData(plotData->scatterData);
+
+							p->scatterIntensityLog=plotData->scatterIntensityLog;
+
+							plotNew=p;				
 							break;
 						}
 						default:
@@ -740,7 +309,7 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 					plotNew->parentObject=plotData->parent;
 					plotNew->parentPlotIndex=plotData->index;
 					
-					plotID=targetPlots->addPlot(plotNew);
+					plotID=targetPlots.addPlot(plotNew);
 					
 					// -----
 
@@ -792,9 +361,9 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 
 					//Make a copy if cached; otherwise just steal it.
 					if(vSrc->cached)
-						vSrc->data.clone(*v);
+						vSrc->data->clone(*v);
 					else
-						v->swap(vSrc->data);
+						v->swap(*(vSrc->data));
 
 					switch(vSrc->representationType)
 					{
@@ -843,12 +412,18 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 				(*it)[ui]=0;	
 			}
 			
-			//Run the callback to update the window as needed
-			(*wxYieldCallback)(true);
 
 		}
 			
 	}
+
+	//Free the rate-limited points
+	for(map<const IonStreamData *, const IonStreamData*>::iterator it =throttleMap.begin();
+		it!=throttleMap.end();++it)
+	{
+		delete it->second;
+	}
+	throttleMap.clear();	
 	//---
 
 	//Construct an OpenGL display list from the dataset
@@ -883,7 +458,7 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 	{
 		//Otherwise try to use the last visibility information
 		//to set the selection
-		targetPlots->bestEffortRestoreVisibility();
+		targetPlots.bestEffortRestoreVisibility();
 
 	}
 
@@ -900,17 +475,17 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 			//Retrieve the uniqueID
 			unsigned int plotID;
 			plotID=plotMap[ui];
-			if(targetPlots->isPlotVisible(plotID))
+			if(targetPlots.isPlotVisible(plotID))
 				plotSelList->SetSelection(ui);
 		}
 	}
-	targetPlots->lockInteraction(false);
+	targetPlots.lockInteraction(false);
 	//-----------
 		
 
 	
-	targetScene->clearObjs();
-	targetScene->clearRefObjs();
+	scene.clearObjs();
+	scene.clearRefObjs();
 
 
 
@@ -955,13 +530,13 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 				delete drawIons[ui];
 			}
 			else
-				targetScene->addDrawable(drawIons[ui]);
+				scene.addDrawable(drawIons[ui]);
 		}
 
 		if(listStarted)	
 		{
 			displayList->endList();
-			targetScene->addDrawable(displayList);
+			scene.addDrawable(displayList);
 		}
 		else
 			delete displayList;
@@ -969,412 +544,65 @@ unsigned int VisController::updateScene(list<vector<const FilterStreamData *> >
 	else
 	{
 		for(unsigned int ui=0;ui<drawIons.size(); ui++)
-			targetScene->addDrawable(drawIons[ui]);
+			scene.addDrawable(drawIons[ui]);
 	}
 
 	//add all drawable objects (not ions)	
 	for(size_t ui=0;ui<sceneDrawables.size();ui++)
-		targetScene->addDrawable(sceneDrawables[ui]);
+		scene.addDrawable(sceneDrawables[ui]);
 	
 	sceneDrawables.clear();
-	targetScene->computeSceneLimits();
-	targetScene->addSelectionDevices(devices);
-	targetScene->lockInteraction(false);
+	scene.computeSceneLimits();
+	scene.lockInteraction(false);
 	//===============
-
-
-
-	return 0;
 }
 
-
-
-
-unsigned int VisController::addCam(const std::string &camName)
+void VisController::throttleSceneInput(list<vector<const FilterStreamData *> > &sceneData,
+		std::map<const IonStreamData *,const IonStreamData *> &throttleMap) const
 {
-	//Duplicate the current camera, and give it a new name
-	Camera *c=targetScene->cloneActiveCam();
-	c->setUserString(camName);
-
-	//create an ID for this camera, then add to scene 
-	size_t id =currentState.getNumCams();
-	
-	currentState.addCamByClone(c);
-	delete c;
-	
-	return id;
-}
-
-bool VisController::removeCam(unsigned int offset)
-{
-	//obtain the offset to the camera from its unique id
-	currentState.removeCam(offset);
-
-	if(!currentState.getNumCams())
-	{
-		const Camera *c;
-		c = targetScene->getActiveCam();
-		currentState.addCamByClone(c);
-	}
-	else
-	{
-		size_t activeCam=currentState.getActiveCam();
-		targetScene->setActiveCamByClone(currentState.getCam(activeCam) );
-	}
-
-	return true;
-}
-
-bool VisController::setCam(unsigned int offset)
-{
-	ASSERT(offset < currentState.getNumCams());
-	
-	currentState.setActiveCam(offset);
-	targetScene->setActiveCamByClone(currentState.getCam(offset));
-	
-	return true;
-}
-
-bool VisController::reparentFilter(size_t filter, size_t newParent)
-{
-	//Save current filter state to undo stack
-	pushUndoStack();
-
-	//Try to reparent this filter. It might not work, if, for example
-	// the new parent is actually a child of the filter we are trying to
-	// assign the parent to. 
-	if(!filterTree.reparentFilter(filterMap[filter],filterMap[newParent]))
-	{
-		//Pop the undo stack, to reverse our 
-		//push, but don't restore it,
-		// as this would cost us our filter caches
-		popUndoStack(false);
-		return false;
-	}
-	
-	currentState.setFilterTreeByClone(filterTree);
-	return true;
-}
-
-
-bool VisController::setFilterString(size_t id,const std::string &s)
-{
-	
-	Filter *p=(Filter *)getFilterById(id);
-
-	if(s != p->getUserString())
-	{
-		//Save current filter state to undo stack
-		pushUndoStack();
-		
-		//Do the actual update
-		p->setUserString(s);
-		
-		//Update the current state	
-		currentState.setFilterTreeByClone(filterTree);
-		return true;
-	}
-
-	return false;
-}
-
-unsigned int VisController::numCams() const 
-{
-	return currentState.getNumCams();
-}
-		
-void VisController::ensureSceneVisible(unsigned int direction)
-{
-	currentState.setStateModified(true);
-	targetScene->ensureVisible(direction);
-}
-
-
-bool VisController::saveState(const char *cpFilename, std::map<string,string> &fileMapping,
-		bool writePackage,bool resetModifyLevel) const
-{
-	AnalysisState state;
-
-
-	//Make a copy of the state, as some variables are still stored in viscontrol's scope
-	state=currentState; 
-	
-	
-	//-- scene variables --
-	float rBack,gBack,bBack;
-	targetScene->getBackgroundColour(rBack,gBack,bBack);
-	state.setBackgroundColour(rBack,gBack,bBack);
-       	
-	state.setWorldAxisMode(targetScene->getWorldAxisVisible());
-
-	vector<const Effect *> effectVec;
-	
-	targetScene->getEffects(effectVec);
-	state.setEffectsByCopy(effectVec);
-	//------
-
-	//Plotting variables
-	//------------------
-	//TODO: Migrate plot status out of viscontrol
-	// and into state
-	state.setPlotLegend(targetPlots->getLegendVisible());
-
-	{
-	vector<unsigned int> visiblePlotIDs;
-	targetPlots->getVisibleIDs(visiblePlotIDs);
-	
-	//Obtain filter pointer -> serialised name data
-	map<const Filter *,string> serialisedFilterNames;
-	filterTree.serialiseToStringPaths(serialisedFilterNames);
-
-	vector<pair<string, unsigned int> > visiblePlotNames;
-	visiblePlotNames.resize(visiblePlotIDs.size());
-
-	for(size_t ui=0;ui<visiblePlotIDs.size (); ui++)
-	{
-		unsigned int thisID;
-		thisID= visiblePlotIDs[ui];
-
-		const Filter *f;
-		f=(const Filter *)targetPlots->getParent(thisID);
-	
-		ASSERT(serialisedFilterNames.find(f) != serialisedFilterNames.end());
-		visiblePlotNames[ui] = make_pair(
-			serialisedFilterNames[f], targetPlots->getParentIndex(thisID));
-	}
-
-	state.setEnabledPlots(visiblePlotNames);
-	}
-	//------------------
-
-	//-- viscontrol variables
-	state.setFilterTreeByClone(filterTree);
-	//--
-
-	if(resetModifyLevel)
-		currentState.setStateModified(false);
-
-
-	return state.save(cpFilename,fileMapping,writePackage);
-}
+	//Count the number of input ions, as we may need to perform culling,
+	if(!limitIonOutput)
+		return;
 
-bool VisController::loadState(const char *cpFilename, std::ostream &errStream, bool merge,bool noUpdating) 
-{
+	size_t inputIonCount=0;
+	for(list<vector<const FilterStreamData *> >::const_iterator it=sceneData.begin(); 
+							it!=sceneData.end(); ++it)
+		inputIonCount+=numElements(*it,STREAM_TYPE_IONS);
 
-	//Load into a temporary state
-	// and if successful, transfer to full state
-	AnalysisState tmpState;
-	bool result=tmpState.load(cpFilename,errStream);
+	//If limit is higher than what we have, no culling required
+	if(limitIonOutput >=inputIonCount)
+		return;
 
-	if(!result)
-		return false;
-	
-	if(merge)
-	{
-		currentState.merge(tmpState);
-	}
-	else
-	{
-		currentState=tmpState;
-	}
-	//Synchronise scene and viscontrol components to 
-	// current state
+	//Need to cull
+	float cullFraction = (float)limitIonOutput/(float)inputIonCount;
 
-	//Grab the filter tree from the state
-	currentState.copyFilterTree(filterTree);
-	
-	if(!noUpdating)
+	for(list<vector<const FilterStreamData *> >::iterator it=sceneData.begin(); 
+							it!=sceneData.end(); ++it)
 	{
-		// -- Set scene options --
-		float rBack,gBack,bBack;
-		currentState.getBackgroundColour(rBack,gBack,bBack);
-		targetScene->setBackgroundColour(rBack,gBack,bBack);
-
-		targetScene->setWorldAxisVisible(currentState.getWorldAxisMode());
-
-		vector<const Camera *> cams;
-		currentState.copyCamsByRef(cams);
-
-		if(cams.size())
-		{
-			size_t activeCam=currentState.getActiveCam();
-			targetScene->setActiveCamByClone(cams[activeCam]);
-		}
-
-		vector<Effect*> e;
-		currentState.copyEffects(e);
-		targetScene->setEffectVec(e);
-		//----
-
-		//Conver the enabled plots to underlying
-		// pointer representation, then pass to 
-		// plot functions, so it can enable plots
-		// after refresh
-		vector<pair<string, unsigned int> > enabledPlotsPath;
-		currentState.getEnabledPlots(enabledPlotsPath);
-
-		map<string,const Filter *> pathMap;
-		filterTree.serialiseToStringPaths(pathMap);	
-		
-		vector<pair<const void *, unsigned int> > enabledPlotsPtr;
-		for(unsigned int ui=0;ui<enabledPlotsPath.size();ui++)
+		for(unsigned int ui=0;ui<it->size(); ui++)
 		{
-			std::string curPath;
-			curPath=enabledPlotsPath[ui].first;
-			//Check to see if the filter tree we loaded
-			// has the same info as the selected item
-			if(pathMap.find(curPath)!=pathMap.end())
-			{
-		
-				enabledPlotsPtr.push_back(
-					make_pair((void *)pathMap[curPath],enabledPlotsPath[ui].second));
-			}
-		}
-
-		//override the target plots internal rep. of what is visible,
-		// so that at next refresh it will pick this up and auto-select the plots,
-		// if it caun
-		targetPlots->overrideLastVisible(enabledPlotsPtr); 
-		deferClearPlotVisibility=true;
-	}
-
-
-
-
-	fta.clear();
-
-	filterTree.initFilterTree();
-		
-	//Try to restore the working directory as needed
-	std::string wd;
-	wd=currentState.getWorkingDir();
-	if(wd.size() && wxDirExists((wd)))
-		wxSetWorkingDirectory((currentState.getWorkingDir()));
-
-	currentState.setStateModified(false);
-	return true;
-}
-
-void VisController::clear()
-{
-	filterMap.clear();
-	filterTree.clear();
-	fta.clear();
-
-	currentState.setFilterTreeByClone(filterTree);
-	currentState.clearUndoRedoStacks();
-}
-
-void VisController::getCamData(std::vector<std::string>  &camData) const
-{
-	vector<const Camera *> camRefs;
-	currentState.copyCamsByRef(camRefs);	
-	
-	camData.resize(camRefs.size());
-	for(size_t ui=0;ui<camRefs.size();ui++)
-	{
-		camData[ui]=camRefs[ui]->getUserString();
-	}
-}
-
-unsigned int VisController::getActiveCamId() const
-{ 
-	return currentState.getActiveCam();
-}
-
-unsigned int VisController::exportIonStreams(const std::vector<const FilterStreamData * > &selectedStreams,
-		const std::string &outFile, unsigned int format)
-{
+			if((*it)[ui]->getStreamType() != STREAM_TYPE_IONS)
+				continue;
+			//Obtain the ion data pointer
+			const IonStreamData *ionData;
+			ionData=((const IonStreamData *)((*it)[ui]));
 
-	//test file open, and truncate file to zero bytes
-	ofstream f(outFile.c_str(),ios::trunc);
-	
-	if(!f)
-		return 1;
 
-	f.close();
+			//Duplicate this object, then forget
+			// about the old one. The freeing will be done by
+			//the refresh thread as needed, so don't free here.
 
-	for(unsigned int ui=0; ui<selectedStreams.size(); ui++)
-	{
-		switch(selectedStreams[ui]->getStreamType())
-		{
-			case STREAM_TYPE_IONS:
-			{
-				const IonStreamData *ionData;
-				ionData=((const IonStreamData *)(selectedStreams[ui]));
-				switch(format)
-				{
-					case IONFORMAT_POS:
-					{
-						//Append this ion stream to the posfile
-						IonHit::appendPos(ionData->data,outFile.c_str());
+			// We can't modify the input, even when uncached,
+			// as the object is const
+			IonStreamData *newIonData;
+			newIonData=ionData->cloneSampled(cullFraction);
+			throttleMap[ionData]  = newIonData;
 
-						break;
-					}
-					default:
-						ASSERT(false);
-						break;
-				}
-			}
 		}
 	}
+		
 
-	return 0;
-}
-
-
-void VisController::addStashedToFilters(const Filter *parentFilter, unsigned int stashOffset)
-{
-	//Retrieve the specified stash
-	pair<string,FilterTree> f;
-
-	currentState.copyStashedTree(stashOffset,f);
-
-	filterTree.addFilterTree(f.second,parentFilter);
-	currentState.setFilterTreeByClone(filterTree);
-
-	//Save current filter state to undo stack
-	pushUndoStack();
-
-	
-	filterTree.initFilterTree();
-}
-
-void VisController::eraseStash(unsigned int stashPos)
-{
-	//Remove viscontrol's mapping to this
-	currentState.eraseStash(stashPos);
-}
-
-unsigned int VisController::stashFilters(unsigned int filterId, const char *stashName)
-{
-	Filter *target = filterMap[filterId];
-	
-	FilterTree fTree;
-	filterTree.cloneSubtree(fTree,target);
-
-	currentState.addStashedTree(std::make_pair(string(stashName),fTree));
-	return currentState.getStashCount()-1; 
-}
-
-void VisController::getStashTree(unsigned int stashPos, FilterTree &f) const
-{
-	pair<string,FilterTree > stash;
-	currentState.copyStashedTree(stashPos,stash);
-	f=stash.second;
-}
-
-//TDOO: Drop pairm and just use string
-void VisController::getStashes(std::vector<std::pair<std::string,unsigned int > > &stashList) const
-{
-	ASSERT(stashList.empty()); // should be empty	
-
-	vector<pair<string,FilterTree> > tmp;
-	currentState.copyStashedTrees(tmp);
-	stashList.resize(tmp.size());
 
-	for(size_t ui=0;ui<tmp.size();ui++)
-		stashList[ui]= std::make_pair(tmp[ui].first,ui);
 }
 
 void VisController::updateRawGrid() const
@@ -1382,7 +610,7 @@ void VisController::updateRawGrid() const
 	vector<vector<vector<float> > > plotData;
 	vector<std::vector<std::string> > labels;
 	//grab the data for the currently visible plots
-	targetPlots->getRawData(plotData,labels);
+	targetPlots.getRawData(plotData,labels);
 
 
 
@@ -1428,119 +656,68 @@ void VisController::updateRawGrid() const
 	}
 }
 
-void VisController::setCachePercent(unsigned int newCache)
+void VisController::updateWxTreeCtrl(wxTreeCtrl *t, const Filter *visibleFilt)
 {
-	filterTree.setCachePercent(newCache);
-}
 
+	map<size_t,Filter *> filterMap; 
+	upWxTreeCtrl(state.treeState.getTreeRef(),t,	
+			filterMap,persistentFilters,visibleFilt);
 
-void VisController::pushUndoStack()
-{
-	currentState.pushUndoStack();
+	cerr << "Rebuilt filter map" <<endl;
+	state.treeState.swapFilterMap(filterMap);
 }
 
-void VisController::popUndoStack(bool restorePopped)
+
+void VisController::updateStashComboBox(wxComboBox *comboStash) const
 {
-	currentState.popUndoStack(restorePopped);
+	comboStash->Clear();
 
-	if(restorePopped)
+	unsigned int nStashes = state.getStashCount();	
+	for(unsigned int ui=0;ui<nStashes; ui++)
 	{
-		currentState.copyFilterTree(filterTree);
-		filterTree.initFilterTree();
+		wxListUint *u;
+		u = new wxListUint(ui);
+		std::string stashName;
+		stashName=state.getStashName(ui);
+		comboStash->Append(stashName,(wxClientData *)u);
+		ASSERT(comboStash->GetClientObject(comboStash->GetCount()-1));
 	}
 }
 
-void VisController::popRedoStack()
-{
-	currentState.popRedoStack();
-	currentState.copyFilterTree(filterTree);
-	
-	filterTree.initFilterTree();
-}
-
-void VisController::updateConsole(const std::vector<std::string> &v, const Filter *f) const
+void VisController::updateCameraComboBox(wxComboBox *comboCamera) const
 {
-	for(unsigned int ui=0; ui<v.size();ui++)
+	//Update the camera dropdown
+	comboCamera->Clear();
+	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++)
 	{
-		std::string s;
-		s = f->getUserString() + string(" : ") + v[ui];
-		textConsole->AppendText((s));
-		textConsole->AppendText(wxT("\n"));
-
+		std::string camName;
+		camName = state.getCamName(ui);
+		ASSERT(camName.size());
+		//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(ui == state.getActiveCam())
+			comboCamera->SetSelection(ui-1);
 	}
-}
-
-
-bool VisController::getAxisVisible() const
-{
-	return targetScene->getWorldAxisVisible();
-}
-
 
-void VisController::setStrongRandom(bool strongRand)
-{
-	Filter::setStrongRandom(strongRand);
-
-	//Invalidate every filter cache.
-	filterTree.clearCache(0);
-
-}
-
-
-void VisController::setEffects(bool enable)
-{
-	targetScene->setEffects(enable);
 }
 
-void VisController::setAnimationState(const PropertyAnimator &p,
-		const std::vector<pair<string,size_t> > &pathMapping) 
-{
-	//Add animation state saving
-	currentState.setAnimationState(p,pathMapping);
-}
-
-void VisController::getAnimationState(PropertyAnimator &p, std::vector<pair<string,size_t> > &pathMapping) const
-{
-	currentState.getAnimationState(p,pathMapping);
+size_t VisController::getPlotID(size_t position) const
+{ 
+	ASSERT(plotMap.size());
+	ASSERT(plotMap.find(position)!=plotMap.end());
+	return plotMap.find(position)->second;
 }
 
-bool VisController::hasHazardousContents() const
-{
-	//Filters can contain hazardous content - that is 
-	// content that can come from untrusted sources, and be used
-	// in a nefarious way. Check to see if any filter that
-	// could potentially be used in such a manner exists in our
-	// current state
-
-	//Check the stashed state
-	vector<pair<string, FilterTree > > stashedFilters;
-
-	currentState.copyStashedTrees(stashedFilters);
-	for(size_t ui=0;ui<stashedFilters.size();ui++)
-	{
-		if(stashedFilters[ui].second.hasHazardousContents())
-			return true;
-	}
-
-	//Check the active filter tree
-	FilterTree f;
-	currentState.copyFilterTree(f);	
-	return f.hasHazardousContents();
-}
 
-bool VisController::filterIsPureDataSource(size_t filterId) const
+void VisController::setActiveCam(unsigned int newActive) 
 {
-	ASSERT(filterId); //root node, as set by upWxTreeCtrl uses 0.
-	ASSERT(filterMap.find(filterId)!=filterMap.end()); //needs to exist
-
-	return filterMap.at(filterId)->isPureDataSource();
+	//set the active camera in state, then clone it to the scene
+	state.setActiveCam(newActive);
 	
+	scene.setActiveCam(state.getCam(newActive)->clone());
 }
-
-void VisController:: safeDeleteFilterList(std::list<FILTER_OUTPUT_DATA> &outData, 
-								size_t typeMask, bool maskPrevents) const
-{
-	filterTree.safeDeleteFilterList(outData,typeMask,maskPrevents);
-}
-//---------------
-
diff --git a/src/backend/viscontrol.h b/src/backend/viscontrol.h
index 107770a..9770ce2 100644
--- a/src/backend/viscontrol.h
+++ b/src/backend/viscontrol.h
@@ -1,6 +1,7 @@
 /*
+
  * 	viscontrol.h - Visualisation control header; "glue" between user interface and scene
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -21,22 +22,48 @@
 
 #include <wx/listbox.h>
 #include <wx/textctrl.h>
+#include <wx/grid.h>
 
 class VisController;
 class wxGrid;
 class wxTreeCtrl;
 
-class Scene;
-
-#include "filtertreeAnalyse.h"
-#include "backend/plot.h"
-#include "backend/animator.h"
 #include "state.h"
+#include "filtertreeAnalyse.h"
 #include "filter.h"
+#include "gl/scene.h"
+#include "backend/plot.h"
+
+#include <list>
+
+//!Tree controller
+
+class RefreshController
+{
+	private:
+		TreeState *treeState;
+		//!Results of last refresh
+		std::list<FILTER_OUTPUT_DATA> refreshData;
+		std::vector<std::pair<const Filter*, std::string> > consoleMessages;
+	public:
+
+		//Initialisation requires treecontroller
+		RefreshController(TreeState &treeState);
+		~RefreshController();
+		//!Current progress
+		ProgressData curProg;
 
-#include "backend/APT/APTFileIO.h"
+		//!Refresh the tree-control's tree, and return error code
+		// returns 0 on success, nonzero on failure (see TreeState::refreshFilterTree)
+		unsigned int refresh();
 
 
+		std::list<FILTER_OUTPUT_DATA> &getRefreshData() { return refreshData;};
+		std::vector<std::pair<const Filter*, std::string> > &getConsoleMessages() { return consoleMessages;};
+	
+
+};
+
 //!Visualisation controller
 /*!
  * Keeps track of what visualisation controls the user has available
@@ -44,15 +71,14 @@ class Scene;
  * This is essentially responsible for interfacing between program
  * data structures and the user interface.
  *
- * Only one of these should be instantiated at any time (such as due to abort mechanisms).
+ * Only one of these should be instantiated at any time .
  */
 class VisController
 {
 	private:
-		//!Target scene
-		Scene *targetScene;
+		static bool isInstantiated;
 		//!Target Plot wrapper system
-		PlotWrapper *targetPlots;
+		PlotWrapper targetPlots;
 		//!Target raw grid
 		wxGrid *targetRawGrid;
 
@@ -63,366 +89,55 @@ class VisController
 		wxListBox *plotSelList;
 
 
-
-
-		//--- Data storage ----
-		AnalysisState currentState;
-		
-		//TODO: Migrate to analysis state
-		//!Primary data storage for filter tree
-		FilterTree filterTree;
-	
-		//!Analysis results for last filter tree refresh
-		FilterTreeAnalyse fta;
-
-		//--------------------
-
-		
-		//!True if viscontrol should abort current operation
-		bool doProgressAbort;
-	
-		//!True if viscontrol is in the middle of a refresh operation
-		bool amRefreshing;
-
-		//!True if there are pending updates from the user
-		bool pendingUpdates;
-
-		//!Current progress
-		ProgressData curProg;
-
 		//!Maximum number of ions to pass to scene
 		size_t limitIonOutput;
 
-		//TODO: Move plot visbility data into state file, 
-		// thus obviating the need to do this
-		//!Should we defer altering plot visibility during refresh
-		// this is used when loading a state file to prevent from bringing older selection state into current plots
-		bool deferClearPlotVisibility;
-
-	
-
-		//!Retreive the updates to the filter tree from the scene
-		void getFilterUpdates();
-
-
-
-		//!Push the current filter tree onto the undo stack
-		void pushUndoStack(); 
-
-
-		//!Erase the redo stack
-		void clearRedoStack();
-	
-		
-
-		//!Update the console strings
-		void updateConsole(const std::vector<std::string> &v, const Filter *f) const;
-
-		
-		//!Limit the number of objects we are sending to the scene
-		void throttleSceneInput(std::list<vector<const FilterStreamData *> > &outputData) const;
-
-		//!Force an update to the scene. 
-		unsigned int updateScene(std::list<vector<const FilterStreamData *> > &outputData,
-					std::vector<SelectionDevice *> &devices,bool releaseData=true);
-		
-		//!ID handler that assigns each filter its own ID that
-		// is guaranteed to be unique for the life of the filter in the filterTree	
-		std::map<size_t, Filter * > filterMap;		
-		
 		//Filters that should be able to be seen next time we show
 		// the wxTree control
 		std::vector<const Filter *> persistentFilters;
 
-		//Map plot position to ID
+		//Map plot position to ID. TODO: Remove me
 		std::map<size_t, size_t> plotMap;
 
 
-	public:
-		VisController();
-		~VisController();
-
-		//Filter tree access functions
-		//-----------------
-		//!Run a refresh of the underlying tree. Returns 0 on success
-		// iff return value == 0, then scene update has been initiated
-		// otherwise returns error code value for filter, pr
-		// one of the "higher" value error code, ( >=FILTERTREE_REFRESH_ERR_MEM);
-		unsigned int refreshFilterTree(bool doUpdateScene=true);
-
-		
-		//!Force an update to the scene. 
-		unsigned int doUpdateScene(std::list<vector<const FilterStreamData *> > &outputData, 
-					std::vector<SelectionDevice *> &devices,bool releaseData=true);
-
-		//obtain the outputs from the filter tree's refresh. 
-		// The outputs *must* be deleted with safeDeleteFilterList
-		unsigned int refreshFilterTree(
-			std::list<std::pair<Filter *, 
-				std::vector<const FilterStreamData * > > > &outputData); 
-
-		//Obtain a clone of the active filter tree
-		void cloneFilterTree(FilterTree &f) const{f=filterTree;};
-
-		//!Safely delete data generated by refreshFilterTree(...). 
-		//a mask can be used to *prevent* STREAM_TYPE_blah from being deleted. Deleted items are removed from the list.
-		void safeDeleteFilterList(std::list<FILTER_OUTPUT_DATA> &outData, 
-					size_t typeMask=0, bool maskPrevents=true) const;
-
-
-		//!Add a new filter to the tree. Set isbase=false and parentID for not
-		//setting a parent (ie making filter base)
-		void addFilter(Filter *f, bool isBase, size_t parentId);
-		
-		
-		//!Add a new subtree to the tree. Note that the tree will be cleared
-		// as a result of this operation. Control of all pointers will be handled internally.
-		// If you wish to use ::getFilterById you *must* rebuild the tree control with
-		// ::updateWxTreeCtrl
-		void addFilterTree(FilterTree &f,bool isBase=true, 
-						size_t parentId=(unsigned int)-1); 
-
-		//!Grab the filter tree from the internal one, and swap the 
-		// internal with a cloned copy of the internal.
-		// Can be used eg, to steal the cache
-		// Note that the contents of the incoming filter tree will be destroyed.
-		//  -> This implies the tree comes *OUT* of viscontrol,
-		//     and a tree  cannot be inserted in via this function
-		void switchoutFilterTree(FilterTree &f);
-
-		//Perform a swap operation on the filter tree. 
-		// - *must* have same topology, or you must call updateWxTreeCtrl
-		// - can be used to *insert* a tree into this function
-		void swapFilterTree(FilterTree &f) { f.swap(filterTree);}
-
-		//!Duplicate a branch of the tree to a new position. Do not copy cache,
-		bool copyFilter(size_t toCopy, size_t newParent,bool copyToRoot=false) ;
-
-		//TODO: Deprecate me - filter information should not be leaking like this!
-		//Get the ID of the filter from its actual pointer
-		size_t getIdByFilter(const Filter* f) const;
-
-		const Filter* getFilterById(size_t filterId) const; 
-
-		//!Return all of a given type of filter from the filter tree. Type must be the exact type of filter - it is not a mask
-		void getFiltersByType(std::vector<const Filter *> &filters, unsigned int type)  const;
-
-		//!Returns true if the tree contains any state overrides (external entity referrals)
-		bool hasStateOverrides() const 
-				{return filterTree.hasStateOverrides();};
-
-		//!Make the filters safe for the end user, assuming the filter tree could have had
-		// its data initialised from anywhere
-		void stripHazardousContents() { filterTree.stripHazardousContents();};
-
-		//!Get the analysis results for the last refresh
-		void getAnalysisResults(vector<FILTERTREE_ERR> &res) const { fta.getAnalysisResults(res);}
-
-		//!Return the number of filters currently in the main tree
-		size_t numFilters() const { return filterTree.size();};
-
-		//!Clear the cache for the filters
-		void purgeFilterCache() { filterTree.purgeCache();};
-
-		//!Delete a filter and all its children
-		void removeFilterSubtree(size_t filterId);
-
-		//Move a filter from one part of the tree to another
-		bool reparentFilter(size_t filterID, size_t newParentID);
-
-		//!Set the properties using a key-value result 
-		/*
-		 * The return code tells whether to reject or accept the change. 
-		 * need update tells us if the change to the filter resulted in a change to the scene
-		 */
-		bool setFilterProperty(size_t filterId,unsigned int key,
-				const std::string &value, bool &needUpdate);
-	
-		//!Set the filter's string	
-		bool setFilterString(size_t id, const std::string &s);
-		
-		//!Clear all caches
-		void clearCache();
-		
-		//!Clear all caches
-		void clearCacheByType(unsigned int type) { filterTree.clearCacheByType(type);};
 
-		//Overwrite the contents of the pointed-to range files with
-		// the map contents
-		void modifyRangeFiles(const map<const RangeFile *, const RangeFile *> &toModify) { filterTree.modifyRangeFiles(toModify);};
 
-		//-----------------
-
-
-		//!Call to get viscontrol to abort current operation. Call once per abort.
-		void abort()  ;
-
-		//!Call to set window to be partially excluded (wx dependant) from blocking during scene updates
-		void setYieldWindow(wxWindow *win);
-
-		//!Set the text console
-		void setConsole(wxTextCtrl *t) { textConsole = t;}
-		//!Set the backend scene
-		void setScene(Scene *theScene);
-		//!Set the backend plot
-		void setPlotWrapper(PlotWrapper *thePlots){targetPlots=thePlots;};
-			
-		PlotWrapper *getPlotWrapper(){return targetPlots;};
-		//!Set the listbox for plot selection
-		void setPlotList(wxListBox *box){plotSelList=box;};
-		
-		
-		//!Set the backend grid control for raw data
-		void setRawGrid(wxGrid *theRawGrid){targetRawGrid=theRawGrid;};
+		//!Update the console strings
+		void updateConsole(const std::vector<std::string> &v, const Filter *f) const;
+		//!Limit the number of objects we are sending to the scene
+		void throttleSceneInput(std::list<std::vector<const FilterStreamData *> > &outputData, 
+			std::map<const IonStreamData *, const IonStreamData *> &throttleMap) const;
+	public:
+		AnalysisState state;
+		Scene scene;
 
-		//Get a plot ID from the listbox position
-		size_t getPlotID(size_t position) const ;
+		VisController() {ASSERT(!isInstantiated); isInstantiated=true; scene.setVisControl(this);}; 
 	
-		//!Write out the filters into a wxtreecontrol.
-		// optional argument is the fitler to keep visible in the control
-		void updateWxTreeCtrl(wxTreeCtrl *t,const Filter *f=0);
-		//!Update a wxPropertyGrid with the properties for a given filter
-		void updateFilterPropGrid(wxPropertyGrid *g,size_t filterId, const std::string &stateString="") const; 
-		//!Update a wxPropertyGrid with the properties for a given filter
-		void updateCameraPropGrid(wxPropertyGrid *g,size_t cameraId) const; 
-		
-		//!Set the camera to use in the scene
-		bool setCam(unsigned int uniqueID) ;
-
-		//!Remove a camera from the scene
-		bool removeCam(unsigned int uniqueID);
-
-		//!Add a new camera to the scene
-		unsigned int addCam(const std::string &camName);
-
-		//!Return the number of cameras
-		unsigned int numCams() const ;
-
-		//!Set the properties using a key-value result 
-		/*! The return code tells whether to reject or accept the change. 
-		 */
-		bool setCamProperties(size_t camId, 
-				unsigned int key, const std::string &value);
-
-
-		//!Ensure visible
-		void ensureSceneVisible(unsigned int direction);
+		void setActiveCam(unsigned int cam);
 
+		//Returns true if current state has been modified since last save 
+		bool stateIsModified(unsigned int minLevel = STATE_MODIFIED_ANCILLARY) const;
 	
-
-		//!Return the current working directory for when loading/saving state file contents
-		std::string getWorkDir() const { return currentState.getWorkingDir();};
-		
-		//!Set current working dir used when saving state files
-		void setWorkDir(const std::string &wd) { currentState.setWorkingDir(wd);};
-
-		
-		//!Save the viscontrol state: writes an XML file containing the viscontrol state
-		bool saveState(const char *filename, std::map<string,string> &fileMapping,
-					bool writePackage=false, bool resetModifyLevel=true) const;
-		
-		//!Save the viscontrol "package":this  writes an XML file containing the viscontrol state, altering the output of the filters to obtain the files it needs 
-		bool savePackage(const char *filename) const;
-
-		//!Load the viscontrol state, optionally merging this tree with the currently loaded tree. Also, as an option, we can bypass updating any UI data, for debug purposes	
-		bool loadState(const char *filename, std::ostream &f,bool merge=false,bool noUpdating=false);	
-
-		//!Are we currently using relative paths due to a previous load?
-		bool usingRelPaths() const { return currentState.getUseRelPaths();};
-
-		//!Set whether to use relative paths when saving
-		void setUseRelPaths(bool useRelPaths){ currentState.setUseRelPaths(useRelPaths);};
-
-		//!Get the camera ID-pair data TODO: this is kinda a halfway house between
-		//updating the camera data internally and passing in the dialog box and not
-		//should homogenise this...
-		void getCamData(std::vector<std::string> &camData) const;
-
-		//!Get the active camera ID
-		unsigned int getActiveCamId() const;
-
-		//Obtain updated camera from the scene and then commit it to the state
-		void getCameraUpdates();
-
 		//Set the maximum number of ions to allow the scene to display
 		void setIonDisplayLimit(size_t newLimit) { limitIonOutput=newLimit;}
 
 		size_t getIonDisplayLimit() const { return limitIonOutput;}
 
+		RefreshController &getRefreshControl() const;
 
-		//!export given filterstream data pointers
-		static unsigned int exportIonStreams(const std::vector<const FilterStreamData *> &selected, 
-								const std::string &outFile, unsigned int format=IONFORMAT_POS);
-		
-		//!Returns true if the filter is in the midst of a refresh
-		bool isRefreshing() const {return amRefreshing; }
-
-		//!Inform viscontrol that it has new updates to filters from external sources (eg bindings)
-		void setUpdates() { pendingUpdates=true;};
-
-		//!Returns true if the scene has updates that need to be processed
-		bool hasUpdates() const; 
-
-
-		//"Stash" operations
-		//	- The stashes are secondary trees that are not part of the update
-		//	  but can be stored for reference (eg to recall common tree sequences)
-		//----
-		//!Create a new stash. Returns ID for stash
-		unsigned int stashFilters(unsigned int filterID, const char *stashName);
-
-		//!Add a stash as a subtree for the specified parent filter
-		void addStashedToFilters(const Filter *parentFilter, unsigned int stashID);
-
-		//!Delete a stash using its uniqueID
-		void eraseStash(unsigned int stashID);
-
-		//!Retrieve the stash filters
-		void getStashes(std::vector<std::pair<std::string,unsigned int > > &stashes) const;
-
-		//!Retrieve a given stash tree by ID
-		void getStashTree(unsigned int stashId, FilterTree &) const;
-
-		//!Get the number of stashes
-		unsigned int getNumStashes() const { return currentState.getStashCount();}
+		void clearScene() {scene.clearAll();};
 
-		//----
-
-		void updateRawGrid() const;
-
-		//!Set the percentage of ram to use for cache. 0 to disable
-		void setCachePercent(unsigned int percent); 
-
-
-		//!restore top filter tree from undo stack
-		void popUndoStack(bool restorePopped=true);
-
-		//!restore top filter tree from redo stack
-		void popRedoStack();
-
-		//!Get the size of the undo stack
-		unsigned int getUndoSize() const { return currentState.getUndoSize();};
-		//!Get the size of the redo stack
-		unsigned int getRedoSize() const { return currentState.getRedoSize();};
-
-		//!Get the current progress
-		ProgressData getProgress() const { return curProg;}
-
-		//!reset the progress data
-		void resetProgress() { curProg.reset();};
-
-		//!Return the scene's world axis visibility
-		bool getAxisVisible() const;
-
-		//!Set whether filter should use strong or weak randomisation
-		void setStrongRandom(bool strongRand);
+		
+		//Return the selection devices obtained from the last refresh
+		std::vector<SelectionDevice *> &getSelectionDevices() { return state.treeState.getSelectionDevices();};
 
-		//!Set whether to use effects or not
-		void setEffects(bool enable); 
+		//Apply bindings from any selection devices (3D object modifiers) to the tree
+		void applyBindingsToTree()  { state.treeState.applyBindingsToTree();}
 
-		
-		//!return true if the primary tree contains hazardous filters
-		bool hasHazardousContents() const ;
+		//Obtain updated camera from the scene and then commit it to the state
+		void transferSceneCameraToState();
+		//set the camera property for the state, then transfer to scene
+		void setCamProperty(size_t offset, unsigned int key, const std::string &value);
 
 		//!Ask that next time we build the tree, this filter is kept visible/selected.
 		//	may be used repeatedly to make more items visible, 
@@ -430,42 +145,42 @@ class VisController
 		//	filterId must exist during call.
 		void setWxTreeFilterViewPersistence(size_t filterId);
 
-		//Erase the filters that will persist in the view
+		//!Erase the filters that will persist in the view
 		void clearTreeFilterViewPersistence() { persistentFilters.clear();}
 
-		//!Ask if a given filter is a pure data source
-		bool filterIsPureDataSource(size_t filterId) const ;  
-
-
-		//true if the state has been modified since last load/save.
-		int stateModifyLevel() const { return currentState.stateModifyLevel();};
-
-		bool hasStateData() const { return currentState.hasStateData(); }
+		//!Write out the filters into a wxtreecontrol.
+		// optional argument is the fitler to keep visible in the control
+		void updateWxTreeCtrl(wxTreeCtrl *t,const Filter *f=0);
+		//!Update a wxPropertyGrid with the properties for a given filter
+		void updateFilterPropGrid(wxPropertyGrid *g,size_t filterId, const std::string &stateString="") const; 
+		//!Update a wxPropertyGrid with the properties for a given filter
+		void updateCameraPropGrid(wxPropertyGrid *g,size_t cameraId) const; 
+		
+		void updateCameraComboBox(wxComboBox *comboCamera) const;
 
-		//Return the current state's filename
-		string getFilename() const { return currentState.getFilename(); }
-		//Return the current state's filename
-		void setFilename(std::string &s) {currentState.setFilename(s); }
+		//Update the raw numerical data grid
+		void updateRawGrid() const;
+		
+		void updateStashComboBox(wxComboBox *comboStash) const;
+		//Update the 3D scene
+		void updateScene(RefreshController *r); 
 
-		//!Set the animation state, by copy, overwriting the current one
-		// pathmapping provides an animation ID <-> serialised filter path mapping
-		void setAnimationState(const PropertyAnimator &pA, 
-				const std::vector<pair<string,size_t> > &pathMapping) ;
+		//update a scene, simply using some streams and whether we should release the data
+		void updateScene(std::list<std::vector<const FilterStreamData *> > &sceneData, 
+				bool releaseData);
+		//!Set the backend grid control for raw data
+		void setRawGrid(wxGrid *theRawGrid){targetRawGrid=theRawGrid;};
+		//!get the plot wrapper : TODO: Deprecate me
+		PlotWrapper *getPlotWrapper(){return &targetPlots;};
 		
-		//!Retrieve the animation state, by copy, overwriting the current one
-		void getAnimationState(PropertyAnimator &pA, 
-				std::vector<pair<string,size_t> > &pathMapping) const;
-
-		//wipe the state. Does not perform a refresh  need to do this manually
-		void clear();
-
-		//Return the number of filters that are currently in the state
-		size_t getNumFilters() const { return filterTree.size();}
-#ifdef DEBUG
-		//Check that the tree conrol is synced up to the filter map correctly
-		void checkTree(wxTreeCtrl *t);
-#endif
-};
+		//Get a plot ID from the listbox position
+		size_t getPlotID(size_t position) const ;
 
+		//!Set the listbox for plot selection
+		void setPlotList(wxListBox *box){plotSelList=box;};
+		//!Set the text console
+		void setConsole(wxTextCtrl *t) { textConsole = t;}
+	
+};
 
 #endif
diff --git a/src/common/assertion.cpp b/src/common/assertion.cpp
index 93fc00b..ffa5835 100644
--- a/src/common/assertion.cpp
+++ b/src/common/assertion.cpp
@@ -1,6 +1,6 @@
 /*
  * common/assertion.h  - Program assertion header
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
 #include "assertion.h"
 #include <iostream>
 
+#ifdef DEBUG
 void userAskAssert(const char * const filename, const unsigned int lineNumber) 
 {
 
@@ -44,3 +45,37 @@ void userAskAssert(const char * const filename, const unsigned int lineNumber)
 	if(y == 'a')
 		skipAll=true;
 }
+
+//DEBUG NaN and INF
+#ifdef __linux__
+	#ifdef DEBUG
+	#include <fenv.h>
+	void trapfpe (bool doTrap) 
+	{
+		if(doTrap)
+		{
+			feenableexcept(FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
+		}
+		else 
+		{
+			fedisableexcept((FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW));
+		}
+	}
+
+	bool getTrapfpe() 
+	{ 
+		return fegetexcept() & (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
+	}
+	#endif
+#else
+	void trapfpe(bool doTrap)
+	{
+	}
+
+	bool getTrapfpe() 
+	{
+		return false;
+	}
+#endif
+
+#endif
diff --git a/src/common/assertion.h b/src/common/assertion.h
index e3c7675..89b069c 100644
--- a/src/common/assertion.h
+++ b/src/common/assertion.h
@@ -1,6 +1,6 @@
 /*
  * common/assertion.h  - Program assertion header
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,14 +25,22 @@
 	
 	#include <sys/time.h>
 
+	//Do we want to trap floating point exceptions 
+	void trapfpe (bool doTrap=true);
+	//Do we want to trap floating point exceptions 
+	bool getTrapfpe ();
+	//Ask the user about continuing (or not) in the case of an assertion
 	void userAskAssert(const char * const filename, const unsigned int lineNumber); 
+	//Warn the programmer about an error detected by a check
 	void warnProgrammer(const char * const filename, const unsigned int lineNumber,
 							const char *message);
 
+	//Assertion macro. Used to trigger fatal errors in program (debug mode)
 	#ifndef ASSERT
 	#define ASSERT(f) {if(!(f)) { userAskAssert(__FILE__,__LINE__);}}
 	#endif
 
+	//warn programmer about unusual situation occurrence
 	#ifndef WARN
 	#define WARN(f,g) { if(!(f)) { warnProgrammer(__FILE__,__LINE__,g);}}
 	#endif
@@ -52,21 +60,28 @@
 	std::cerr << (TIME_DEBUG_tend.tv_sec - TIME_DEBUG_t.tv_sec) + ((float)TIME_DEBUG_tend.tv_usec-(float)TIME_DEBUG_t.tv_usec)/1.0e6 << std::endl;
 
 	#ifndef TEST
+	#define EQ_TOL(f,g) (fabs( (f) - (g)) < 0.001)
+	#define EQ_TOLV(f,g,h) (fabs( (f) - (g)) < (h))
+
 	#define TEST(f,g) if(!(f)) { std::cerr << "Test fail :" << __FILE__ << ":" << __LINE__ << "\t"<< (g) << std::endl;return false;}
 	#endif
 
 	//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
 	#define COMPILE_ASSERT(pred)            \
-	    switch(0){case 0:case pred:;}
-
-
+		{ switch(0){case 0:case pred:;}}
+	#else
+	#define COMPILE_ASSERT(pred) {static_assert( pred , "Static assertion failed" );}
+	#endif
 #else
 	#define ASSERT(f)
 	#define COMPILE_ASSERT(f)
 	#define WARN(f,g) 
 	#define TEST(f,g)
-	
+	//Do we want to trap floating point exceptions 
+	void trapfpe (bool doTrap=true);
+		
 
 #endif
 
diff --git a/src/common/basics.cpp b/src/common/basics.cpp
index 85062fb..1571566 100644
--- a/src/common/basics.cpp
+++ b/src/common/basics.cpp
@@ -1,6 +1,6 @@
 /*
  *	basics.cpp  - basic functions header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -137,7 +137,7 @@ std::string getDefaultFontFile()
 void tickSpacingsFromInterspace(float start, float end, 
 		float interSpacing, std::vector<float> &spacings)
 {
-	ASSERT(interSpacing > sqrt(std::numeric_limits<float>::epsilon()));
+	ASSERT(interSpacing > sqrtf(std::numeric_limits<float>::epsilon()));
 	unsigned int nTicks;
 
 	if(end < start)
@@ -754,7 +754,7 @@ bool BoundCube::isFlat() const
 
 bool BoundCube::isNumericallyBig() const
 {
-	const float TOO_BIG=sqrt(std::numeric_limits<float>::max());
+	const float TOO_BIG=sqrtf(std::numeric_limits<float>::max());
 	for(unsigned int ui=0;ui<2; ui++)
 	{
 		for(unsigned int uj=0;uj<3; uj++)
@@ -1471,6 +1471,30 @@ unsigned int loadTextStringData(const char *cpFilename, vector<vector<string> >
 	return 0;
 }
 
+
+#if !defined(__WIN32__) && !defined(__WIN64)
+	
+bool isNotDirectory(const char *filename)
+{
+	struct stat statbuf;
+
+	if(stat(filename,&statbuf) == -1)
+		return false;
+
+	return (statbuf.st_mode !=S_IFDIR);
+}
+
+bool rmFile(const std::string &filename)
+{
+	return remove(filename.c_str()) == 0;
+}
+#elif defined(__WIN32) || defined(__WIN64)
+bool rmFile(const std::string &filename)
+{ 
+	return DeleteFile((const wchar_t*)filename.c_str()) == 0;
+}
+#endif
+
 #ifdef DEBUG
 bool isValidXML(const char *filename)
 {
@@ -1502,28 +1526,4 @@ bool isValidXML(const char *filename)
 	WARN(!result,"xmllint not installed in system PATH, cannot perform debug check")
 	return true;
 }
-
-#if !defined(__WIN32__) && !defined(__WIN64)
-	
-bool isNotDirectory(const char *filename)
-{
-	struct stat statbuf;
-
-	if(stat(filename,&statbuf) == -1)
-		return false;
-
-	return (statbuf.st_mode !=S_IFDIR);
-}
-
-bool rmFile(const std::string &filename)
-{
-	return remove(filename.c_str()) == 0;
-}
-#elif defined(__WIN32) || defined(__WIN64)
-bool rmFile(const std::string &filename)
-{ 
-	return DeleteFile((const wchar_t*)filename.c_str()) == 0;
-}
-#endif
-
 #endif
diff --git a/src/common/basics.h b/src/common/basics.h
index 5067d40..064ffbb 100644
--- a/src/common/basics.h
+++ b/src/common/basics.h
@@ -1,6 +1,6 @@
 /*
  *	common/basics.h - Basic functionality header 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -21,6 +21,39 @@
 #define BASICS_H
 //!Basic objects header file
 
+
+#if (_MSC_VER >= 1600) ||  (__cplusplus > 199711L) 
+	#define HAVE_CPP_1X
+#endif
+
+//C-style Array size macro
+#ifdef HAVE_CPP_1X
+	template<typename T, unsigned int s> constexpr unsigned int THREEDEP_ARRAYSIZE(T (&)[s]) {     return s;  } 
+#else
+	#define THREEDEP_ARRAYSIZE(f) (sizeof (f) / sizeof(*f))
+#endif
+
+//macro to switch between normal bool and atomic, as available.
+// do *NOT* declare const ATOMIC_BOOLs. This has wierd CPU caching
+// assumptions, which cause the type to not work properly if not using
+// true atomics	
+#ifndef ATOMIC_BOOL
+
+
+	#ifdef HAVE_CPP_1X
+		// Killed? By an ATOMIC bool? No sir, I guess I don't take 
+		// much solace that the implosion trigger
+		// functioned perfectly. 
+
+		//This bool is reliable for attempting to perform inter-thread flagging.
+		#include <atomic>
+		#define ATOMIC_BOOL std::atomic<bool>
+	#else
+		//C++ <1X does not provide a truly safe bool type. Most implementations it seems to work though (provided you don't use const).
+		#define ATOMIC_BOOL bool
+	#endif
+#endif
+
 #include "mathfuncs.h"
 #include "common/assertion.h"
 
@@ -34,10 +67,6 @@
 class K3DTree;
 
 
-bool dummyCallback(bool);
-
-
-
 //Set new locale code. Must be followed by a popLocale call before completion
 // Only one locale type can be pushed at a time this way
 void pushLocale(const char *newLocale, int type);
@@ -91,6 +120,12 @@ enum
 	ERR_FILE_ENUM_END // not an error, just end of enum
 };
 
+//Exclusive or operator
+template<class T>
+bool xorFunc(const T a, const T b)
+{
+  return (a || b) && !(a && b);
+}
 
 //Perform a a<-b<-c<-a rotation of data
 template<class T>
@@ -494,11 +529,11 @@ class ColourRGBAf
 		float at(unsigned int idx) const;
 };
 
-//Randomly select subset. Subset will be (somewhat) sorted on output
+//Randomly select subset. Subset will be (somewhat) sorted on output.
+// Returns -1 on abort, otherwise returns number of randomly selected items
 template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<T> &source, 
-							RandNumGen &rng, size_t num,unsigned int &progress,bool (*callback)(bool), bool strongRandom=false)
+							RandNumGen &rng, size_t num,unsigned int &progress, ATOMIC_BOOL &wantAbort, bool strongRandom=false)
 {
-	const unsigned int NUM_CALLBACK=50000;
 	//If there are not enough points, just copy it across in whole
 	if(source.size() <= num)
 	{
@@ -532,7 +567,6 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 		for(size_t ui=0; ui<numTicksNeeded; ui++)
 			ticks[ui]=(size_t)(rng.genUniformDev()*(source.size()-1));
 
-		//Remove duplicates. Intersperse some callbacks to be nice
 		std::sort(ticks.begin(),ticks.end());
 		std::vector<size_t>::iterator newLast;
 		newLast=std::unique(ticks.begin(),ticks.end());	
@@ -541,7 +575,7 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 		//Top up with unique entries
 		//TODO: Overcommit & Discard implementation?
 		// - Should be possible to predict how many we need after collisions, and then overcommit and discard randomly. Removes need to loop-sort like this
-		while(ticks.size() < numTicksNeeded)
+		while(ticks.size() < numTicksNeeded && !wantAbort)
 		{
 			size_t moreTicks=numTicksNeeded-ticks.size();
 			for(size_t uk=0;uk<moreTicks;uk++)
@@ -558,12 +592,13 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 			ticks.erase(newLast,ticks.end());
 		}
 
+		if(wantAbort)
+			return -1;
+
 		ASSERT(ticks.size() == numTicksNeeded);
 		//---------
 		
 		//Transfer the output
-		unsigned int curProg=NUM_CALLBACK;
-
 		if(num < source.size()/2)
 		{
 			size_t pos=0;
@@ -572,12 +607,7 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 
 				result[pos]=source[*it];
 				pos++;
-				if(!curProg--)
-				{
-					progress= (unsigned int)((float)(pos)/((float)num)*100.0f);
-					(*callback)(false);
-					curProg=NUM_CALLBACK;
-				}
+				progress= (unsigned int)((float)(pos)/((float)num)*100.0f);
 			}
 		}
 		else
@@ -597,12 +627,7 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 					result[ui-curTick]=source[ui];
 				}
 				
-				if(!curProg--)
-				{
-					progress= (unsigned int)(((float)(ui)/(float)source.size())*100.0f);
-					(*callback)(false);
-					curProg=NUM_CALLBACK;
-				}
+				progress= (unsigned int)(((float)(ui)/(float)source.size())*100.0f);
 			}
 		}
 
@@ -635,7 +660,6 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 		l.setState(start);
 
 		size_t ui=0;	
-		unsigned int curProg=NUM_CALLBACK;
 		//generate unique weak random numbers.
 		while(ui<num)
 		{
@@ -649,12 +673,7 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 				result[ui] =source[res];
 				ui++;
 			}
-			if(!curProg--)
-			{
-				progress= (unsigned int)((float)(ui)/((float)source.size())*100.0f);
-				(*callback)(false);
-				curProg=NUM_CALLBACK;
-			}
+			progress= (unsigned int)((float)(ui)/((float)source.size())*100.0f);
 		}
 
 	}
@@ -664,7 +683,7 @@ template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<
 
 //Randomly select subset [0,max). Subset will be (somewhat) sorted on output
 template<class T> size_t randomDigitSelection(std::vector<T> &result, const size_t max,
-			RandNumGen &rng, size_t num,unsigned int &progress,bool (*callback)(bool),
+			RandNumGen &rng, size_t num,unsigned int &progress,
 			bool strongRandom=false)
 {
 	//If there are not enough points, just copy it across in whole
@@ -703,13 +722,11 @@ template<class T> size_t randomDigitSelection(std::vector<T> &result, const size
 		for(size_t ui=0; ui<numTicksNeeded; ui++)
 			ticks[ui]=(size_t)(rng.genUniformDev()*(max-1));
 
-		//Remove duplicates. Intersperse some callbacks to be nice
+		//Remove duplicates
 		std::sort(ticks.begin(),ticks.end());
-		(*callback)(false);
 		std::vector<size_t>::iterator itLast;
 		itLast=std::unique(ticks.begin(),ticks.end());	
 		ticks.erase(itLast,ticks.end());
-		(*callback)(false);
 		
 		//Top up with unique entries
 		while(ticks.size() < numTicksNeeded)
@@ -725,10 +742,8 @@ template<class T> size_t randomDigitSelection(std::vector<T> &result, const size
 			}
 
 			std::sort(ticks.begin(),ticks.end());
-			(*callback)(false);
 			itLast=std::unique(ticks.begin(),ticks.end());	
 			ticks.erase(itLast,ticks.end());
-			(*callback)(false);
 		}
 
 
@@ -747,12 +762,7 @@ template<class T> size_t randomDigitSelection(std::vector<T> &result, const size
 
 				result[pos]=*it;
 				pos++;
-				if(!curProg--)
-				{
-					progress= (unsigned int)((float)(curProg)/((float)num)*100.0f);
-					(*callback)(false);
-					curProg=CURPROG;
-				}
+				progress= (unsigned int)((float)(curProg)/((float)num)*100.0f);
 			}
 		}
 		else
@@ -769,12 +779,7 @@ template<class T> size_t randomDigitSelection(std::vector<T> &result, const size
 				else
 					result[ui-curTick]=ui;
 				
-				if(!curProg--)
-				{
-					progress= (unsigned int)((float)(curProg)/((float)num)*100.0f);
-					(*callback)(false);
-					curProg=CURPROG;
-				}
+				progress= (unsigned int)((float)(curProg)/((float)num)*100.0f);
 			}
 		}
 
diff --git a/src/common/colourmap.h b/src/common/colourmap.h
index d336f1d..d086952 100644
--- a/src/common/colourmap.h
+++ b/src/common/colourmap.h
@@ -1,6 +1,6 @@
 /*
  *	colourmap.h - Colour continumms definitions
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/common/constants.cpp b/src/common/constants.cpp
index 61d3da9..3ce30a5 100644
--- a/src/common/constants.cpp
+++ b/src/common/constants.cpp
@@ -1,6 +1,6 @@
 /*
  * common/constants.cpp  - Common constants used across program
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,6 +26,6 @@ const char *DTD_NAME="threeDepict-state.dtd";
 //Program name
 const char *PROGRAM_NAME = "3Depict";
 //Program version
-const char *PROGRAM_VERSION = "0.0.17";
+const char *PROGRAM_VERSION = "0.0.18";
 //Path to font for Default FTGL  font
 const char *FONT_FILE= "FreeSans.ttf";
diff --git a/src/common/constants.h b/src/common/constants.h
index 56208fe..f4f0245 100644
--- a/src/common/constants.h
+++ b/src/common/constants.h
@@ -1,6 +1,6 @@
 /*
  * common/constants.h  - Common constants used across program
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,13 +20,6 @@
 
 #include "translation.h"
 
-//C-style Array size macro
-#define THREEDEP_ARRAYSIZE(f) (sizeof (f) / sizeof(*f))
-//C++1x availabilty (as far as we are concerned)
-#if __cplusplus > 199711L
-	#define HAVE_CPP1X 1
-#endif
-
 //Hack-ish variable for minimal size of data to execute
 // openMP conditionals as parallel regions. This is highly empirical!
 const unsigned int OPENMP_MIN_DATASIZE=1000;
@@ -57,6 +50,7 @@ enum
 	PROPERTY_TYPE_POINT3D,
 	PROPERTY_TYPE_CHOICE,
 	PROPERTY_TYPE_FILE,
+	PROPERTY_TYPE_DIR,
 	PROPERTY_TYPE_ENUM_END //Not a prop, just end of enum
 };
 
diff --git a/src/common/endianTest.h b/src/common/endianTest.h
index 84731d9..0afa42e 100644
--- a/src/common/endianTest.h
+++ b/src/common/endianTest.h
@@ -1,6 +1,6 @@
 /*
  *	endianttest.h - Platform endian testing
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/testing/testing.h b/src/common/gsl_helper.cpp
similarity index 60%
copy from src/testing/testing.h
copy to src/common/gsl_helper.cpp
index 0b94768..f1dc910 100644
--- a/src/testing/testing.h
+++ b/src/common/gsl_helper.cpp
@@ -1,6 +1,6 @@
 /*
- *	testing.cpp - unit testing framework
- *	Copyright (C) 2013, D Haley 
+ *	gsl_helper.cpp - gsl assistance routines
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -15,21 +15,27 @@
  *	You should have received a copy of the GNU General Public License
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
-
-#ifndef TESTING_H
-#define TESTING_H
-
-#ifdef DEBUG
-#include "backend/filtertree.h"
-#include "testing/mglTesting.h"
-
-
-//Run all the built-in unit tests.
-bool runUnitTests();
-
-//Run the particular specified filter tree
-bool testFilterTree(const FilterTree &f);
-
-#endif
-
-#endif
+#include "gsl_helper.h"
+
+#include <iostream>
+
+
+using std::cerr;
+using std::endl;
+
+void gslPrint(const gsl_matrix *m)
+{
+	for(unsigned int ui=0;ui<m->size1; ui++)
+	{
+		cerr << "|";
+		for(unsigned int uj=0; uj<m->size2; uj++)
+		{
+		
+			cerr << gsl_matrix_get(m,ui,uj);
+			
+			if (uj +1 < m->size2)
+				cerr << ",\t" ;
+		}
+		cerr << "\t|" << endl;
+	}	
+}
diff --git a/src/testing/mglTesting.h b/src/common/gsl_helper.h
similarity index 77%
copy from src/testing/mglTesting.h
copy to src/common/gsl_helper.h
index a9da1d2..a2e7f26 100644
--- a/src/testing/mglTesting.h
+++ b/src/common/gsl_helper.h
@@ -1,6 +1,6 @@
 /*
- *	mglTesting.h - unit testing implementation for mgl code
- *	Copyright (C) 2014, D Haley 
+ *	gsl_helper.h - GSL Helper routines
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -16,10 +16,13 @@
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef MGLTESTING_H
-#define MGLTESTING_H
+#ifndef GSL_HELPER_H
+#define GSL_HELPER_H
+
+
+#include <gsl/gsl_linalg.h>
+
+void gslPrint(const gsl_matrix *m);
 
-//!Run a quick test that mathgl is working
-bool mglTest();
 
 #endif
diff --git a/src/common/mathfuncs.cpp b/src/common/mathfuncs.cpp
index e10c2f2..1d5e49e 100644
--- a/src/common/mathfuncs.cpp
+++ b/src/common/mathfuncs.cpp
@@ -1,6 +1,6 @@
 /*
  *	mathfuncs.cpp - Miscellaneous mathematical functions implementation. 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -247,18 +247,47 @@ float Point3D::angle(const Point3D &pt) const
 	return acos(dotProd(pt)/(sqrtf(sqrMag()*pt.sqrMag())));
 }
 
+
+void Point3D::sphericalAngles(float &theta, float &phi) const
+{
+	float sqrVal=sqrMag();
+	theta=acos(value[2]/sqrtf(sqrVal));
+	//phi
+	phi=atan2(value[1],value[0]);
+
+#ifdef DEBUG
+	static bool amRecursing;
+	if(amRecursing)
+		return;
+	amRecursing=true;
+	//Check that the definition of the spherical coordinates matches
+	Point3D retreived(sin(theta)*cos(phi),sin(theta)*sin(phi),cos(theta));
+	float tmpTheta,tmpPhi;
+	retreived.sphericalAngles(tmpTheta,tmpPhi);
+	ASSERT(EQ_TOL(tmpTheta,theta));
+	ASSERT(EQ_TOL(tmpPhi,phi));
+
+	retreived*=sqrtf(sqrVal);
+	for(unsigned int ui=0;ui<3;ui++)
+	{
+		ASSERT(EQ_TOL(retreived[ui] ,value[ui]));
+	}
+	amRecursing=false;
+#endif
+}
+
 bool Point3D::orthogonalise(const Point3D &pt)
 {
 	Point3D crossp;
 	crossp=this->crossProd(pt);
 
 	//They are co-linear, or near-enough to be not resolvable.
-	if(crossp.sqrMag()  < sqrt(std::numeric_limits<float>::epsilon()))
+	if(crossp.sqrMag()  < sqrtf(std::numeric_limits<float>::epsilon()))
 		return false;
 	crossp.normalise();
 
 	crossp=crossp.crossProd(pt);
-	*this=crossp.normalise()*sqrt(this->sqrMag());	
+	*this=crossp.normalise()*sqrtf(this->sqrMag());	
 
 	return true;
 }
@@ -576,7 +605,7 @@ void quat_rot(Point3D &p, const Point3D &r, float angle)
 void quat_rot(Point3f *point, const Point3f *rotVec, float angle)
 {
 	ASSERT(rotVec->fx*rotVec->fx + rotVec->fy*rotVec->fy + rotVec->fz*rotVec->fz - 1.0f < 
-			5.0f*sqrt(std::numeric_limits<float>::epsilon()));
+			5.0f*sqrtf(std::numeric_limits<float>::epsilon()));
 
 	double sinCoeff;
        	Quaternion rotQuat;
@@ -599,7 +628,7 @@ void quat_rot(Point3f *point, const Point3f *rotVec, float angle)
 	rotQuat.c=sinCoeff*rotVec->fy;
 	rotQuat.d=sinCoeff*rotVec->fz;
 
-//	pointQuat.a =0.0f; This is implied in the pointQuat multiplcation function
+//	pointQuat.a =0.0f; This is implied in the pointQuat multiplication function
 	pointQuat.b = point->fx;
 	pointQuat.c = point->fy;
 	pointQuat.d = point->fz;
@@ -645,7 +674,7 @@ void quat_rot_array(Point3f *pointArr, unsigned int n,
 	Quaternion temp;
 	{
 		ASSERT(rotVec->fx*rotVec->fx + rotVec->fy*rotVec->fy + rotVec->fz*rotVec->fz - 1.0f < 
-				5.0f*sqrt(std::numeric_limits<float>::epsilon()));
+				5.0f*sqrtf(std::numeric_limits<float>::epsilon()));
 
 		double sinCoeff;
 		
@@ -667,7 +696,7 @@ void quat_rot_array(Point3f *pointArr, unsigned int n,
 
 		for(unsigned int ui=0;ui<n; ui++)
 		{
-		//	pointQuat.a =0.0f; This is implied in the pointQuat multiplcation function
+		//	pointQuat.a =0.0f; This is implied in the pointQuat multiplication function
 			pointQuat.b = pointArr[ui].fx;
 			pointQuat.c = pointArr[ui].fy;
 			pointQuat.d = pointArr[ui].fz;
@@ -685,7 +714,7 @@ void quat_rot_array(Point3f *pointArr, unsigned int n,
 void quat_get_rot_quat(const Point3f *rotVec, float angle,Quaternion *rotQuat) 
 {
 	ASSERT(rotVec->fx*rotVec->fx + rotVec->fy*rotVec->fy + rotVec->fz*rotVec->fz - 1.0f < 
-			5.0f*sqrt(std::numeric_limits<float>::epsilon()));
+			5.0f*sqrtf(std::numeric_limits<float>::epsilon()));
 	double sinCoeff;
 #ifdef _GNU_SOURCE
 	double cosCoeff;
diff --git a/src/common/mathfuncs.h b/src/common/mathfuncs.h
index 61c38c3..f48c122 100644
--- a/src/common/mathfuncs.h
+++ b/src/common/mathfuncs.h
@@ -1,6 +1,6 @@
 /*
  *	mathfuncs.h - General mathematic functions header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -103,23 +103,31 @@ class Point3D
                 //!returns the square of distance another pt
                 float sqrDist(const Point3D &pt) const;
 
-                //!Calculate the dot product of this and another pint
+                //!overload for array indexing returns |pt|^2
+                float sqrMag() const;
+		
+		//!Apply float->float transformation
+		void sqrt() { for(unsigned int ui=0;ui<3;ui++) value[ui]=sqrtf(value[ui]); }
+                
+		//ISO31-11 spherical co-ordinates. theta is clockwise rotation around z- axis.
+		// phi is elevation from x-y plane
+		void sphericalAngles(float &theta, float &phi) const;	
+
+		//!Calculate the dot product of this and another pint
                 float dotProd(const Point3D &pt) const;
                 //!Calculate the cross product of this and another point
                 Point3D crossProd(const Point3D &pt) const;
 
-				//!Calculate the angle between two position vectors in radiians
-				float angle(const Point3D &pt) const;
+		//!Calculate the angle between two position vectors in radiians
+		float angle(const Point3D &pt) const;
 
 		//Extend the current vector by the specified distance
 		void extend(float distance);
 
-                //!overload for array indexing returns |pt|^2
-                float sqrMag() const;
 
-				//!Retrieve by value
+		//!Retrieve by value
                 float operator[](unsigned int ui) const; 
-				//!Retrieve element by referene
+		//!Retrieve element by referene
                 float &operator[](unsigned int ui) ;
 
                 //!Is a given point stored inside a box bounded by orign and this pt?
diff --git a/src/common/mesh.cpp b/src/common/mesh.cpp
new file mode 100644
index 0000000..4f3e62b
--- /dev/null
+++ b/src/common/mesh.cpp
@@ -0,0 +1,3236 @@
+/* 
+ * Copyright (C) 2015  D Haley
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "mesh.h"
+
+
+#include <string>
+#include <algorithm>
+#include <limits>
+#include <list>
+#include <utility>
+#include <deque>
+
+
+using std::deque;
+using std::make_pair;
+using std::vector;
+using std::string;
+using std::endl;
+using std::list;
+using std::cerr;
+
+enum
+{
+	MESH_LOAD_UNSPECIFIED_ERROR=1,
+	MESH_LOAD_BAD_NODECOUNT,
+	MESH_LOAD_BAD_ELEMENTCOUNT,
+	MESH_LOAD_IS_INSANE,
+	MESH_LOAD_ENUM_END
+};
+
+const char *MESH_LOAD_ERRS[] = {  "",
+	"Missing error message. This is a bug, please report it",
+	"Node count was different to number of present nodes",
+	"Element count was less than number of present elements",
+	"Mesh loaded, but failed to pass sanity checks"
+};
+			
+
+const size_t PROGRESS_REDUCE=500;				
+
+
+float signVal(unsigned int val)
+{
+	if (val &1)
+		return 1;
+	else
+		return -1;
+}
+
+size_t findMaxLessThanOrEq(const vector< std::pair<size_t,size_t> > &v,
+			size_t value)
+{
+	ASSERT(v.size());
+	size_t curMax=v.begin()->first; 
+	size_t curMaxOff=0;
+	for(size_t ui=0;ui<v.size();ui++)
+	{
+		if(v[ui].first >  curMax && v[ui].first <=value)
+			curMaxOff=ui;
+	}
+
+	return curMaxOff;
+}
+
+
+//   Recursive definition of determinate using expansion by minors.
+float Determinant(float **a,int n)
+{
+	float det = 0;
+	ASSERT( n > 1);
+
+	//Fundamental 2x2 det.
+	if (n == 2) 
+		det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
+	else
+       	{
+		int i,j,j1,j2;
+		//recurisve det
+		det = 0;
+		for (j1=0; j1<n; j1++) {
+			float **m = new float*[n-1];
+			for (i=0; i<n-1; i++)
+				m[i] = new float[n-1];
+			for (i=1; i<n; i++) {
+				j2 = 0;
+				for (j=0; j<n; j++) {
+					if (j == j1)
+						continue;
+					m[i-1][j2] = a[i][j];
+					j2++;
+				}
+			}
+			det += signVal(2+j1) * a[0][j1] * Determinant(m,n-1);
+			for (i=0; i<n-1; i++)
+				delete[] m[i];
+			delete[] m;
+		}
+	}
+	return(det);
+}
+
+//Special case of the four by four determinant, useful for tetrahedrons
+//|a0 a1 a2 1 |
+//|b0 b1 b2 1 |
+//|c0 c1 c2 1 |
+//|d0 d1 d2 1 |
+float fourDeterminant(const Point3D &a, const Point3D &b, const Point3D &c, const Point3D &d)
+{
+	float **rows;
+	rows = new float*[4];
+	//Malloc cols.
+	for(unsigned int ui=0;ui<4;ui++)
+		rows[ui] = new float[4];
+
+	for(unsigned int ui=0;ui<3;ui++)
+	{
+		rows[0][ui] = a[ui];
+		rows[1][ui] = b[ui];
+		rows[2][ui] = c[ui];
+		rows[3][ui] = d[ui];
+	}
+
+	for(unsigned int ui=0;ui<4;ui++)
+		rows[ui][3] = 1;
+
+	float v;
+	v=Determinant(rows,4);
+
+	
+	for(unsigned int ui=0;ui<4;ui++)
+		delete[] rows[ui];
+
+	delete[] rows;
+
+	return v;
+
+}
+
+//return the edge number for a triangle.
+//To see this, draw a triangle, and going clockwise, label the edges 0, 1 and 2
+//With the edge facing you, label the left corner with the edge number.
+//Draw up the table mapping the edge that is formed by the vertices i,j; 
+//that is this function.
+unsigned int edgeIdx(unsigned int i,unsigned int j)
+{
+	ASSERT(i<3 && j < 3);
+	switch(i+j)
+	{
+		case 1:
+			return 1;
+		case 2:
+			return 0;
+		case 3:
+		       	return 2;
+	}
+	ASSERT(false);
+	return -1;
+}
+
+
+//---- BEGIN This section under specific licence ---
+// Copyright 2001, softSurfer (www.softsurfer.com)
+// This code may be freely used and modified for any purpose
+// providing that this copyright notice is included with it.
+// SoftSurfer makes no warranty for this code, and cannot be held
+// liable for any real or imagined damage resulting from its use.
+// Users of this code must verify correctness for their application.
+// R - ray; t, triangle (array of 3 pts)
+// returns 
+// 	-1 : degenerate case (triangle degen)
+// 	0: No intersect
+// 	1 : Single Intersection
+// 	2: Edge intersection (co-planar)
+// I - intersection of ray w triangle; if exists and is unique
+int intersect_RayTriangle( const Point3D &rayStart, const Point3D &rayEnd, 
+					Point3D *tri, Point3D &I )
+{
+    Point3D    u, v, n;             // triangle vectors
+    Point3D    dir, w0, w;          // ray vectors
+
+
+
+    // get triangle edge vectors and plane normal
+    u = tri[1] - tri[0];
+    v = tri[2] - tri[0];
+    n = u.crossProd(v);             // cross product
+
+
+    if (n.sqrMag() < (std::numeric_limits<float>::epsilon()) )
+	return -1;                 // do not deal with this case, the triangle is degenerate
+
+    n.normalise();
+
+    dir = rayEnd - rayStart;             // ray direction vector
+
+
+    //Check for ray-plane intersection point
+    //--
+    Point3D rv1,rv2;
+    rv1 = rayStart - tri[0];
+    rv2 = rayEnd - tri[0];
+
+    //If the dot products do not flip, the ray cannot cross infinite plane
+    float dp1 = rv1.dotProd(n);
+    float dp2 = rv2.dotProd(n); 
+    if(dp1*dp2 > 0) //(signs are the same --> ray is on one side of plane only)
+	    return 0;
+    else if(rv1.dotProd(n) < std::numeric_limits<float>::epsilon() &&
+			   rv2.dotProd(n) < std::numeric_limits<float>::epsilon())
+
+    {
+	    //If the ray-ends -> vertex vectors have no component in the normal direction
+	    //the ray is coplanar
+	    return 2;
+    }
+
+    //Project the ray onto the plane to create intersection point
+    //Solution is found by parameterising ray and solving for 
+    //dot product with normal 
+    I=rayStart-dir*rv1.dotProd(n)/dir.dotProd(n);
+
+
+    //--
+
+    // is I inside T? If so, then the dot product of each edge
+    // with the ray from the edge start to the intersection will always
+    // be in range [0-1]; otherwise there will be at least one that is negative
+    float    uu, uv, vv, wu, wv, D;
+    uu = u.dotProd(u);
+    uv = u.dotProd(v);
+    vv = v.dotProd(v);
+    w = I - tri[0];
+    wu = w.dotProd(u);
+    wv = w.dotProd(v);
+    D = uv * uv - uu * vv;
+
+    // get and test parametric coords
+    float s, t;
+    s = (uv * wv - vv * wu) / D;
+    if (s < 0.0 || s > 1.0)        // I is outside T
+	return 0;
+    t = (uv * wu - uu * wv) / D;
+    if (t < 0.0 || (s + t) > 1.0)  // I is outside T
+	return 0;
+
+    return 1;                      // I is in T
+}
+
+//---- END This section under specific licence ---
+
+//Creates a list of pair,vector of point indices that are within a certain
+// tolerance radius of one another. Output first value in list will be strictly increasing. (i.e. it->first < (it+1)->first, regardless of it position)
+
+//FIXME: This algorithm is a poor effort. It just picks a semi-random point, 
+//then nicks everything within the capture radius that was not nicked before.
+// this will work if the adjacency points are *close*, and well separated, but not otherwise.
+
+//TODO: Unify these two overloads!
+//--
+void findNearVerticies(float tolerance, const vector<Point3D> &ptVec,
+		vector<std::pair<size_t,vector<size_t> > > &clusterList)
+{
+	ASSERT(!clusterList.size());
+	
+	vector<bool> marked;
+	marked.resize(ptVec.size(),false);
+	//Try to find the common points
+	for(size_t ui=0;ui<ptVec.size();ui++)
+	{
+		vector<size_t> curClustered;
+
+		//FIXME: replace with KD tree based search, or some other smart structure
+		for(size_t uj=0;uj<ptVec.size();uj++)
+		{
+			if(ui==uj|| marked[uj])
+				continue;
+
+			if(ptVec[ui].sqrDist(ptVec[uj]) < tolerance)
+			{
+				curClustered.push_back(uj);
+				marked[uj]=true;
+			}
+
+		}
+	
+		//If we found any points, then this point has also not been seen before
+		// by handshaking lemma
+		if(curClustered.size())
+		{
+			marked[ui]=true;
+			clusterList.push_back(std::make_pair(ui,curClustered));
+			curClustered.clear();
+		}
+
+	}
+
+}
+
+void findNearVerticies(float tolerance, const vector<Point3D> &ptVec,
+		std::list<std::pair<size_t,vector<size_t> > > &clusterList)
+{
+	ASSERT(clusterList.empty());
+	
+	vector<bool> marked;
+	marked.resize(ptVec.size(),false);
+	//Try to find the common points
+	for(size_t ui=0;ui<ptVec.size();ui++)
+	{
+		vector<size_t> curClustered;
+
+		//FIXME: replace with KD tree based search, or some other smart structure
+		for(size_t uj=0;uj<ptVec.size();uj++)
+		{
+			if(ui==uj|| marked[uj])
+				continue;
+
+			if(ptVec[ui].sqrDist(ptVec[uj]) < tolerance)
+			{
+				curClustered.push_back(uj);
+				marked[uj]=true;
+			}
+
+		}
+	
+		//If we found any points, then this point has also not been seen before
+		// by handshaking lemma
+		if(curClustered.size())
+		{
+			marked[ui]=true;
+			clusterList.push_back(std::make_pair(ui,curClustered));
+			curClustered.clear();
+		}
+
+	}
+}
+//--
+
+
+
+void Mesh::print(std::ostream &o) const
+{	
+	o << " Node count :" << nodes.size() << endl;
+	o << " Point count :" << points.size() << endl;
+	o << " Line count :" << tetrahedra.size() << endl;
+	o << " Triangle count :" << triangles.size() << endl;
+	o << " Tetrahedra count :" << tetrahedra.size() << endl;
+
+
+	BoundCube b;
+	b.setBounds(nodes);
+	o << "Bounding box:" << endl;
+	o << b << endl;
+	
+	Point3D centroid(0,0,0);
+	
+	for(size_t ui=0;ui<nodes.size();ui++)
+		centroid+=nodes[ui];
+
+	centroid*=1.0/(float)nodes.size();
+
+	o << "Centroid:" << endl;
+	o << centroid << endl;
+}
+//Input must be sorted and unique.
+void Mesh::killOrphanNodes(const vector<size_t> &orphans)
+{
+#ifdef HAVE_CPP11
+	ASSERT(std::is_sorted(orphans.begin(),orphans.end()));
+#endif
+	ASSERT(std::adjacent_find(orphans.begin(),orphans.end()) == orphans.end())
+
+	ASSERT(isSane());
+
+
+	vector<size_t> offsets;
+	offsets.resize(nodes.size());
+	size_t curOrphan=0;
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		while(curOrphan < orphans.size() && 
+				orphans[curOrphan] <=ui)
+			curOrphan++;
+		offsets[ui]=curOrphan;
+	}
+
+	//renumber the points
+	vector<size_t>::iterator itJ;
+	for(size_t ui=0;ui<points.size();ui++)
+		points[ui]-=offsets[points[ui]];
+	
+	//renumber the lines 
+	for(size_t ui=0;ui<lines.size();ui++)
+		for(size_t uj=0;uj<2;uj++)
+			lines[ui].p[uj]-=offsets[lines[ui].p[uj]];
+
+	//renumber the triangles 
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		for(size_t uj=0;uj<3;uj++)
+		{
+			ASSERT(triangles[ui].p[uj] -
+			offsets[triangles[ui].p[uj]]< nodes.size());
+			triangles[ui].p[uj]-=offsets[triangles[ui].p[uj]];
+		}
+	}
+	
+	//renumber the tetrahedra
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+		for(size_t uj=0;uj<4;uj++)
+			tetrahedra[ui].p[uj]-=offsets[tetrahedra[ui].p[uj]];
+
+
+	//FIXME: Not efficient
+	vector<Point3D> newNodes;
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		if(!binary_search(orphans.begin(),orphans.end(),ui))
+			newNodes.push_back(nodes[ui]);
+	}
+	nodes.swap(newNodes);
+
+	//Even if each object has every vertex with its own node, not
+	// shared with any other, it cannot exceed this.
+	ASSERT(nodes.size() <= (3*triangles.size() + 
+		2*lines.size() + 4*tetrahedra.size() + points.size()));
+}
+
+bool Mesh::tetrahedronDegenerate(unsigned int tet) const
+{
+	//Compute the following determinant:
+	//if zero --> degenerate
+	//	       |x1 y1 z1 1|
+	//        D0 = |x2 y2 z2 1|
+	//             |x3 y3 z3 1|
+	//             |x4 y4 z4 1|
+
+	return  fourDeterminant(nodes[tetrahedra[tet].p[0]],
+			nodes[tetrahedra[tet].p[1]],
+			nodes[tetrahedra[tet].p[2]],
+			nodes[tetrahedra[tet].p[3]]) < std::numeric_limits<float>::epsilon();
+}
+
+
+bool Mesh::pointInTetrahedron(unsigned int tet, const Point3D &p) const
+{
+	//Compute the following determinants;
+	//if there is a sign change, then 
+	//	       |x1 y1 z1 1|
+	//        D0 = |x2 y2 z2 1|
+	//             |x3 y3 z3 1|
+	//             |x4 y4 z4 1|
+	
+	float f;
+	f = fourDeterminant(nodes[tetrahedra[tet].p[0]],
+				nodes[tetrahedra[tet].p[1]],
+				nodes[tetrahedra[tet].p[2]],
+				nodes[tetrahedra[tet].p[3]]);
+
+	//Ensure that we do not get the zero case.
+	ASSERT(!tetrahedronDegenerate(tet));
+
+	bool positive;
+       	positive= f>0;
+	
+
+	//
+	//             |x  y  z  1|
+	//        D1 = |x2 y2 z2 1|
+	//             |x3 y3 z3 1|
+	//             |x4 y4 z4 1|
+	f = fourDeterminant(p,
+				nodes[tetrahedra[tet].p[1]],
+				nodes[tetrahedra[tet].p[2]],
+				nodes[tetrahedra[tet].p[3]]);
+	if(f < 0 &&  positive)
+		return false;
+	//
+	//             |x1 y1 z1 1|
+	//        D2 = |x  y  z  1|
+	//             |x3 y3 z3 1|
+	//             |x4 y4 z4 1|
+	f = fourDeterminant(
+				nodes[tetrahedra[tet].p[0]],
+				p,
+				nodes[tetrahedra[tet].p[2]],
+				nodes[tetrahedra[tet].p[3]]);
+	if(f < 0 &&  positive)
+		return false;
+	//
+	//             |x1 y1 z1 1|
+	//        D3 = |x2 y2 z2 1|
+	//             |x  y  z  1|
+	//             |x4 y4 z4 1|
+	f = fourDeterminant(	nodes[tetrahedra[tet].p[0]],
+				nodes[tetrahedra[tet].p[1]],
+				p,
+				nodes[tetrahedra[tet].p[3]]);
+	if(f < 0 &&  positive)
+		return false;
+	//
+	//             |x1 y1 z1 1|
+	//        D4 = |x2 y2 z2 1|
+	//             |x3 y3 z3 1|
+	//             |x  y  z  1|
+	f = fourDeterminant(	nodes[tetrahedra[tet].p[0]],
+				nodes[tetrahedra[tet].p[1]],
+				nodes[tetrahedra[tet].p[2]],
+				p);
+
+	if(f < 0 &&  positive)
+		return false;
+
+	return true;
+}
+
+//Tell us if the two triangles are indeed the same
+bool Mesh::sameTriangle(unsigned int ui, unsigned int uj) const
+{
+	vector<unsigned int> t1,t2;
+
+	t1.resize(3);
+	t2.resize(3);
+	for(unsigned int idx=0;idx<3;idx++)
+	{
+		t1[idx]= triangles[ui].p[idx];
+		t2[idx]= triangles[uj].p[idx];
+	}
+
+	std::sort(t1.begin(),t1.end());
+	std::sort(t2.begin(),t2.end());
+
+	return std::equal(t1.begin(),t1.end(),t2.begin());
+
+}
+
+bool Mesh::sameTriangle(const TRIANGLE &t1, const TRIANGLE &t2) 
+{
+	vector<unsigned int> ta,tb;
+
+	ta.resize(3);
+	tb.resize(3);
+	for(unsigned int idx=0;idx<3;idx++)
+	{
+		ta[idx]= t1.p[idx];
+		tb[idx]= t2.p[idx];
+	}
+
+	std::sort(ta.begin(),ta.end());
+	std::sort(tb.begin(),tb.end());
+
+	return std::equal(ta.begin(),ta.end(),tb.begin());
+
+}
+
+bool Mesh::isSane(bool output, std::ostream &outStr) const
+{
+	//Check sanity
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+	{
+		//Tetrahedra has unique vertices
+		for(unsigned int uj=0;uj<4; uj++)
+		{
+			for(unsigned int uk=0;uk<4;uk++)
+			{
+				if(uk == uj)
+					continue;
+
+				if( tetrahedra[ui].p[uj] == tetrahedra[ui].p[uk])
+				{
+					if(output)
+						outStr << "It's INSANE. " << __LINE__ << std::endl;
+					return false;
+				}
+			}
+		
+			//tetrahedra point to valid node
+			if(tetrahedra[ui].p[uj] > nodes.size())
+			{
+				if(output)
+					outStr << "It's INSANE. " << __LINE__ << std::endl;
+				return false;
+			}
+		}
+
+	}
+	
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		//triangle vertices unique test
+		for(unsigned int uj=0;uj<3; uj++)
+		{
+			for(unsigned int uk=0;uk<3;uk++)
+			{
+				if(uk == uj)
+					continue;
+
+				if( triangles[ui].p[uj] == triangles[ui].p[uk])
+				{	
+					if(output)
+					{
+						outStr << "It's INSANE. " << __LINE__ << std::endl;
+						outStr << "vertex  " << uj << " and " << uk
+							<< " of triangle " << ui << "not unique" << endl;
+						outStr << triangles[ui].p[uj] << " node is duplicated" << std::endl;
+					}
+					return false;
+				}
+			}
+		
+			//triangle points to valid node
+			if(triangles[ui].p[uj] > nodes.size())
+			{
+				if(output)
+					outStr << "It's INSANE. " << __LINE__ << std::endl;
+				return false;
+			}
+		}
+
+	}
+
+	for(size_t ui=0;ui<lines.size();ui++)
+	{
+		//lines have no common vertices
+		for(unsigned int uj=0;uj<2; uj++)
+		{
+			for(unsigned int uk=0;uk<2;uk++)
+			{
+				if(uk == uj)
+					continue;
+
+				if( lines[ui].p[uj] == lines[ui].p[uk])
+				{	
+					if(output)	
+						outStr << "It's INSANE. " << __LINE__ << std::endl;
+					return false;
+				}
+			}
+		
+			//lines point to valid node
+			if(lines[ui].p[uj] > nodes.size())
+			{	
+				if(output)	
+					outStr << "It's INSANE. " << __LINE__ << std::endl;
+				return false;
+			}
+		}
+
+	}
+
+
+	//Check that we have enough nodes to support each primitive type
+	if(nodes.size() < 4 && tetrahedra.size())
+	{	
+		if(output)	
+			outStr << "It's INSANE. " << __LINE__ << std::endl;
+		return false;
+	}
+	if(nodes.size() < 3 && triangles.size())
+	{
+		if(output)	
+			outStr << "It's INSANE. " << __LINE__ << std::endl;
+		return false;
+	}
+	if(nodes.size() < 2 && lines.size())
+	{	
+		if(output)
+			outStr << "It's INSANE. " << __LINE__ << std::endl;
+		return false;
+	}
+
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+
+
+		vector<size_t> tris,lines;
+		//Lets look for triangles and lines attached to this 
+		//(really we just want the triangles)
+		getAttachedComponents(ui,tris,lines);
+
+		if(tris.size() > 4)
+		{
+			if(output)
+			{
+				//Too many triangles
+				outStr << "INSANE: Tetrahedron " << ui << " has more than 4 attached triangles.." << std::endl;
+			}
+			return false;
+		}
+
+		if(lines.size() > 6)
+		{
+			if(output)
+			{
+				//Too many lines
+				outStr << "INSANE: Tetrahedron " << ui << " has more than 6 attached lines.." << std::endl;
+			}
+			return false;
+		}
+
+	}
+	return true;
+}
+
+
+
+void Mesh::getDisconnectedTets(vector<size_t> &tets) const
+{
+	using std::list;
+
+	//In a fully connected mesh, every tetrahedron must be 
+	//matched to at least one other tetrahedron by  a face
+
+	//By definition, this is true for a zero or singly sized mesh	
+	if(tetrahedra.size() <=1)
+		return;
+
+	//Create a lookup table of vertices -> tetrahedra
+	vector<list<size_t> > tetLookup;
+	tetLookup.resize(nodes.size());
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+		for(unsigned int uj=0;uj<4;uj++)
+			tetLookup[tetrahedra[ui].p[uj]].push_back(ui);
+	}
+
+	//Now loop through the tetrahedra, and using the previously constructed
+	//table, do a lookup to check that there is exactly one or zero attached 
+	//tetrahedra to each face, and that at least one face has an attached tetrahedron
+	
+	//Map that tells us the vertex indices that are incident to each face
+	unsigned int faceMap[4][3] = { 
+					{0,1,3},
+					{0,2,3},
+					{1,2,3},
+					{0,1,2}
+					};
+
+
+	list<size_t> connectedMap;
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+
+
+		bool faceConnected;
+		faceConnected=false;
+		for(unsigned int uj=0;uj<4;uj++)
+		{
+			
+			//Get the tetrahedra coincident to an initial vertex
+			//on a given face
+			connectedMap = tetLookup[tetrahedra[ui].p[faceMap[uj][0]]];
+
+			ASSERT(connectedMap.size());
+			for(unsigned int uk=1;uk<3;uk++)
+			{
+				size_t nextVert;
+				nextVert=tetrahedra[ui].p[faceMap[uj][uk]];
+				//do a knockout of all tetrahedra who were initially
+				//connected, but are not connected to the current vertex
+				for(list<size_t>::iterator it=connectedMap.begin(); 
+						 	 it!=connectedMap.end();++it)
+				{
+					ASSERT(tetLookup[nextVert].size());
+					if(find(tetLookup[nextVert].begin(),tetLookup[nextVert].end(),(*it)) == 
+						tetLookup[nextVert].end())
+					{
+						it=connectedMap.erase(it);
+						--it;
+					}
+				}
+			}
+			
+			
+			if(!(connectedMap.size() == 2 ||
+				connectedMap.size() ==1))
+			{
+				//this tetrahedron is multiply connected. 
+				//(remember, that itself is included in the above test)
+				//Thats screwed up.
+				tets.push_back(ui);
+			}
+
+			if(connectedMap.size() == 2)
+				faceConnected=true;
+				
+
+
+		}
+
+		if(!faceConnected)
+		{
+			tets.push_back(ui);
+		}
+	}
+
+	return;	
+}
+
+bool Mesh::isTetFullyConnected(unsigned int &badTet) const
+{
+	using std::list;
+
+	//In a fully connected mesh, every tetrahedron must be 
+	//matched to at least one other tetrahedron by  a face
+
+	//By definition, this is true for a zero or singly sized mesh	
+	if(tetrahedra.size() <=1)
+		return true;
+
+	//Create a lookup table of vertices -> tetrahedra
+	vector<list<size_t> > tetLookup;
+	tetLookup.resize(nodes.size());
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+		for(unsigned int uj=0;uj<4;uj++)
+			tetLookup[tetrahedra[ui].p[uj]].push_back(ui);
+	}
+
+	//Now loop through the tetrahedra, and using the previously constructed
+	//table, do a lookup to check that there is exactly one or zero attached 
+	//tetrahedra to each face, and that at least one face has an attached tetrahedron
+	
+	//Map that tells us the vertex indices that are incident to each face
+	unsigned int faceMap[4][3] = { 
+					{0,1,3},
+					{0,2,3},
+					{1,2,3},
+					{0,1,2}
+					};
+
+
+	list<size_t> connectedMap;
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+
+
+		bool faceConnected;
+		faceConnected=false;
+		for(unsigned int uj=0;uj<4;uj++)
+		{
+			
+			//Get the tetrahedra coincident to an initial vertex
+			//on a given face
+			connectedMap = tetLookup[tetrahedra[ui].p[faceMap[uj][0]]];
+
+			ASSERT(connectedMap.size());
+			for(unsigned int uk=1;uk<3;uk++)
+			{
+				size_t nextVert;
+				nextVert=tetrahedra[ui].p[faceMap[uj][uk]];
+				//do a knockout of all tetrahedra who were initially
+				//connected, but are not connected to the current vertex
+				for(list<size_t>::iterator it=connectedMap.begin(); 
+						 	 it!=connectedMap.end();++it)
+				{
+					ASSERT(tetLookup[nextVert].size());
+					if(find(tetLookup[nextVert].begin(),tetLookup[nextVert].end(),(*it)) == 
+						tetLookup[nextVert].end())
+					{
+						it=connectedMap.erase(it);
+						--it;
+					}
+				}
+			}
+			
+			
+			if(!(connectedMap.size() == 2 ||
+				connectedMap.size() ==1))
+			{
+				//this tetrahedron is multiply connected. 
+				//(remember, that itself is included in the above test)
+				//Thats screwed up.
+				badTet=ui;
+				return false;
+			}
+
+			if(connectedMap.size() == 2)
+			{
+				faceConnected=true;
+				break;
+			}
+				
+
+
+		}
+
+		if(!faceConnected)
+		{
+			badTet=ui;
+			return false;
+		}
+	}
+
+	return true;	
+}
+
+void Mesh::removeDuplicateTris()
+{
+	ASSERT(isSane());
+
+	using std::list;
+	vector<size_t> dups;
+
+	//Create a listing of all the triangles incident to each node
+	vector<list<unsigned int> > vl;
+	vl.resize(nodes.size());
+
+	for(unsigned int ui=0;ui<triangles.size();ui++)
+	{
+		for(unsigned int uj=0;uj<3;uj++)
+			vl[triangles[ui].p[uj]].push_back(ui);
+	}
+
+	for(unsigned int ui=0;ui<vl.size();ui++)
+	{
+		for(list<unsigned int>::iterator it=vl[ui].begin();
+				it!=vl[ui].end();++it)
+		{
+			//Examine the triangle that is coincident on this vertex, then
+			//see if the other triangles on this vertex are duplicates 
+			for(list<unsigned int>::iterator itJ=it;
+					itJ!=vl[ui].end();++itJ)
+			{
+				if(itJ == it)
+					continue;
+
+				if(sameTriangle(*it,*itJ))
+				{
+					if(std::find(dups.begin(),dups.end(),*itJ) == dups.end())
+						dups.push_back(*itJ);
+				}
+			}
+			
+		}
+	}
+	
+	for(unsigned int ui=dups.size();ui--;)
+	{
+		std::swap(triangles[dups[ui]],triangles.back());
+		triangles.pop_back();
+	}
+}
+
+void Mesh::mergeDuplicateVertices(float tol)
+{
+	using std::list;
+	using std::pair;
+	vector<size_t> thisDup;
+	vector<pair<size_t,vector<size_t> > > dups;
+
+	//Find the duplicates 
+	// placing duplicates into a list of sorted vectors of duplicate indices
+	findNearVerticies(tol,nodes,dups);
+	for(size_t ui=0;ui<dups.size();ui++)
+	{
+		std::sort(dups[ui].second.begin(),
+				dups[ui].second.end());
+	}
+
+	for(vector<pair<size_t,vector<size_t> > >::iterator it=dups.begin();
+		it!=dups.end();++it)
+	{
+		//replace the points
+		vector<size_t>::iterator itJ;
+		
+		for(size_t ui=0;ui<points.size();ui++)
+		{
+			itJ=find(it->second.begin(),it->second.end(),points[ui]);
+			if(itJ !=it->second.end())
+				points[ui]=it->first;
+		}
+		
+		//replace the lines 
+		for(size_t ui=0;ui<lines.size();ui++)
+		{
+			for(size_t uj=0;uj<2;uj++)
+			{
+				itJ=find(it->second.begin(),it->second.end(),lines[ui].p[uj]);
+				if(itJ !=it->second.end())
+					lines[ui].p[uj]=it->first;
+			}
+		}
+		
+		//replace the triangles 
+		for(size_t ui=0;ui<triangles.size();ui++)
+		{
+			for(size_t uj=0;uj<3;uj++)
+			{
+				itJ=find(it->second.begin(),it->second.end(),triangles[ui].p[uj]);
+				if(itJ !=it->second.end())
+					triangles[ui].p[uj]=it->first;
+			}
+		}
+	
+		//replace the tetrahedra
+		for(size_t ui=0;ui<tetrahedra.size();ui++)
+		{
+			for(size_t uj=0;uj<4;uj++)
+			{
+				itJ=find(it->second.begin(),it->second.end(),tetrahedra[ui].p[uj]);
+				if(itJ !=it->second.end())
+					tetrahedra[ui].p[uj]=it->first;
+			}
+		}
+	}
+	ASSERT(isSane());	
+
+
+	//obtain the set of vertices that have now been orphaned
+	vector<size_t> toRemove;
+	for(vector<pair<size_t,vector<size_t> > >::iterator it=dups.begin();
+		it!=dups.end();++it)
+	{
+		for(size_t ui=0;ui<it->second.size();ui++)
+			toRemove.push_back(it->second[ui]);
+	}
+
+	std::sort(toRemove.begin(),toRemove.end());
+	//hokay, so all we need to do is remove orphans
+	killOrphanNodes(toRemove);
+	
+	ASSERT(isSane());	
+}
+
+
+void Mesh::killOrphanNodes()
+{
+	vector<bool> referenced(nodes.size(),false);
+	for(size_t ui=0;ui<points.size();ui++)
+		referenced[points[ui]] =true;
+
+	for(size_t ui=0;ui<lines.size();ui++)
+		for(size_t uj=0;uj<2;uj++)
+		referenced[lines[ui].p[uj]] =true;
+
+	for(size_t ui=0;ui<triangles.size();ui++)
+		for(size_t uj=0;uj<3;uj++)
+			referenced[triangles[ui].p[uj]] =true;
+
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+		for(size_t uj=0;uj<4;uj++)
+			referenced[tetrahedra[ui].p[uj]] =true;
+
+	vector<size_t> orphans;
+
+	for(size_t ui=0;ui<referenced.size();ui++)
+	{
+		if(!referenced[ui])
+		{
+			orphans.push_back(ui);
+		}
+	}
+
+	if(orphans.size())
+		killOrphanNodes(orphans);
+	ASSERT(isSane());
+}
+
+
+unsigned int Mesh::numDupTris() const
+{
+	ASSERT(isSane());
+
+	using std::list;
+	vector<size_t> dups;
+
+	//Create a listing of all the triangles incident to each node
+	vector<list<unsigned int> > vl;
+	vl.resize(nodes.size());
+
+	for(unsigned int ui=0;ui<triangles.size();ui++)
+	{
+		for(unsigned int uj=0;uj<3;uj++)
+			vl[triangles[ui].p[uj]].push_back(ui);
+	}
+
+	for(unsigned int ui=0;ui<vl.size();ui++)
+	{
+		for(list<unsigned int>::iterator it=vl[ui].begin();
+				it!=vl[ui].end();++it)
+		{
+			//Examine the triangle that is coincident on this vertex, then
+			//see if the other triangles on this vertex are duplicates 
+			for(list<unsigned int>::iterator itJ=it;
+					itJ!=vl[ui].end();++itJ)
+			{
+				if(itJ == it)
+					continue;
+
+				if(sameTriangle(*it,*itJ))
+				{
+					if(std::find(dups.begin(),dups.end(),*itJ) == dups.end())
+						dups.push_back(*itJ);
+				}
+			}
+			
+		}
+	}
+
+	return dups.size();	
+}
+
+unsigned int Mesh::numDupVertices(float tolerance) const
+{
+	float sqrTol;
+	sqrTol = tolerance*tolerance;
+
+	
+	unsigned int numDups=0;
+
+	//TODO: Non brute force approach (k3d-mk2)
+#pragma omp parallel for reduction(+:numDups)
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		for(size_t uj=0;uj<nodes.size();uj++)
+		{
+			if(ui == uj)
+				continue;
+
+			if (nodes[ui].sqrDist(nodes[uj]) < sqrTol)
+				numDups++;
+		}
+	}
+
+	return numDups;
+}
+
+void Mesh::clear()
+{
+	nodes.clear();
+	physGroupNames.clear();
+	tetrahedra.clear();
+	triangles.clear();
+	lines.clear();
+	points.clear();
+}
+
+void Mesh::removeStrayTris()
+{
+	//NOTE: This algorithm is non-functional in the case whereby
+	//we have triangles where two other triangles have matched edges against
+	//a "primary" triangle. This is a "non-conformal" mesh or somesuch, and I don't
+	//like them.
+	
+	//This algorithm is  also extremely inefficient
+	//Could be better off constructing an edge list
+	//and then querying that.
+	vector<size_t> trianglesToKill;
+
+	unsigned int triKillCount;
+	do
+	{
+		triKillCount=0;
+		std::cerr << "Pass..." << std::endl;
+		trianglesToKill.clear();
+		#pragma omp parallel for
+		for(size_t ui=0;ui<triangles.size();ui++)
+		{
+			bool coincident[3];
+			coincident[0]=coincident[1]=coincident[2]=false;
+		
+			for(size_t uj=0;uj<triangles.size();uj++)
+			{
+				//Disallow self-coincidence matching
+				if(ui == uj)
+					continue;
+
+				for(unsigned int uk=0;uk<3;uk++)
+				{
+					//opposite vertex, winding in clockwise fashion
+					unsigned int nextVert;
+					nextVert=(uk+1)%3;
+
+					for(unsigned int um=0;um<3;um++)
+					{
+						unsigned int nextVertTwo;
+
+						nextVertTwo = (um+1)%3;
+
+						//Check reverse co-incidence
+						if(
+							(triangles[ui].p[nextVert] == triangles[uj].p[nextVertTwo] && 
+							  triangles[ui].p[uk] == triangles[uj].p[um]) ||
+							(triangles[ui].p[nextVert] == triangles[uj].p[um] &&
+							  triangles[ui].p[uk] == triangles[uj].p[nextVertTwo])
+							)
+
+						{
+							coincident[edgeIdx(uk,nextVert)]=true;
+
+						}
+						
+					}
+
+					
+				}
+
+				if(coincident[0] && coincident[1] && coincident[2])
+					break;
+
+			}
+		
+		
+			//Check to see if triangle is fully coherent int mesh
+			if(!coincident[0] || !coincident[1] || !coincident[2])
+			{
+				#pragma omp critical
+				trianglesToKill.push_back(ui);
+			}
+		
+		}
+
+		//Reverse sort
+		std::sort(trianglesToKill.begin(),trianglesToKill.end(),std::less<size_t>());
+
+		
+		for(unsigned int ui=0;ui<trianglesToKill.size();ui++)
+		{
+			//Move triangle to kill to back of vector
+			std::swap(triangles[trianglesToKill[ui]],triangles.back());
+			//pop vector
+			triangles.pop_back();
+
+			triKillCount++;
+		}
+
+		std::cerr << "Killed" << triKillCount << " stray triangles" << std::endl;
+	} while(triKillCount);
+}
+
+
+//Re-surface the mesh by placing triangles over the top of tetrahedra that are not sharing
+//a face
+//This could be a lot more efficient
+void Mesh::resurface(unsigned int newPhys)
+{
+	ASSERT(isSane());
+
+	//Loop through the tetrahedra in the mesh
+	//to determine if it is connected to the 
+	using std::list;
+
+	//In a fully connected mesh, every tetrahedron must be 
+	//matched to at least one other tetrahedron by  a face
+
+	//Create a lookup table of vertices -> tetrahedra
+	//and vertices->triangles
+	vector<list<size_t> > tetLookup;
+	vector<list<size_t> > triLookup;
+	
+	tetLookup.resize(nodes.size());
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+		for(unsigned int uj=0;uj<4;uj++)
+			tetLookup[tetrahedra[ui].p[uj]].push_back(ui);
+	}
+	
+	triLookup.resize(nodes.size());
+	for(size_t ui=0; ui<triangles.size();ui++)
+	{
+		for(unsigned int uj=0;uj<3;uj++)
+			triLookup[triangles[ui].p[uj]].push_back(ui);
+	}	
+	
+	//Now loop through the tetrahedra, and using the previously constructed
+	//table, do a lookup to check that there is exactly one or zero attached 
+	//tetrahedra to each face, and that at least one face has an attached tetrahedron
+	
+	//Map that tells us the vertex indices that are incident to each face
+	unsigned int faceMap[4][3] = { 
+					{0,1,3},
+					{0,2,3},
+					{1,2,3},
+					{0,1,2}
+					};
+
+	vector<std::pair<size_t,unsigned int> > triMaps;
+
+	list<size_t> connectedMap;
+	std::cerr << "Examining " << tetrahedra.size() << " tetrahedra " << std::endl;
+	//Draw a progress bar
+	unsigned int lastFrac=0;
+	std::cerr << std::endl << "|";
+	for(unsigned int ui=0; ui<100; ui++)
+		std::cerr << ".";
+	std::cerr << "| 100%" << std::endl << "|.";
+
+	for(size_t ui=0; ui<tetrahedra.size();ui++)
+	{
+
+		for(unsigned int uj=0;uj<4;uj++)
+		{
+			//Get the tetrahedra coincident to an initial vertex
+			//on a given face
+			connectedMap = tetLookup[tetrahedra[ui].p[faceMap[uj][0]]];
+
+			ASSERT(connectedMap.size());
+			for(unsigned int uk=1;uk<3;uk++)
+			{
+				size_t nextVert;
+				nextVert=tetrahedra[ui].p[faceMap[uj][uk]];
+				//do a knockout of all tetrahedra who were initially
+				//connected, but are not connected to the current vertex
+				for(list<size_t>::iterator it=connectedMap.begin(); 
+						 	 it!=connectedMap.end();++it)
+				{
+					ASSERT(tetLookup[nextVert].size());
+					if(find(tetLookup[nextVert].begin(),tetLookup[nextVert].end(),(*it)) == 
+						tetLookup[nextVert].end())
+					{
+						it=connectedMap.erase(it);
+						--it;
+					}
+				}
+			}
+			
+
+
+			//Should either face another tet, or be exposed	
+			//note that itself is counted in the connectedMap
+			ASSERT((connectedMap.size() == 2 ||
+				connectedMap.size() ==1));
+
+			if(connectedMap.size() == 1)
+			{
+				//OK, the only tetrahedron that shares
+				//this face is itself, so it does not share
+				//a face with another tet.
+				//However it could be exposed
+				//or covered by an existing triangle
+
+
+				//See if the triangles are attached to this face
+				vector<size_t> tetFaceNodes;
+				tetFaceNodes.clear();
+				for(unsigned int uk=0;uk<3;uk++)
+					tetFaceNodes.push_back(tetrahedra[ui].p[faceMap[uj][uk]]);
+
+				std::sort(tetFaceNodes.begin(),tetFaceNodes.end());
+
+
+				vector<size_t> attachedTris;
+				//Loop over each vertex on this face, creating a list of incident
+				//triangles to this vertex
+				for(unsigned int uk=0;uk<3;uk++)
+				{
+					size_t vertex;
+					vertex = tetrahedra[ui].p[faceMap[uj][uk]];
+					for(list<size_t>::iterator it=triLookup[vertex].begin();
+							it!=triLookup[vertex].end();++it)
+					{
+						//Push on list of attached triangles, if not prev.
+						//seen
+						if(find(attachedTris.begin(),attachedTris.end(),
+									*it) == attachedTris.end())
+							attachedTris.push_back(*it);
+					}
+				}
+
+				//So now we have  list of triangles that are incident to
+				//any vertex on this face. now, we need to look through these
+				//to determine if any triangle is incident to this face
+				vector<size_t> triNodes;
+				triNodes.resize(3);
+				bool triClothedFace;
+				triClothedFace=false;
+				for(unsigned int uk=0;uk<attachedTris.size();uk++)
+				{
+					for(unsigned int um=0;um<3;um++)
+						triNodes[um] = triangles[attachedTris[uk]].p[um];
+
+					std::sort(triNodes.begin(),triNodes.end());
+
+					if(std::equal(triNodes.begin(),triNodes.end(),tetFaceNodes.begin()))
+					{
+						triClothedFace=true;
+						break;
+					}
+				}
+
+				if(!triClothedFace)
+					triMaps.push_back(std::make_pair(ui,uj));
+
+			}
+				
+		}
+
+
+		if((unsigned int)(((float)ui*100.0f)/(float)tetrahedra.size()) > lastFrac)
+		{
+			std::cerr << ".";
+			lastFrac++;
+		}	
+	}
+
+	while(lastFrac++ < 100)
+		std::cerr << ".";
+	std::cerr << "|";	
+
+
+	ASSERT(triMaps.size() < tetrahedra.size());
+	//Trimaps now holds the mapping of all the tetrahedra that are exposed
+	//but have no covering triangle. Let us apply a surface to this triangle
+	//using the physical group specified above
+	std::cerr << "Found " << triMaps.size() << " uncovered tetrahedra " << std::endl;	
+
+	vector<std::pair<unsigned int, size_t> > curPhys;
+	getCurPhysGroups(curPhys);
+	std::cerr << "DEBUG : Found " << curPhys.size() << " physical groups " << std::endl;
+	for(unsigned int ui=0;ui<curPhys.size(); ui++)
+		std::cerr << "\t" << curPhys[ui].first << " : " << curPhys[ui].second << std::endl;
+
+
+	BoundCube nakedTetsBound;
+	nakedTetsBound.setInverseLimits();
+
+	if(triMaps.size())
+	{
+		nakedTetsBound.setBounds(nodes[tetrahedra[triMaps[0].first].p[0]],
+					nodes[tetrahedra[triMaps[0].first].p[1]]);
+		for(unsigned int ui=0;ui<triMaps.size();ui++)
+		{
+
+			for(unsigned int uj=0;uj<4; uj++)
+				nakedTetsBound.expand(nodes[tetrahedra[triMaps[ui].first].p[uj]]);
+		}
+	}
+	std::cerr << "Bounding box : " << nakedTetsBound << std::endl;
+
+	triangles.reserve(triangles.size()+triMaps.size());
+	for(unsigned int ui=0;ui<triMaps.size();ui++)
+	{
+		//Look up the specified tetrahedron in the map
+		//and the associated face. Use this to construct
+		//the new triangle
+
+		TRIANGLE t;
+
+		t.p[0] =tetrahedra[triMaps[ui].first].p[faceMap[triMaps[ui].second][0]];
+		t.p[1] =tetrahedra[triMaps[ui].first].p[faceMap[triMaps[ui].second][1]];
+		t.p[2] =tetrahedra[triMaps[ui].first].p[faceMap[triMaps[ui].second][2]];
+
+		t.physGroup=newPhys;
+		triangles.push_back(t);
+	}
+	
+}
+
+void Mesh::setTriangleMesh(const std::vector<float> &ptsX, 
+		const std::vector<float> &ptsY, const std::vector<float> &ptsZ)
+{
+	//Incoming data streams should describe triangles
+	ASSERT(ptsX.size() == ptsY.size() && ptsY.size() == ptsZ.size());
+	ASSERT(ptsX.size() %3 == 0);
+
+	clear();
+
+
+	vector<Point3D> ptVec;
+	ptVec.resize(ptsX.size());
+#pragma omp parallel for 
+	for(size_t ui=0;ui<ptsX.size();ui++)
+		ptVec[ui]=Point3D(ptsX[ui],ptsY[ui],ptsZ[ui]);
+
+	
+	const float MAX_SQR_RAD=0.001f;
+	std::list<std::pair<size_t,vector<size_t> > > clusterList;
+	findNearVerticies(MAX_SQR_RAD,ptVec,clusterList);
+
+
+	//FIXME: This is totally inefficient.
+	
+	//Now, we have a vector of pts, each group of 3 corresponding to 1 triangle
+	//and we have the mapping for the new triangles
+       	vector<size_t > triangleMapping;
+
+	triangleMapping.resize(ptVec.size());
+#pragma omp parallel for
+	for(size_t ui=0;ui<ptVec.size(); ui++)
+		triangleMapping[ui]=ui;
+
+	//Now run through the original mapping, and generate the new modified mapping.
+	//this maps old point -> "rally pt"
+	for(std::list<std::pair<size_t, vector<size_t> > >::iterator it=clusterList.begin();it!=clusterList.end();++it)
+	{
+		for(size_t uj=0;uj<it->second.size();uj++)
+			triangleMapping[it->second[uj]]=it->first;
+	}
+
+
+	//now we have to do an additional step. When we create the new node vector, we are going to exclude points
+	//that are no longer referenced. So, to do this (inefficiently), we need to know how many times each point was referenced
+	//if it was referenced zero times, we have to modify the triangle mapping for any nodes of higher index
+	
+
+	vector<size_t> refCount;
+	refCount.resize(ptVec.size(),0);
+	for(size_t ui=0;ui<triangleMapping.size();ui++)
+		refCount[triangleMapping[ui]]++;
+
+	//modify the triangle mapping such that it points from ptVec->nodeVec (ie the indices of the pts, after dropping unreferenced pts)
+	size_t delta=0;
+	vector<size_t> numPtsDropped;
+	for(size_t ui=0;ui<refCount.size();ui++)
+	{
+		numPtsDropped.push_back(delta);
+		if(!refCount[ui])
+		{
+			delta++;
+			continue;
+		}
+		nodes.push_back(ptVec[ui]);
+	}
+
+
+	//Build the triangle vector
+	for(size_t ui=0;ui<triangleMapping.size()/3; ui++)
+	{
+		size_t offset;
+		offset=ui*3;
+		for(size_t uj=0;uj<3;uj++)
+		{
+			TRIANGLE t;
+
+			//Build the triangle from the triangle mapping, accounting for the shift due
+			//to dropped points that have been "clustered"
+			t.p[0]=triangleMapping[offset]-numPtsDropped[triangleMapping[offset]] ;
+			t.p[1]=triangleMapping[offset+1] - numPtsDropped[triangleMapping[offset+1]];
+			t.p[2]=triangleMapping[offset+2]- numPtsDropped[triangleMapping[offset+2]];
+			triangles.push_back(t);
+		}
+	}
+
+	std::cerr << "Input size of " << ptVec.size() << std::endl;
+	std::cerr << "found " << clusterList.size() << " shared nodes"  << std::endl;
+
+	ASSERT(isSane());
+
+	std::cerr << "Appears to be sane?? " << std::endl;
+}
+
+/*
+unsigned int Mesh::loadGmshMesh(const char *meshFile, unsigned int &curLine, bool allowBadMeshes)
+{
+	//COMPILE_ASSERT(THREEDEP_ARRAYSIZE(MESH_LOAD_ERRS) == MESH_LOAD_ENUM_END);
+	vector<string> strVec;	
+
+	tetrahedra.clear();
+	triangles.clear();
+	lines.clear();
+	points.clear();
+	nodes.clear();
+
+	curLine=0;
+	std::ifstream f(meshFile);
+
+	if(!f)
+		return 1;
+
+	curLine=1;
+	string line;
+
+	//Read file header
+	//---
+	getline(f,line);
+	if(f.fail())
+		return 1;
+
+	//Check first line is "$MeshFormat"
+	if(line != "$MeshFormat")
+		return 1;
+
+	getline(f,line);
+	curLine++;
+	if(f.fail())
+		return 1;
+
+	//Second line should be versionNumber file-type data-size
+	splitStrsRef(line.c_str(),' ',strVec);
+
+	if(strVec.size() != 3)
+		return 1;
+
+	//Only going to allow version 2.0 && 2.1 && 2.2; can't guarantee other versions....
+	if(!(strVec[0] == "2.1" || strVec[0] =="2" || strVec[0] == "2.2"))
+		return 1;
+
+	//file-type should be "0" (for ascii file)
+	//Number of bytes in a double is third arg; but I will skip it
+	if(strVec[1] != "0")
+		return 1;
+
+	getline(f,line);
+	curLine++;
+	if(f.fail())
+		return 1;
+	if(line != "$EndMeshFormat")
+		return 1;
+	//--------
+
+	//Read the nodes header
+	getline(f,line);
+	curLine++;
+	if(f.fail())
+		return 1;
+
+	if(line != "$Nodes")
+		return 1;
+
+	getline(f,line);
+	curLine++;
+	if(f.fail())
+		return 1;
+
+	unsigned int nodeCount;
+	if(stream_cast(nodeCount,line))
+		return 1;
+
+	std::cerr << "reading node coords " << std::endl;
+	//Read the node XYZ coords
+	do
+	{
+		getline(f,line);
+		curLine++;
+	
+		if(f.fail())
+			return 1;
+
+		if(line == "$EndNodes")
+			break;
+
+		splitStrsRef(line.c_str(),' ',strVec);
+
+		if(strVec.size() < 4)
+			return 1;
+
+		Point3D pt;
+		if(stream_cast(pt[0],strVec[1]))
+			return 1;
+
+		if(stream_cast(pt[1],strVec[2]))
+			return 1;
+
+		if(stream_cast(pt[2],strVec[3]))
+			return 1;
+
+
+		nodes.push_back(pt);
+	}
+	while(!f.eof());
+
+
+	if(f.eof())
+		return 1;
+	//Read the elements header
+	getline(f,line);
+	curLine++;
+	if(f.fail())
+		return 1;
+
+	if(line != "$Elements")
+		return 1;
+
+	getline(f,line);
+	curLine++;
+	if(f.fail())
+		return 1;
+
+	unsigned int elementCount;
+	if(stream_cast(elementCount,line))
+		return 1;
+
+
+	std::cerr << "Reading Element data" << std::endl;
+	//Read the element data
+	do
+	{
+		getline(f,line);
+		curLine++;
+	
+		if(f.fail())
+			return 1;
+
+		if(line == "$EndElements")
+			break;
+
+		splitStrsRef(line.c_str(),' ',strVec);
+
+		if(strVec.size() < 3)
+			return 1;
+
+		unsigned int numTags,elemType;
+		stream_cast(numTags,strVec[2]);
+		stream_cast(elemType,strVec[1]);
+		
+		bool badNode;
+		badNode=false;
+
+		switch(elemType)
+		{
+			case ELEM_SINGLE_NODE_POINT:
+			{
+				if(strVec.size() - numTags < 4)
+					return 2;
+
+				unsigned int ptNum;
+				stream_cast(ptNum,strVec[strVec.size() -1]);
+				ptNum--;
+				points.push_back(ptNum);
+				break;
+			}
+			case ELEM_TWO_NODE_LINE:
+			{
+				if(strVec.size()-numTags < 5)
+					return 2;
+
+				LINE l;
+
+				if(stream_cast(l.physGroup,strVec[3]))
+					return 1;
+				if(stream_cast(l.p[0],strVec[strVec.size() -2]))
+					return 1;
+				if(stream_cast(l.p[1],strVec[strVec.size() -1]))
+					return 1;
+
+				if( l.p[0] == l.p[1])
+				{
+					if(allowBadMeshes)
+					{
+						badNode=true;
+						std::cerr << "WARNING: Bad mesh line element at file line " << curLine << std::endl;
+					}
+					else
+						return 1;
+				}
+				//Convert from 1-index to zero index notation
+				l.p[0]--;
+				l.p[1]--;
+				lines.push_back(l);
+				break;
+			}
+			case ELEM_THREE_NODE_TRIANGLE:
+			{
+				if(strVec.size()-numTags < 6)
+					return 2;
+
+				TRIANGLE t;
+				if(stream_cast(t.physGroup,strVec[3]))
+					return 1;
+				if(stream_cast(t.p[0],strVec[strVec.size() -3]))
+					return 1;
+				if(stream_cast(t.p[1],strVec[strVec.size() -2]))
+					return 1;
+				if(stream_cast(t.p[2],strVec[strVec.size() -1]))
+					return 1;
+
+				if( t.p[0] == t.p[1] || t.p[1] == t.p[2] ||
+						t.p[2] == t.p[0])
+				{
+					if(allowBadMeshes)
+					{
+						badNode=true;
+						std::cerr << "WARNING: Bad mesh triangle at line " << curLine << std::endl;
+					}
+					else
+						return 1;
+				}
+				if(!badNode)
+				{
+					//Convert from 1-index to zero index notation
+					t.p[0]--;
+					t.p[1]--;
+					t.p[2]--;
+					triangles.push_back(t);
+				}
+				break;
+			}
+			case ELEM_FOUR_NODE_TETRAHEDRON:
+			{
+				if(strVec.size()-numTags < 7)
+					return 2;
+
+				TETRAHEDRON t;
+				if(stream_cast(t.physGroup,strVec[3]))
+					return 1;
+				if(stream_cast(t.p[0],strVec[strVec.size() -4]))
+					return 1;
+				if(stream_cast(t.p[1],strVec[strVec.size() -3]))
+					return 1;
+				if(stream_cast(t.p[2],strVec[strVec.size() -2]))
+					return 1;
+				if(stream_cast(t.p[3],strVec[strVec.size() -1]))
+					return 1;
+
+				for(unsigned int ui=0;ui<4; ui++)
+				{
+					for(unsigned int uj=0;uj<4;uj++)
+					{
+						if(ui == uj)
+							continue;
+
+						if( t.p[ui] == t.p[uj])
+						{
+							if(allowBadMeshes)
+							{
+								std::cerr << "WARNING: Bad mesh tetrahedron at line " << curLine << std::endl;
+								badNode=true;
+							}
+							else
+								return 1;
+						}
+					}
+				}
+
+				if(!badNode)
+				{
+					//Convert from 1-index to zero index notation
+					t.p[0]--;
+					t.p[1]--;
+					t.p[2]--;
+					t.p[3]--;
+					tetrahedra.push_back(t);
+				}
+				break;
+			}
+			default:
+				return 3;
+		}
+
+
+
+
+	}while(!f.eof());
+
+
+	//Do some final checks - element count can only be under-counted by our class
+	// as there may be some primitives we don't support. However, it should be
+	// never over counted
+	if(!allowBadMeshes)
+	{
+		if(elementCount < triangles.size() + lines.size() + points.size() + tetrahedra.size() )
+			return MESH_LOAD_BAD_ELEMENTCOUNT;
+
+		if(nodeCount != nodes.size())
+			return MESH_LOAD_BAD_NODECOUNT;
+	}
+
+	if(!isSane())
+		return MESH_LOAD_IS_INSANE;
+
+	return 0;
+}
+*/
+
+unsigned int Mesh::countTriNodes() const
+{
+	vector<size_t> touchedNodes;
+
+	touchedNodes.resize(triangles.size()*3);
+	//Build monolithic list
+#pragma omp parallel for
+	for(size_t ui=0;ui<triangles.size(); ui++)
+	{
+		touchedNodes[ui*3]=triangles[ui].p[0];
+		touchedNodes[ui*3+1]=triangles[ui].p[1];
+		touchedNodes[ui*3+2]=triangles[ui].p[2];
+	}
+
+	//Remove non-unique entries
+	vector<size_t>::iterator it;
+	std::sort(touchedNodes.begin(),touchedNodes.end());
+	it=std::unique(touchedNodes.begin(),touchedNodes.end());
+
+	//TODO: Test me...
+	touchedNodes.resize(it-touchedNodes.begin());
+
+	return touchedNodes.size();
+}
+
+void Mesh::reassignGroups(unsigned int newPhys)
+{
+#pragma omp parallel for
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+		tetrahedra[ui].physGroup = newPhys;
+	
+#pragma omp parallel for
+	for(size_t ui=0;ui<triangles.size();ui++)
+		triangles[ui].physGroup = newPhys;
+
+#pragma omp parallel for
+	for(size_t ui=0;ui<lines.size();ui++)
+		lines[ui].physGroup = newPhys;
+}
+
+unsigned int Mesh::saveGmshMesh(const char *meshFile) const
+{
+	ASSERT(isSane());
+
+	using std::endl;
+	using std::ofstream;
+
+	ofstream f(meshFile);
+
+	if(!f)
+		return 1;
+
+	f << "$MeshFormat" << endl;
+	f << "2.1 0 8" << endl;
+	f << "$EndMeshFormat" << endl;
+
+
+	f << "$Nodes" << endl;
+
+	f << nodes.size() << endl;
+
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		f << ui+1 << " " << nodes[ui][0] << " " << nodes[ui][1] 
+			<< " " << nodes[ui][2] << std::endl;
+	}
+
+	f << "$EndNodes" << endl;
+
+	f << "$Elements" << endl;
+	f << tetrahedra.size() + triangles.size() + lines.size() + points.size() << endl;
+
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+	{
+		f <<  ui+1 <<  " " << ELEM_FOUR_NODE_TETRAHEDRON << " 3 " << tetrahedra[ui].physGroup <<  " 1 0 " << 
+			tetrahedra[ui].p[0]+1 << " "  << tetrahedra[ui].p[1]+1 << " "
+			 << tetrahedra[ui].p[2]+1 << " " << tetrahedra[ui].p[3]+1 << endl;
+	}
+	
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		f << tetrahedra.size()+ ui+1 <<  " " << ELEM_THREE_NODE_TRIANGLE << " 3 " << triangles[ui].physGroup << " 1 0 " << 
+			triangles[ui].p[0]+1 << " "  << triangles[ui].p[1]+1 << " "
+			 << triangles[ui].p[2]+1 << endl;
+	}
+
+	for(size_t ui=0;ui<lines.size();ui++)
+	{
+		f << tetrahedra.size() + triangles.size() +  ui+1 <<  " " << ELEM_TWO_NODE_LINE << " 3 " << lines[ui].physGroup << " 1 0 " << 
+			lines[ui].p[0]+1 << " "  << lines[ui].p[1]+1 << endl;
+	}
+
+	for(size_t ui=0;ui<points.size();ui++)
+	{
+		f << tetrahedra.size() + triangles.size() + lines.size() +  ui+1 <<  " "
+		       	<< ELEM_SINGLE_NODE_POINT<< " 1 0 " << points[ui]+1 << endl;
+	}
+	f << "$EndElements" << endl;
+
+	return 0;
+}
+
+
+/*
+void Mesh::rotate(const Point3f &axis, const Point3f &origin, float angle)
+{
+	Quaternion q1,q2;
+
+	//generate rotating quat
+	quat_get_rot_quats(&axis,angle,&q1,&q2);
+
+	#pragma omp parallel for
+	for(size_t ui=0; ui<nodes.size();ui++)
+	{
+		Point3f p;
+		p.fx = nodes[ui][0]-origin.fx;
+		p.fy = nodes[ui][1]-origin.fy;
+		p.fz = nodes[ui][2]-origin.fz;
+
+		//use quat
+		quat_rot_apply_quats(&p,&q1,&q2);
+
+		nodes[ui][0] = p.fx;
+		nodes[ui][1] = p.fy;
+		nodes[ui][2] = p.fz;
+
+	}
+}
+*/
+
+void Mesh::translate()
+{
+	Point3D origin(0,0,0);
+	for(size_t ui=0;ui<nodes.size();ui++)
+		origin-=nodes[ui];
+
+	origin*=1.0f/(float)nodes.size();
+
+	translate(origin);
+}
+
+void Mesh::translate(const Point3f &origin)
+{
+	#pragma omp parallel for
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		nodes[ui][0]+=origin.fx;
+		nodes[ui][1]+=origin.fy;
+		nodes[ui][2]+=origin.fz;
+	}
+}
+
+void Mesh::translate(const Point3D &origin)
+{
+	#pragma omp parallel for
+	for(size_t ui=0;ui<nodes.size();ui++)
+		nodes[ui]+=origin;
+}
+
+void Mesh::scale(const Point3f &origin,float scaleFactor)
+{
+	#pragma omp parallel for
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		nodes[ui][0]=(nodes[ui][0]-origin.fx)*scaleFactor + origin.fx;
+		nodes[ui][1]=(nodes[ui][1]-origin.fy)*scaleFactor + origin.fy;
+		nodes[ui][2]=(nodes[ui][2]-origin.fz)*scaleFactor + origin.fz;
+	}
+}
+
+void Mesh::scale(const Point3D &origin,float scaleFactor)
+{
+	#pragma omp parallel for
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		nodes[ui][0]=(nodes[ui][0]-origin[0])*scaleFactor + origin[0];
+		nodes[ui][1]=(nodes[ui][1]-origin[1])*scaleFactor + origin[1];
+		nodes[ui][2]=(nodes[ui][2]-origin[2])*scaleFactor + origin[2];
+	}
+}
+
+void Mesh::scale(float scaleFactor)
+{
+	#pragma omp parallel for
+	for(size_t ui=0;ui<nodes.size();ui++)
+		nodes[ui]*=scaleFactor;
+}
+
+void Mesh::getBounds(BoundCube &b) const
+{
+	b.setBounds(nodes);
+}
+
+void Mesh::refineTetrahedra(vector<size_t> &refineTets)
+{
+
+	//Split the marked tetrahedra into sub-tetrahedra of 4 using a 
+	//internal vertex insertion method. 
+	//Some strategies are available in the PhD thesis of Wessner,
+	//"Mesh Refinement Techniques for TCAD Tools", Vienna
+	//which is available at httpP://www.iue.tuwien.ac.at/phd/wessner/
+	for(unsigned int ui=0;ui<refineTets.size();ui++)
+	{
+	
+		Point3D accum(0,0,0);
+		for(unsigned int uk=0;uk<4;uk++)
+		{
+			//Compute the central vertex to insert
+			accum+=nodes[tetrahedra[refineTets[ui]].p[uk]];
+		}	
+
+		accum*=0.25;
+
+
+		TETRAHEDRON t;
+
+		//T1 := [0,1,4,3],    
+		t.p[0] = tetrahedra[refineTets[ui]].p[0];
+		t.p[1] = tetrahedra[refineTets[ui]].p[1];
+		t.p[2] = nodes.size();
+		t.p[3] = tetrahedra[refineTets[ui]].p[3];
+		tetrahedra.push_back(t);
+
+		//T2 := [0,4,2,3],    
+		t.p[0] = tetrahedra[refineTets[ui]].p[0];
+		t.p[1] = nodes.size();
+		t.p[2] = tetrahedra[refineTets[ui]].p[2];
+		t.p[3] = tetrahedra[refineTets[ui]].p[3];
+		tetrahedra.push_back(t);
+		
+		//T3 := [4,1,2,3],    
+		t.p[0] = nodes.size();
+		t.p[1] = tetrahedra[refineTets[ui]].p[1];
+		t.p[2] = tetrahedra[refineTets[ui]].p[2];
+		t.p[3] = tetrahedra[refineTets[ui]].p[3];
+		tetrahedra.push_back(t);
+
+		//T4 := [0,1,2,4],    
+		t.p[0] = tetrahedra[refineTets[ui]].p[0];
+		t.p[1] = tetrahedra[refineTets[ui]].p[1];
+		t.p[2] = tetrahedra[refineTets[ui]].p[2];
+		t.p[3] = nodes.size();
+		tetrahedra.push_back(t);
+		
+
+		nodes.push_back(accum);
+
+	}
+
+	//Delete the original unrefined tetrahedra, as we have refined these
+	std::sort(refineTets.begin(),refineTets.end(),std::less<size_t>());
+	for(unsigned int ui=0;ui<refineTets.size();ui++)
+	{
+		std::swap(tetrahedra[refineTets[ui]],tetrahedra.back());
+		tetrahedra.pop_back();
+		
+	}
+}
+
+void Mesh::getTriEdgeAdjacencyMap(std::vector<std::list<size_t> > &adj) const
+{
+
+	using std::list;
+
+	adj.resize(triangles.size());
+
+	//Create a lookup table of vertices -> triangles
+	vector<list<size_t> > triLookup;
+	triLookup.resize(nodes.size());
+	for(size_t ui=0; ui<triangles.size();ui++)
+	{
+		for(unsigned int uj=0;uj<3;uj++)
+			triLookup[triangles[ui].p[uj]].push_back(ui);
+	}
+
+
+
+	list<size_t> connectedMap;
+	for(size_t ui=0; ui<triangles.size();ui++)
+	{
+		//Check each vertex pair
+		for(unsigned int uj=0;uj<3;uj++)
+		{
+			size_t v1,v2;
+
+			v1 = triangles[ui].p[uj]; 
+			v2 = triangles[ui].p[(uj+1)%3];
+
+			ASSERT(triLookup[v1].size());
+			ASSERT(triLookup[v2].size());
+
+			//Compute the intersection of the two tri lookup table rows
+			list<size_t> intersect;
+			intersect=triLookup[v1];
+			for(list<size_t>::iterator it=intersect.begin();it!=intersect.end();)
+			{
+				if( find(triLookup[v2].begin(),triLookup[v2].end(),*it) == triLookup[v2].end())
+				{
+					it=intersect.erase(it);
+				}
+				else
+					++it;
+
+			}
+
+			//OK, so the intersection of the two lookups is the triangles attached to the nodes.
+			for(list<size_t>::iterator it=intersect.begin();it!=intersect.end();++it)
+			{
+				//Disallow self adjacency
+				if(*it !=ui)
+					adj[ui].push_back(*it);
+			}
+
+
+		}
+
+	}
+}
+
+unsigned int Mesh::divideMeshSurface(float divisionAngle, unsigned int newPhysGroupStart,
+			const vector<size_t> &physGroupsToSplit)
+{
+	using std::list;
+
+	unsigned int origStart=newPhysGroupStart;
+	//Construct the 
+	vector<list<size_t> > adjacencyMap;
+	vector<bool> touchedTris;
+	vector<size_t> boundaryTris;
+	getTriEdgeAdjacencyMap(adjacencyMap);
+	touchedTris.resize(adjacencyMap.size(),false);
+
+	//OK, so the plan is to pick a triangle (any triangle)
+	//then to expand this out until we hit an edge, as defined by the
+	//angle between adjacent triangle normals.
+	//once we hit the edge, we then don't cross that vertex.
+	//
+	//this algorithm will FAIL (awh new, bro!)
+	//if triangles have more than one neighbour on each edge.
+	
+	//Once we run out of triangles to try (BFS), we then pick one of the "untouched"
+	//tris, and then work from there.
+
+	// Step 1:
+	// 	* Remove any triangles that are not in the physical groups of interest
+	
+	for(size_t ui=0;ui<adjacencyMap.size();ui++)
+	{
+		ASSERT(adjacencyMap[ui].size());
+
+		//Is this triangle in our list?
+		if(find(physGroupsToSplit.begin(),physGroupsToSplit.end(),
+				triangles[ui].physGroup) == physGroupsToSplit.end())
+		{
+			//No? OK, lets kill the adj. list entry at this triangle, we don't need it.
+			adjacencyMap[ui].clear();
+			touchedTris[ui]=true;
+		}
+		else
+		{
+			//It is? Well, remove any triangles that are adjacent, but not in the list.
+			for(list<size_t>::iterator it=adjacencyMap[ui].begin(); it!=adjacencyMap[ui].end(); ++it)
+			{
+				if(find(physGroupsToSplit.begin(),physGroupsToSplit.end(),
+						triangles[*it].physGroup) == physGroupsToSplit.end())
+				{
+					it=adjacencyMap[ui].erase(it);
+					--it;
+				}
+			}
+		}
+	}
+
+
+
+	//OK, so now we have an adjacency list of the interesting phys groups.
+	//Step 2:
+	//	* search for new triangles to group using an expanding boundary method
+
+	BoundCube debugBounds,dbgTmp;
+	debugBounds.setInverseLimits();
+	do
+	{
+		//Find a triangle to use as the "seed"
+		size_t curTri;
+		curTri= find(touchedTris.begin(),touchedTris.end(),false) - touchedTris.begin();
+
+
+		//No more triangles.. all touched.
+		if(curTri == touchedTris.size())
+			break;
+
+		//OK, so now we have a "seed" triangle to work with. 
+		//create an expanding boundary via adjacency.
+
+		size_t groupSize=0;
+		list<size_t> boundary,moreBoundary;
+		boundary.clear();
+		boundary.push_back(curTri);
+
+		std::cerr << "Seeded with triangle # " << curTri << std::endl;
+		touchedTris[curTri]=true; // we touched it.
+		triangles[curTri].physGroup=newPhysGroupStart; // we touched it.
+
+		do
+		{
+			//Expand the boundary using the current boundary triangles
+
+			//loop over the current boundary
+			for(list<size_t>::iterator bIt=boundary.begin();bIt!=boundary.end();++bIt)
+			{
+				ASSERT(adjacencyMap[*bIt].size());
+				//Check the adjacency map of the triangles adjacent to a specific boundary element
+				for(list<size_t>::iterator it=adjacencyMap[*bIt].begin();it!=adjacencyMap[*bIt].end();++it)
+				{
+					if(!touchedTris[*it] && 
+						(normalAngle(*bIt,*it) < divisionAngle || fabs(normalAngle(*bIt,*it,true)) < divisionAngle) )
+					{
+						//Alright then, add this new triangle to the potential new boundary
+						//(let us not add straight away, as we would like to expand in a minimum
+						//perimeter to surface area manner
+						moreBoundary.push_back(*it);
+						touchedTris[*it]=true;
+						triangles[*it].physGroup=newPhysGroupStart;
+
+						dbgTmp.setBounds(nodes[triangles[*it].p[0]],
+								  nodes[triangles[*it].p[1]]);
+						dbgTmp.expand(nodes[triangles[*it].p[2]]);
+
+						debugBounds.expand(dbgTmp);
+						groupSize++;
+
+					}
+				}
+
+			}
+			//exchange the new boundary list with the boundary list
+			boundary.swap(moreBoundary);
+
+			moreBoundary.clear();
+		
+		}
+		while(!boundary.empty());
+
+
+		//Debug: print bounding box
+		std::cerr << "Group size: "<< groupSize << std::endl;
+		std::cerr << debugBounds << std::endl;
+
+		//advance the physical group listing
+		newPhysGroupStart++;
+	}
+	while(true);
+
+
+	//return the number of divided surfaces
+	return newPhysGroupStart-origStart+1;
+
+}
+
+
+
+
+void Mesh::getAttachedComponents(size_t tet, 
+			vector<size_t> &tris, vector<size_t> &l) const
+{
+	ASSERT(tet<tetrahedra.size());
+	for(size_t ui=0;ui<lines.size(); ui++)
+	{
+		int mask;
+		mask=0;
+		//line shares the edges with the tetrahedron?
+		for(unsigned int uj=0;uj<4;uj++)
+		{
+			if(tetrahedra[tet].p[uj] == lines[ui].p[0])
+				mask|=1;
+			if(tetrahedra[tet].p[uj] == lines[ui].p[1])
+				mask|=2;
+		}
+
+		if(mask==3)
+		{
+			//Line is shared.
+			l.push_back(ui);
+		}
+
+	}
+	
+	for(size_t ui=0;ui<triangles.size(); ui++)
+	{
+		int mask;
+		mask=0;
+		//Triangle shares the edges with the tetrahedron?
+		for(unsigned int uj=0;uj<4;uj++)
+		{
+			if(tetrahedra[tet].p[uj] == triangles[ui].p[0])
+				mask|=1;
+			if(tetrahedra[tet].p[uj] == triangles[ui].p[1])
+				mask|=2;
+			if(tetrahedra[tet].p[uj] == triangles[ui].p[2])
+				mask|=4;
+		}
+
+		if(mask==7)
+		{
+			//Triangle  is shared.
+			tris.push_back(ui);
+		}
+
+	}
+}
+
+float Mesh::normalAngle(size_t t1, size_t t2, bool flip) const
+{
+	Point3D nA,nB;
+
+	//Compute nA.
+	getTriNormal(t1,nA);
+	getTriNormal(t2,nB);
+	
+	if(flip)
+		return nA.angle(-nB);
+	//Compute angle
+	return nA.angle(nB);	
+}
+
+void Mesh::getTriNormal(size_t tri,Point3D &p) const
+{
+	ASSERT(tri < triangles.size());
+	p= (nodes[triangles[tri].p[1]] - nodes[triangles[tri].p[0]]);
+	p = p.crossProd(nodes[triangles[tri].p[2]] - nodes[triangles[tri].p[0]]);
+	p.normalise();
+}
+
+void Mesh::getContainedNodes(const BoundCube &b, std::vector<size_t> &res) const
+{
+	ASSERT(!res.size());
+	for(size_t ui=0;ui<nodes.size();ui++)
+	{
+		if(b.containsPt(nodes[ui]))
+			res.push_back(ui);
+	}
+
+}
+
+void Mesh::getIntersectingPrimitives(	std::vector<size_t> &searchNodes,
+					std::vector<size_t> &lineRes,
+					std::vector<size_t> &triangleRes,
+					std::vector<size_t> &tetrahedraRes	) const
+{
+
+	std::sort(searchNodes.begin(),searchNodes.end());
+
+	bool searchFound;
+	ASSERT(lineRes.size() == triangleRes.size() && tetrahedraRes.size() == lineRes.size() && 
+			!tetrahedraRes.size());
+	for(size_t ui=0;ui<lines.size();ui++)
+	{
+		searchFound=false;
+		for(size_t uj=0;uj<2;uj++)
+		{
+			searchFound=std::binary_search(searchNodes.begin(),searchNodes.end(),lines[ui].p[uj]);
+			if(searchFound)
+				break;
+		}
+
+		if(searchFound)
+			lineRes.push_back(ui);
+	}
+	
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		searchFound=false;
+		for(size_t uj=0;uj<3;uj++)
+		{
+			searchFound=std::binary_search(searchNodes.begin(),searchNodes.end(),triangles[ui].p[uj]);
+			if(searchFound)
+				break;
+		}
+
+		if(searchFound)
+			triangleRes.push_back(ui);
+
+	}
+
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+	{
+		searchFound=false;
+		for(size_t uj=0;uj<4;uj++)
+		{
+			searchFound=std::binary_search(searchNodes.begin(),searchNodes.end(),tetrahedra[ui].p[uj]);
+			if(searchFound)
+				break;
+		}
+
+		if(searchFound)
+			tetrahedraRes.push_back(ui);
+
+	}
+
+}
+
+void Mesh::getCurPhysGroups(std::vector<std::pair<unsigned int, size_t> > &curPhys) const
+{
+	ComparePairFirst cmp;
+
+	//TODO: could be more efficient	 by replacing linear search with
+	//boolean one 
+	for(unsigned int ui=0;ui<triangles.size();ui++)
+	{
+		bool found=false;
+		for(unsigned int uj=0;uj<curPhys.size();uj++)
+		{
+			if(curPhys[uj].first== triangles[ui].physGroup)
+			{
+				found=true;
+				curPhys[uj].second++;
+				break;
+			}
+		}
+
+		if(!found)
+		{
+			//we've not seen this before...
+			curPhys.push_back(make_pair(triangles[ui].physGroup,1));
+			std::sort(curPhys.begin(),curPhys.end(),cmp);
+		}
+		
+	}
+/*
+	for(unsigned int ui=0;ui<tetrahedra.size();ui++)
+	{
+		bool found=false;
+		for(unsigned int uj=0;uj<curPhys.size();uj++)
+		{
+			if(curPhys[uj].first== tetrahedra[ui].physGroup)
+			{
+				found=true;
+				curPhys[uj].second++;
+				break;
+			}
+		}
+
+		if(!found)
+		{
+			//we've not seen this before...
+			curPhys.push_back(make_pair(tetrahedra[ui].physGroup,1));
+			std::sort(curPhys.begin(),curPhys.end(),cmp);
+		}
+		
+	}
+*/
+}
+
+void Mesh::erasePhysGroup(unsigned int physGroup, unsigned int typeMask)
+{
+	std::cerr << "Erasing..." <<  typeMask << std::endl;
+	unsigned int eraseCount;
+	if((typeMask & ELEMENT_TRIANGLE) != 0 )
+	{
+		eraseCount=0;
+		//Drop any elements that we don't need
+		for(size_t ui=triangles.size()-1;ui!=0; ui--)
+		{
+			if(triangles[ui].physGroup== physGroup)
+			{
+				std::swap(triangles[ui],*(triangles.end()-(eraseCount+1)));
+				eraseCount++;
+			}
+		}
+
+		std::cerr << "Erasing " << eraseCount << std::endl;	
+		triangles.resize(triangles.size()-eraseCount);
+	}
+
+
+	if(typeMask & ELEMENT_TETRAHEDRON)
+	{
+		eraseCount=0;
+		//Drop any elements that we don't need
+		for(size_t ui=tetrahedra.size()-1;ui!=0; ui--)
+		{
+			if(tetrahedra[ui].physGroup== physGroup)
+			{
+				std::swap(tetrahedra[ui],tetrahedra.back());
+				eraseCount++;
+			}
+		}
+		
+		//drop the tail elements	
+		tetrahedra.resize(tetrahedra.size()-eraseCount);
+	}
+
+	if(typeMask & ELEMENT_LINE)
+	{
+		eraseCount=0;
+		//Drop any elements that we don't need
+		for(size_t ui=tetrahedra.size()-1;ui!=0; ui++)
+		{
+			if(tetrahedra[ui].physGroup== physGroup)
+			{
+				std::swap(tetrahedra[ui],tetrahedra.back());
+				eraseCount++;
+			}
+		}
+		
+		//drop the tail elements	
+		tetrahedra.resize(tetrahedra.size()-eraseCount);
+	}
+
+}
+
+
+//Volume estimation of Zhang and Chen, using overlapping signed tetrahedron
+// method
+// EFFICIENT FEATURE EXTRACTION FOR 2D/3D OBJECTS
+// IN MESH REPRESENTATION. Dept. Electrical and Computer Engineering,
+// Carnegie Mellon University
+// Original URL :  http://amp.ece.cmu.edu/Publication/Cha/icip01_Cha.pdf
+//  Retrieved from internet archive.
+float Mesh::getVolume() const
+{
+	ASSERT(isSane());
+	//ASSERT(isOrientedCoherently());
+
+	//Construct triangle volume estimate
+	//using V = 1.0/6.0* | a.(b x c) | 
+	// where a, b, c are the relative vectors from some vertex d
+	//For us, let d = (0,0,0)
+	float vol=0;
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		Point3D p[3];
+		const TRIANGLE *t;
+		t= &(triangles[ui]);
+
+		for(size_t uj=0;uj<3;uj++)
+			p[uj]=nodes[t->p[uj]];
+
+		float newVol;
+		newVol=p[0].dotProd(p[1].crossProd(p[2]));	
+	
+		ASSERT(newVol > 0.0f);
+		vol+=newVol;
+	}
+
+	vol*=1.0/6.0;
+	std::cerr << "Signed volume :" << vol << std::endl;
+	return fabs(vol);
+}
+
+void Mesh::relax(size_t iterations, float relaxFactor)
+{
+	ASSERT(isSane());
+	using std::pair;
+	vector<vector<size_t> > adjacencyList;
+	adjacencyList.resize(nodes.size());
+	
+	//Compute the adjacency list for each vertex
+	//--
+	for(size_t ui=0;ui<lines.size();ui++)
+	{
+		for(size_t uj=0;uj<2;uj++)
+		{
+			adjacencyList[triangles[ui].p[uj]].push_back(
+						triangles[ui].p[(uj+1)%2]);
+		}
+	}
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		for(size_t uj=0;uj<3;uj++)
+		{
+			adjacencyList[triangles[ui].p[uj]].push_back(
+						triangles[ui].p[(uj+1)%3]);
+
+			adjacencyList[triangles[ui].p[uj]].push_back(
+					 	triangles[ui].p[(uj+2)%3]);
+		}
+	}
+
+	for(size_t ui=0;ui<tetrahedra.size();ui++)
+	{
+		for(size_t uj=0;uj<3;uj++)
+		{
+			adjacencyList[tetrahedra[ui].p[uj]].push_back(
+						tetrahedra[ui].p[(uj+1)%4]);
+
+			adjacencyList[tetrahedra[ui].p[uj]].push_back(
+					 	tetrahedra[ui].p[(uj+2)%4]);
+			adjacencyList[tetrahedra[ui].p[uj]].push_back(
+					 	tetrahedra[ui].p[(uj+3)%4]);
+		}
+	}
+
+	//Measure the mesh volume
+	float origVol;
+	origVol=getVolume();
+
+	//Compute the centroid for the node set
+	Point3D centroid(0,0,0);
+	for(size_t ui=0;ui<nodes.size();ui++)
+		centroid+=nodes[ui];
+	centroid*=1.0f/(float)nodes.size();
+	//Re-centre the mesh around the origin
+	for(size_t ui=0;ui<nodes.size();ui++)
+		nodes[ui]-=centroid;
+
+	//Compute the iteration vertex adjacency weights
+	// each node singly connected to this one is worth 1 weight. 
+	// If it is multiply connected, it is worth more proportionally
+	//-------
+	vector<vector<pair<size_t,float> > > adjacencyFactors;
+	adjacencyFactors.resize(nodes.size());
+
+	for(size_t ui=0;ui<adjacencyList.size();ui++)
+	{
+		size_t factor;
+		std::sort(adjacencyList[ui].begin(),adjacencyList[ui].end());
+	
+		for(size_t uj=0;uj<adjacencyList[ui].size();uj++)
+		{
+			//Skip non-unique entries
+			if(uj && (adjacencyList[ui][uj-1] == adjacencyList[ui][uj]))
+				continue;
+
+			factor=std::count(adjacencyList[ui].begin(),
+					adjacencyList[ui].end(),adjacencyList[ui][uj]);
+		
+			adjacencyFactors[ui].push_back(
+				std::make_pair(adjacencyList[ui][uj],factor));
+		}
+	}
+	adjacencyList.clear();
+	//-------
+
+
+	//Perform main relaxation iteration
+	//---
+	for(size_t it=0;it<iterations;it++)
+	{
+		for(size_t ui=0;ui<adjacencyFactors.size();ui++)
+		{
+			if(adjacencyFactors[ui].empty())
+				continue;
+
+			Point3D nodeV;
+			nodeV=nodes[ui];
+			size_t divisor;
+			divisor=1;
+			for(size_t uj=0;uj<adjacencyFactors[ui].size();uj++)
+			{
+				size_t fact,v;
+				fact=adjacencyFactors[ui][uj].second;
+				v=adjacencyFactors[ui][uj].first;
+
+				nodeV+=(nodes[v]*(float)fact);
+				divisor+=fact;
+			}
+			
+			nodeV*=1.0f/((float)divisor);
+
+			nodes[ui] = (nodeV -nodes[ui])*relaxFactor + nodes[ui];
+		}
+	}	
+
+	//As a *nasty* hack - inflate the mesh such that it will occupy its original volume
+
+
+	//Equate a sphere (4/3*pi*r^3) to a shrunk sphere (4/3*pi*(r+d)^3)
+	// then work out the inflation factor to bring the two volumes into concert. 
+	// This becomes less and less valid, the less spherical the object is...
+	cerr << "Target (original) volume:"  << origVol << endl;
+	for(size_t ui=0;ui<3;ui++)
+	{
+		float inflationDist;
+		float newVol;
+		newVol=getVolume();
+		cerr << "volume before inflation" << ui <<  " :" << newVol << endl;
+		inflationDist = -(pow((3.0/(4.0*M_PI)), (1.0/3.0))*
+			(pow(newVol, (1.0/3.0)) - pow(origVol, (1.0/3.0))));
+		cerr << "volume after inflation" << ui <<  " :" << getVolume()<< endl;
+
+
+		for(size_t ui=0;ui<nodes.size();ui++)
+			nodes[ui].extend(inflationDist);
+	}	
+	//Re-position the mesh back around its original centroid
+	for(size_t ui=0;ui<nodes.size();ui++)
+		nodes[ui]+=centroid;
+}
+
+size_t Mesh::elementCount() const
+{
+	return points.size() + tetrahedra.size() + triangles.size() + lines.size();
+}
+
+//This is a somewhat efficient algorithm to compute if a set of points lies
+//inside or outside a sealed mesh. With work, could be better.
+//Mesh must:
+//	- Consist only of triangles
+//	- Be water tight (no holes in mesh)
+//	- be non-self intersecting
+//	- have coherently oriented triangle normals
+void Mesh::pointsInside(const vector<Point3D> &p,
+			vector<bool> &meshResults, std::ostream &msgs, bool wantProg) const
+{
+//	ASSERT(trianglesFullyConnected()); TODO: Implement me
+	//ASSERT(isOrientedCoherently());
+	ASSERT(!tetrahedra.size());
+
+	Point3D centre=Point3D(0,0,0);;
+	//Find the bounding box of the triangle component of the mesh
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		//create a point cloud consisting of triangle vertices
+		centre+=(nodes[triangles[ui].p[0]]);
+		centre+=(nodes[triangles[ui].p[1]]);
+		centre+=(nodes[triangles[ui].p[2]]);
+	}
+
+	centre=centre*1.0/(3.0f*(float)triangles.size());
+
+	
+	//Find the minimal and maximal distances from the centre of the mesh
+	// to the surface
+	float maxSqrDistance;
+	maxSqrDistance=0; 
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		maxSqrDistance=std::max(maxSqrDistance,
+			centre.sqrDist(nodes[triangles[ui].p[0]]));
+		maxSqrDistance=std::max(maxSqrDistance,
+			centre.sqrDist(nodes[triangles[ui].p[1]]));
+		maxSqrDistance=std::max(maxSqrDistance,
+			centre.sqrDist(nodes[triangles[ui].p[2]]));
+	}
+
+
+	//Two points outside from which we can shoot rays for Jordan 
+	//curve theorem
+	Point3D outsideMesh[2];
+	outsideMesh[0] = centre + Point3D(0,0,1.1)*maxSqrDistance;
+	outsideMesh[1] = centre - Point3D(0,0,1.1)*maxSqrDistance;
+	meshResults.resize(p.size(),false);
+	BoundCube c;
+	c.setBounds(nodes);
+	
+	
+	//Draw a progress bar
+	if(wantProg)
+	{
+		msgs << endl << "|";
+		for(unsigned int ui=0; ui<100; ui++)
+			msgs << ".";
+		msgs << "| 100%" << endl << "|.";
+	}
+
+	size_t actualProg=0;
+	size_t reportedProg=0;
+	size_t curProg=0;
+	size_t progReduce=PROGRESS_REDUCE;
+	//Loop through the point array; generate the final mesh
+#pragma omp parallel for firstprivate(progReduce) 
+	for(unsigned int ui=0;ui<p.size();ui++)
+	{
+		//Do  a quick spherical shell test, 
+		//then a cube test before doing more complex 
+		//polygonal containment test
+		float sqrDist;
+		sqrDist=p[ui].sqrDist(centre);
+		if(sqrDist <= maxSqrDistance && 
+			c.containsPt(p[ui]) )
+		{
+
+			//So the sphere test didn't give us a clear answer.
+			// use a complete polygonal test to check if the point is inside or outside
+			unsigned int intersectionCount;
+			intersectionCount=0;
+			BoundCube rayBound;
+			Point3D *externPt;
+
+			if(p[ui].sqrDist(outsideMesh[0])<
+				p[ui].sqrDist(outsideMesh[1]))
+			{
+				externPt=outsideMesh;
+				rayBound.setBounds(p[ui],outsideMesh[0]);
+			}
+			else
+			{
+				externPt=outsideMesh+1;
+				rayBound.setBounds(p[ui],outsideMesh[1]);
+			}
+
+			//Test each triangle for intersection with the
+			//ray coming from outside the mesh. If even,
+			//point is outside. If odd, inside.
+			for(size_t uj=0;uj<triangles.size();uj++)
+			{
+				Point3D triangle[3];
+				Point3D dummy;
+
+				triangle[0] = nodes[triangles[uj].p[0]];
+				triangle[1] = nodes[triangles[uj].p[1]];
+				triangle[2] = nodes[triangles[uj].p[2]];
+
+				unsigned int intersectType;
+				intersectType = intersect_RayTriangle(*externPt,
+								p[ui],triangle,dummy);
+				if(intersectType == 1) 
+					intersectionCount++;
+			}
+
+			//If is odd, due to boundary crossings, it must be inside
+			if(intersectionCount%2)	
+			{
+				meshResults[ui]=true;
+			}
+		}
+
+		if(wantProg && !progReduce-- )
+		{
+#ifdef _OPENMP
+			if(!omp_get_thread_num())
+			{
+#endif
+			//Compute actual progress
+			actualProg= (unsigned int)(((float)curProg*100.0f)/(float)p.size());
+
+			//Update the user on our actual progress
+			while(reportedProg < actualProg)
+			{
+				msgs << ".";
+				reportedProg++;
+			}
+#ifdef _OPENMP
+			}
+#endif
+
+#pragma omp critical
+			curProg+=PROGRESS_REDUCE;
+			
+			progReduce=PROGRESS_REDUCE;
+		}	
+	}
+	
+	if(wantProg)
+	{
+		while(reportedProg++ < 100)
+			msgs << ".";
+		msgs << "|";	
+	}
+}
+
+
+/*
+size_t Mesh::getNearestTri(const Point3D &p,float &signedDistance) const
+{
+	//Loop over all the triangles in order to locate the nearest
+	size_t nearTri  = (size_t)-1;
+	float distance=std::numeric_limits<float>::max();
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		Point3D normal;
+		getTriNormal(ui,normal);
+		
+		float newDist;
+		newDist=signedDistanceToFacet(nodes[triangles[ui].p[0]],
+					nodes[triangles[ui].p[1]],
+					nodes[triangles[ui].p[2]],normal,p);
+		if(fabs(newDist) < distance)
+		{
+			nearTri=ui;
+			distance= fabs(newDist);
+			signedDistance=newDist;
+		}
+	}
+
+	return nearTri;
+}
+
+
+bool Mesh::isOrientedCoherently() const
+{
+	ASSERT(isSane());
+	//Need to check circulation of triangles, (edge A->B on one
+	// triangle matches that of the other).
+	//for all triangles in the mesh
+	
+	vector<bool> seenTri;
+	seenTri.resize(triangles.size(),false);
+
+	deque<size_t> triQueue;
+
+	std::vector<std::list<size_t> > adjacency;
+	getTriEdgeAdjacencyMap(adjacency);
+
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		//Add unvisited to queue,
+		if(!seenTri[ui])
+		{
+			triQueue.push_back(ui);
+			seenTri[ui] = true;
+		}
+
+		//Visit all triangles contiguous to whatever is in the queue
+		while(triQueue.size())
+		{
+			size_t tri;
+			tri = triQueue.front();
+			triQueue.pop_front();
+				
+			list<size_t> *curAdjT;
+			curAdjT= &(adjacency[tri]);
+
+			for(list<size_t>::const_iterator it=curAdjT->begin(); it !=curAdjT->end();++it)
+			{
+				if(*it ==  tri || seenTri[*it]) 
+					continue;
+
+				if(triangles[tri].edgesMismatch(triangles[*it]))
+					return false;
+
+				seenTri[*it]= true;
+				triQueue.push_back(*it);
+
+			}
+
+		}
+	}
+
+
+	return true;
+}
+
+
+
+void Mesh::orientTriEdgesCoherently() 
+{
+	//Need to check circulation of triangles, (edge A->B on one triangle matches that of the other).
+	//for all triangles in the mesh
+	vector<bool> seenTri;
+	seenTri.resize(triangles.size(),false);
+
+	deque<size_t> triQueue;
+
+	std::vector<std::list<size_t> > adjacency;
+	getTriEdgeAdjacencyMap(adjacency);
+
+
+
+
+	for(size_t ui=0;ui<triangles.size();ui++)
+	{
+		//Add unvisited to queue,
+		if(!seenTri[ui])
+		{
+			triQueue.push_back(ui);
+			seenTri[ui] = true;
+		}
+
+		//Visit all triangles contiguous to whatever is in the queue
+		while(triQueue.size())
+		{
+			size_t tri;
+			tri = triQueue.front();
+			triQueue.pop_front();
+				
+			list<size_t> *curAdjT;
+			curAdjT= &(adjacency[tri]);
+
+			for(list<size_t>::const_iterator it=curAdjT->begin(); it !=curAdjT->end();++it)
+			{
+				if(*it ==  tri || seenTri[*it]) 
+					continue;
+
+				if(triangles[tri].edgesMismatch(triangles[*it]))
+				{
+					//Reverse the vertex order on the triangle
+					std::swap(triangles[*it].p[0],
+							triangles[*it].p[1]);
+
+				}
+				seenTri[*it]= true;
+				triQueue.push_back(*it);
+
+			}
+
+		}
+	}
+
+	//ASSERT(isOrientedCoherently());
+}
+*/
+
+bool TRIANGLE::isSane(size_t nMax) const
+{
+
+	for(size_t ui=0;ui<3;ui++)
+	{
+		if ( p[ui] == p[(ui+1)%3])
+			return false;
+
+		//if nMax supplied, use it
+		if(nMax != (size_t) -1 &&  p[ui] > nMax )
+			return false;
+	}
+
+	return true;
+}
+
+/*
+bool TRIANGLE::edgesMismatch(const TRIANGLE &other) const
+{
+	ASSERT(isSane());
+	ASSERT(other.isSane());
+
+
+	vector<size_t> commonV;
+	for(size_t ui=0;ui<3;ui++)
+	{
+		for(size_t uj=0;uj<3;uj++)
+		{
+			if ( other.p[uj] == p[ui])
+			{
+				commonV.push_back(p[ui]);
+				break;
+			}
+		}
+	}
+
+	ASSERT(commonV.size() <=3);
+
+	//If either zero or one common vertices, then there is no edge
+	// mismatch
+	if(commonV.size() < 2)
+		return false;
+	else 
+	{
+		unsigned int pA[3],pB[3];
+
+		for(size_t ui=0;ui<3;ui++)
+		{
+			//If common vertex cannot be found, replace with "-1"
+			if(std::find(commonV.begin(),commonV.end(),p[ui]) == commonV.end())
+				pA[ui]=-1;
+			else
+				pA[ui]=p[ui];
+			
+			//If common vertex cannot be found, replace with "-1"
+			if(std::find(commonV.begin(),commonV.end(),other.p[ui]) == commonV.end())
+				pB[ui]=-1;
+			else
+				pB[ui]=other.p[ui];
+		}
+
+		if (commonV.size() == 3)
+		{
+
+			//If the triangles have all 3 vertices in common, they will match IFF they
+			// have a rotationally invariant sequence that matches (3 matching edges). As permutations
+			// other than vertex sequence rotation will flip the triangle normal
+			// egg : 1-2-3 matches 2-3-1,  but not  1-3-2
+			return !rotateMatch(pA,pB,3);
+		}
+		else
+		{
+			//If the triangles have 2 vs in common, then they have one edge in common.
+			// this will match IFF the circulation (edge ordering) of the two triangles is opposite
+			return !antiRotateMatch(pA,pB,3);
+		}
+	}
+
+	ASSERT(false);	
+}
+*/
+
+#ifdef DEBUG
+
+bool coherencyTests()
+{
+	//Create a perfects valid mesh of tris
+	//---
+	Mesh m;
+
+	m.nodes.push_back(Point3D(0,0,0));
+	m.nodes.push_back(Point3D(0,0,1));
+	m.nodes.push_back(Point3D(1,0,0));
+	m.nodes.push_back(Point3D(0,1,0));
+
+	TRIANGLE t;
+	t.p[0] = 0;
+	t.p[1] = 1;
+	t.p[2] = 2;
+	m.triangles.push_back(t);
+
+	t.p[0]=1;
+	t.p[1]=0;
+	t.p[2]=3;
+	m.triangles.push_back(t);
+	
+	t.p[0]=3;
+	t.p[1]=2;
+	t.p[2]=1;
+	m.triangles.push_back(t);
+	//---
+
+	//TEST(m.isOrientedCoherently(),"mesh coherency check");
+
+
+	//Flip the shared edge representation for a tri, so we get an inverted
+	//  normal on one tri
+	m.triangles[1].p[0]=0;
+	m.triangles[1].p[1]=1;
+
+	//TEST(!m.isOrientedCoherently(),"check incoherent mesh detection");
+
+	//Attempt to reorient the mesh coherently
+	//m.orientTriEdgesCoherently();
+
+	//check it worked
+	//TEST(m.isOrientedCoherently(), "Mesh auto-reorient")
+
+	return true;
+}
+
+bool nearestTriTest()
+{
+	Mesh m;
+
+	//Make an L shaped edge
+	m.nodes.push_back(Point3D(1,0,0));
+	m.nodes.push_back(Point3D(-1,0,0));
+	m.nodes.push_back(Point3D(0,0,1));
+	m.nodes.push_back(Point3D(0,1,0));
+
+
+	//0,3,1
+	TRIANGLE t;
+	t.p[0]=0;
+	t.p[1]=3;
+	t.p[2]=1;
+	m.triangles.push_back(t);
+
+	//0,1,2
+	t.p[0]=0;
+	t.p[1]=1;
+	t.p[2]=2;
+	m.triangles.push_back(t);
+	
+	//Test that the exterior test works, 
+	// using point (0.5,0,0.4)
+	//float dist;
+	//TEST(m.getNearestTri(Point3D(0,0.5,0.4),dist) == 0,"Nearest tri");
+	//TEST(m.getNearestTri(Point3D(0,0.5,0.6),dist) == 1,"Nearest tri");
+
+	return true;
+
+}
+
+bool meshTests()
+{
+	TEST(coherencyTests(),"Mesh coherency checks");
+	TEST(nearestTriTest(),"Mesh nearest tri");
+	return true;
+}
+
+#endif
diff --git a/src/common/mesh.h b/src/common/mesh.h
new file mode 100644
index 0000000..60e58bc
--- /dev/null
+++ b/src/common/mesh.h
@@ -0,0 +1,255 @@
+/* 
+ * Copyright (C) 2015  Daniel Haley
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef MESH_H
+#define MESH_H
+
+#include "common/basics.h"
+#include "common/mathfuncs.h"
+
+#include <vector>
+#include <fstream>
+#include <list>
+
+//GMSH load constants
+//-----------
+//Constants are defined in the GMSH documentation
+//under section MSH-ASCII-file-format
+//http://geuz.org/gmsh/doc/texinfo/gmsh.html#MSH-ASCII-file-format
+const unsigned int ELEM_SINGLE_NODE_POINT=15;
+const unsigned int ELEM_TWO_NODE_LINE=1;
+const unsigned int ELEM_THREE_NODE_TRIANGLE=2;
+const unsigned int ELEM_FOUR_NODE_TETRAHEDRON=4;
+//-----------
+
+
+extern const char *MESH_LOAD_ERRS[];
+
+enum
+{
+	ELEMENT_TRIANGLE=1,
+	ELEMENT_TETRAHEDRON=2,
+	ELEMENT_LINE=4,
+};
+
+class TETRAHEDRON{ 
+	public:
+	unsigned int p[4];
+	unsigned int physGroup;
+};
+
+class TRIANGLE{ 
+	public:
+	unsigned int p[3];
+	unsigned int physGroup;
+
+	bool isSane(size_t pLimit=(size_t)-1) const;
+	bool edgesMismatch(const TRIANGLE &tOther) const;
+};
+
+class LINE{
+	public:
+	unsigned int p[2];
+	unsigned int physGroup;
+};
+
+class Mesh
+{
+	private:
+
+		//!Returns true if ui and uj are the same
+		bool sameTriangle(unsigned int ui, unsigned int uj) const;
+		static bool sameTriangle(const TRIANGLE &t1, const TRIANGLE &t2);
+
+		//!Returns true if the group of triangles are coincident to the nominated triangle
+		bool trianglesCoincident(unsigned int ui, const std::vector<size_t> &v) const;
+
+		//!Return true if the specified tet is degenerate (coplanar)
+		bool tetrahedronDegenerate(unsigned int tet) const;
+
+		//!Return true if  a specified point is in the tet.
+		bool pointInTetrahedron(unsigned int tet, const Point3D &p) const;
+		
+		//!Get a list of all disconnected tetrahedra	
+		void getDisconnectedTets(std::vector<size_t> &tetIdx) const;	
+
+		//!Build an adjacency listing for the triangles in the mesh
+		//using vertex sharing rules
+		//i.e. this will retrieve all vector of all triangles who share edge incidence 
+		// for a specified vertex, eg: <|>  vertex only 
+		//incident triangles eg : |>*<| , will be excluded.
+		//The returned map can be used to perform triangle -> surrounding triangle lookups for shared edge
+		// incidence
+		void getTriEdgeAdjacencyMap(std::vector<std::list<size_t> > &map) const;
+
+		//!Return the normal angle (in rad) between two triangles
+		float normalAngle(size_t triOne,size_t triTwo,bool flip=false ) const;
+
+		//!Reverse triangle vertex (triTwo) to reverse shared edge to invert implicit normal
+		void flipTriNormalCoherently(size_t triOne, size_t triTwo); 
+
+		//!Kill specified orphan nodes within this dataset
+		void killOrphanNodes(const std::vector<size_t> &orphans) ;
+	public:
+		//!Point storage for 3D Data (nodes/coords/vertices..)
+		std::vector<Point3D> nodes;
+
+		//!Physical group storage
+		std::vector<std::string> physGroupNames;
+		
+		
+		//==== Element Storage ====
+		//!Storage for node connectivity in tet. form 
+		std::vector<TETRAHEDRON> tetrahedra;
+		//!Storage for node connectivity in triangle form (take in groups of 3)
+		//triangles.size() %3 should always == 0.
+		std::vector<TRIANGLE> triangles;
+		//!Storage for line segments. .size()%2 should always==0
+		std::vector<LINE> lines;
+		//!		
+		std::vector<unsigned long long> points;
+
+		//Returns 0 on OK, nonzero on error.
+		unsigned int loadGmshMesh(const char *meshfile, unsigned int &curLine, bool allowBadMeshes=true);
+		unsigned int saveGmshMesh(const char *meshfile) const;
+
+		//Return sum of all element sizes (total lines, points, triangles, tets, etc)
+		size_t elementCount() const;
+
+		//Set the triangle mesh from the following pt triplet, each vector being the same size.
+		// Function will clear any existing data
+		void setTriangleMesh(const std::vector<float> &ptsA, 
+				const std::vector<float> &ptsB, const std::vector<float> &ptsC);
+
+		//!reassign the physical groups to a single number
+		void reassignGroups(unsigned int i);
+
+		//!Remove exact duplicate triangles
+		void removeDuplicateTris();
+		//Remove triangles that are not fully connected to mesh (ie have all edges shared)
+		void removeStrayTris();
+
+		//Merge vertices that lie  within tolerance distance of one another into a single vertex.
+		// note that this can produced degenerate objects in the mesh, if tolerance is large comapred to the smallest element in the mesh
+		void mergeDuplicateVertices(float tolerance);
+
+		//!Perform various sanity tests on mesh. Should return true if your mesh is sane.
+		//Returning true is not a guarantee of anything however.
+		bool isSane(bool output=false,std::ostream &outStream=std::cerr) const;
+
+		//!Get the Axis aligned bounding box for this mesh
+		void getBounds(BoundCube &b) const;
+
+		//!Count the number of unique nodes shared by triangles
+		unsigned int countTriNodes() const;
+
+		//!Translate mesh around node centroid
+		void translate();
+		//!Translate mesh to specified position 
+		void translate(const Point3f &origin);
+		void translate(const Point3D &origin);
+		
+		//!Scale the mesh around a specified origin
+		void scale(const Point3f &origin, float scalefactor);
+
+		//!Scale the mesh around a specified origin
+		void scale(const Point3D &origin,float scaleFactor);
+
+		//!Scale the mesh around origin
+		void scale(float scaleFactor);
+
+		//!Rotate mesh
+		void rotate(const Point3f &axis, const Point3f &origin, float angle);
+
+		//!Obtain the volume of the triangulated space
+		// triangles must be correctly oriented, and closed
+		float getVolume() const;
+
+		//!place triangles over exposed tetrahedral faces
+		void resurface(unsigned int newPhys);
+
+		//!Clear the mesh
+		void clear();
+		
+		//!Check to see if the mesh is a single unit of tetrahedra
+		bool isTetFullyConnected(unsigned int &badTet) const;
+
+		//!Refine the selected tetrahedra using a midpoint division method
+		void refineTetrahedra(std::vector<size_t> &refineTets); 
+
+
+		//!Get the line and triangle segments that are connected to a particular tetrahedron
+		void getAttachedComponents(size_t tet, 
+			std::vector<size_t> &tris, std::vector<size_t> &l) const;
+
+		//!Return all the nodes that are contained within specified bounding box
+		void getContainedNodes(const BoundCube &b,
+					std::vector<size_t> &nodes) const;
+
+		//!Return all primitives that are WHOLLY contained withing bounding box
+		void getIntersectingPrimitives(	std::vector<size_t> &searchNodes,
+					std::vector<size_t> &lines,
+					std::vector<size_t> &triangles,
+					std::vector<size_t> &tetrahedra	) const;
+
+		unsigned int divideMeshSurface(float divisionAngle, unsigned int newPhysGroupStart,
+			const std::vector<size_t> &physGroupsToSplit) ;
+
+		void getCurPhysGroups(std::vector<std::pair<unsigned int,size_t> > &curPhys) const;
+
+		void erasePhysGroup(unsigned int group, unsigned int typeMask);
+
+	
+		//obtain the number of duplicate vertices in the mesh
+		unsigned int numDupVertices(float tolerance) const;
+
+	
+		//Obtain the number of duplicate triangles in the mesh
+		unsigned int numDupTris() const;
+
+		//Print some statistics about the mesh data
+		void print(std::ostream &o) const;
+
+		//Flip normals in the mesh in order to have coherently oriented normals for all contigous objects.
+		//No guarantee is given as to which surface will be inside and which will be outside.
+		//FIXME: BROKEN
+		void orientTriEdgesCoherently() ;
+		
+		//Returns true if the mesh is coherently oriented
+		bool isOrientedCoherently()  const;
+
+		//!Kill specified orphan nodes within this dataset
+		void killOrphanNodes();
+
+		//!Perform vertex weighted relaxation
+		void relax(size_t iterations, float relaxFactor);
+
+		//Find the poitns that lie inside a this mesh
+		void pointsInside(const std::vector<Point3D> &p,
+			std::vector<bool> &meshResults, std::ostream &msgs, bool wantProg) const ;
+
+		//Find the nearest triangle to a particular point
+		size_t getNearestTri(const Point3D &p,float &distance)  const;
+
+		void getTriNormal(size_t tri, Point3D &normal) const;
+};
+
+#ifdef DEBUG
+//Run unit tests for mesh
+bool meshTests();
+#endif
+
+#endif
diff --git a/src/common/stringFuncs.cpp b/src/common/stringFuncs.cpp
index cd0ffdc..6d97f31 100644
--- a/src/common/stringFuncs.cpp
+++ b/src/common/stringFuncs.cpp
@@ -1,6 +1,6 @@
 /*
  *	stringFuncs.cpp - string manipulation routines
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/common/stringFuncs.h b/src/common/stringFuncs.h
index 878a5df..fe5d2bb 100644
--- a/src/common/stringFuncs.h
+++ b/src/common/stringFuncs.h
@@ -1,6 +1,6 @@
 /*
  *	common/stringFuncs.h - String manipulation header 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/common/translation.h b/src/common/translation.h
index 8593603..335ea3e 100644
--- a/src/common/translation.h
+++ b/src/common/translation.h
@@ -1,6 +1,6 @@
 /*
  * common/translation.h  - Program gettext translation macros
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/common/voxels.cpp b/src/common/voxels.cpp
index c21a671..2612e20 100644
--- a/src/common/voxels.cpp
+++ b/src/common/voxels.cpp
@@ -1,6 +1,6 @@
 /*
  * voxels.cpp - Voxelised data manipulation class 
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,113 +21,8 @@ using std::numeric_limits;
 const float FLOAT_SMALL=
 	sqrt(numeric_limits<float>::epsilon());
 
-#ifdef DEBUG
-
-bool testConvolve()
-{
-	{
-	Voxels<unsigned int> data,kernel,result;
-	data.setCallbackMethod(dummyCallback);
-	kernel.setCallbackMethod(dummyCallback);
-
-	const size_t NUM_SIZES=4;
-	const size_t TEST_SIZES[]= { 1, 3, 4, 7};
-
-	for(size_t ui=0;ui<BOUND_ENUM_END; ui++)
-	{
-		//Convolve several kernel sizes
-		//---
-		for(unsigned int ui=0;ui<NUM_SIZES;ui++)
-		{
-
-			size_t curSize;
-			curSize=TEST_SIZES[ui];
-
-			data.resize(curSize,curSize,curSize);
-			kernel.resize(curSize,curSize,curSize);
-
-			//Test with a whole bunch of different fill values
-			for(size_t fillVal=0;fillVal<5;fillVal++)
-			{
-				data.fill(fillVal);
-				kernel.fill(fillVal);
-
-				data.convolve(kernel,result,BOUND_CLIP);
 
-				size_t nX,nY,nZ;
-				result.getSize(nX,nY,nZ);
-
-				TEST(nX == 1,"convolve dimensions");
-				TEST(nY == 1,"convolve dimensions");
-				TEST(nZ == 1,"convolve dimensions");
-
-				TEST(result.max() == curSize*curSize*curSize*(fillVal*fillVal),
-						"Convolve maxima");
-			}
-		}
-		//--
-	
-		kernel.resize(1,1,1);
-		//Convolve several kernel sizes
-		for(unsigned int ui=0;ui<NUM_SIZES;ui++)
-		{
-
-			size_t curSize;
-			curSize=TEST_SIZES[ui];
-
-			data.resize(curSize,curSize,curSize);
-			data.fill(1);
-			kernel.fill(1);
-
-			data.convolve(kernel,result,BOUND_CLIP);
-			TEST(result == data, "convolve identity");
-		}
-		kernel.resize(3,3,3);
-	}
-	}
-
-	//Test integral stuff
-	{
-		Voxels<float> data;
-		data.resize(3,3,3);
-		data.fill(1);
-		TEST(fabs(data.trapezIntegral() - 1.0f )< FLOAT_SMALL,"Trapezoid test");
-
-		data.resize(5,5,5);
-		data.fill(1);
-		data.setBounds(Point3D(0,0,0),Point3D(1,1,1));
-		TEST(fabs(data.trapezIntegral() - 1.0f) < FLOAT_SMALL,"Trapezoid test");
-		data.setBounds(Point3D(0,0,0),Point3D(5,5,5));
-		TEST(fabs(data.trapezIntegral() - 125.0f) < FLOAT_SMALL,"Trapezoid test");
-	}
-
-	//Test convolution stuff
-	{
-	Voxels<float> data,kernel,result;
-	data.setCallbackMethod(dummyCallback);
-	kernel.setCallbackMethod(dummyCallback);
-	//Check that convolving with an impulse with 
-	//a Gaussian  gives us a Gaussian 
-	//back, roughly speaking
-	kernel.setGaussianKernelCube(1.0f,10.0f,10);
-
-	float trapz = kernel.trapezIntegral();
-	TEST(trapz < 1.5f && trapz > 0.5f, "Trapezoidal kernel integral test");
-	
-
-	data.resize(20,20,20);
-	data.fill(0.0f);
-	data.setData(10,10,10,1.0f);
-	data.convolve(kernel,result,BOUND_CLIP);
-
-	TEST(result.max() >  0 && result.max() < 1.0f,"result should be nonzero, and less than the original input (convolve only squeezes maxima/minima)");
-	//Gaussian @ x=0, stdev 1
-	TEST(fabs(result.max() - kernel.max())  < FLOAT_SMALL
-		,"Gaussian kernel test- maxima of convolved and kernel should be the same");
-	}
-
-	return true;
-}
+#ifdef DEBUG
 
 bool simpleMath()
 {
@@ -146,7 +41,6 @@ bool simpleMath()
 bool basicTests()
 {
 	Voxels<float> f;
-	f.setCallbackMethod(dummyCallback);
 	f.resize(3,3,3);
 	
 	size_t xs,ys,zs;
@@ -221,15 +115,53 @@ bool basicTests()
 }
 
 
+/*
+bool edgeCountTests()
+{
+	Voxels<float> v;
+	v.resize(4,4,4);
+
+
+	TEST(v.getEdgeUniqueIndex(0,0,0,3) == v.getEdgeUniqueIndex(0,1,1,0),"Edge coincidence");
+	TEST(v.getEdgeUniqueIndex(0,0,0,6) == v.getEdgeUniqueIndex(1,0,0,4),"Edge coincidence");
+	TEST(v.getEdgeUniqueIndex(0,0,0,2) == v.getEdgeUniqueIndex(0,0,1,0),"Edge coincidence");
 
+	//Check for edge -> index -> edge round tripping 
+	//for single cell
+	size_t x,y,z;
+	x=1;
+	y=2;
+	z=3;
+	for(size_t ui=0;ui<12;ui++)
+	{
+		size_t idx;
+		idx= v.getCellUniqueEdgeIndex(x,y,z,ui);
+		
+		size_t axis;
+		size_t xN,yN,zN;
+		v.getEdgeCell(idx,xN,yN,zN,axis);
+		
+		//if we ask for the cell, we should also 
+		//get the index
+		ASSERT(x == xN && y==yN && z==zN);	
+
+		//TODO: Check that the axis of the edge was preserved (not the edge itself)
+		ASSERT( axis == ui/4);
+	}
+	
+	return true;	
+}
+*/
 
 
 bool runVoxelTests()
 {
-//	TEST(testCubeIntercepts(),"cube intercept test");
+	bool wantAbort=false;
+	voxelsWantAbort = &wantAbort;
+
 	TEST(basicTests(),"basic voxel tests");
-	TEST(testConvolve()," voxel convolve");
 	TEST(simpleMath(), "voxel simple maths");	
+//	TEST(edgeCountTests(), "voxel edge tests");	
 	return true;	
 }
 
diff --git a/src/common/voxels.h b/src/common/voxels.h
index 0e63148..b5c2d01 100644
--- a/src/common/voxels.h
+++ b/src/common/voxels.h
@@ -1,6 +1,6 @@
  /*
  * common/voxels.h - Voxelised data manipulation class
- * Copyright (C) 2013  D. Haley
+ * Copyright (C) 2015  D. Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,6 +25,18 @@ const unsigned int MAX_CALLBACK=500;
 #include "common/basics.h"
 
 #include <stack>
+#include <numeric>
+
+// Not sure who is defining this, but it is causing problems - mathgl?
+#undef I 
+#undef Complex
+#include <typeinfo>
+#include <vigra/multi_array.hxx>
+#include <vigra/multi_convolution.hxx>
+
+#include <typeinfo> //Bug in accumulator.hxx. Needs typeinfo
+
+#include <vigra/accumulator.hxx>
 
 using namespace std;
 
@@ -90,20 +102,10 @@ enum {
 	VOXEL_BOUNDS_INVALID_ERR
 };
 
-#ifdef _OPENMP
-//Auto-detect the openMP iterator type.
-#if ( __GNUC__ > 4 && __GNUC_MINOR__ > 3 )
-	#define MP_INT_TYPE size_t
-#else
-	#define MP_INT_TYPE long long 
-#endif
-#else
-	#define MP_INT_TYPE size_t
-#endif
-
 #ifdef DEBUG
 	bool runVoxelTests();
 #endif
+static const bool *voxelsWantAbort;
 //!Template class that stores 3D voxel data
 /*! To instantiate this class, objects must have
  * basic mathematical operators, such as * + - and =
@@ -115,19 +117,19 @@ template<class T> class Voxels
 		size_t binCount[3];
 
 		//!Voxel array 
-		std::vector<T> voxels;
+		vigra::MultiArray<3,T> voxels;
 		
 		//!Scaling value for furthest bound of the dataset. 
 		//Dataset is assumed to sit in a rectilinear volume from minBound
 		//to maxBound
 		Point3D minBound, maxBound;
 	
-		bool (*callback)(bool);
 			
 
 		void localPaddedConvolve(long long ui,long long uj, long long uk, 
 			const Voxels<T> &kernel,Voxels<T> &result, unsigned int mode) const;
 	public:
+
 		//!Constructor.
 		Voxels();
 		//!Destructor
@@ -161,10 +163,6 @@ template<class T> class Voxels
 		inline T getData(size_t x, size_t y, size_t z) const;
 		//!Retrieve value of the nth voxel
 		inline T getData(size_t i) const { return voxels[i];}
-		//Return a padded version of the data. Valid pads are BOUND_*
-		T getPaddedData(long long x, long long y, long long z, 
-						unsigned int padMode) const;
-
 
 		void setEntry(size_t n, const T &val) { voxels[n] = val;};
 		//!Retrieve a reference to the data ata  given position
@@ -174,14 +172,18 @@ template<class T> class Voxels
 		//!Set the value of nth point in the dataset
 		void setData(size_t n, const T &val);
 
+		//Perform in-place gaussian smoothing
+		void isotropicGaussianSmooth(float stdev,float windowRatio);
+
+		//Perform in-place laplacian smoothing
+		void laplaceOfGaussian(float stdev, float windowRatio);
 
 		//get an interpolated slice from a section of the data
-		void getSlice(size_t normal, float offset, T *p, 
-			size_t interpMode=SLICE_INTERP_NONE, size_t boundMode=BOUND_MIRROR) const;
+		void getInterpSlice(size_t normal, float offset, T *p, 
+			size_t interpMode=SLICE_INTERP_NONE) const;
 
-		//Get a specific slice, from an integral offset in the data
-		void getSlice(size_t normal, size_t offset, 
-					T *p,size_t boundMode) const;
+		//Get a specific slice, from an integral offset in the data, no interp
+		void getSlice(size_t normal, size_t offset, T *p) const;
 		//!Get the size of the data field
 		void getSize(size_t &x, size_t &y, size_t &z) const;
 		size_t getSize() const {return voxels.size();};
@@ -205,25 +207,37 @@ template<class T> class Voxels
 
 
 
-		//!Get a unique integer that corresponds to the edge index for the voxel; where edges are shared between voxels
+		//!DEPRECATED FUNCTION : Get a unique integer that corresponds to the edge index for the voxel; where edges are shared between voxels
 		/*! Each voxel has 12 edges. These are shared (except
 		 * voxels that on zero or positive boundary). Return a
-	 	 * unique index that corresponds to a specified edge (0->11)
+	 	 * unique index that corresponds to a specified edge (0->11).
+		 * Index *CANNOT* be inverted to yield cell
 		 */
-		size_t getEdgeIndex(size_t x,size_t y, size_t z, unsigned int edge) const;
+		size_t deprecatedGetEdgeUniqueIndex(size_t x,
+			size_t y, size_t z, unsigned int edge) const;
 
+		//!Get a unique integer that corresponds to an edge index for the voxel; where edges are shared between voxels
+		/*! Each voxel has 12 edges. These are shared (except
+		 * voxels that on zero or positive boundary). Return a
+	 	 * NON-unique index that corresponds to a specified edge (0->11)
+		 * Index can be inverted to yield cell
+		 */
+		size_t getCellUniqueEdgeIndex(size_t x,
+			size_t y, size_t z, unsigned int edge) const;
 	
-		//!Convert the edge index (as generated by getEdgeIndex) into a cenre position	
+		//!Convert the edge index (as generated by getEdgeUniqueIndex) into a cenre position	
 		// returns the axis value so you know edge vector too.
 		// NOte that the value to pass as the edge index is (getEdgeIndex>>2)<<2 to
 		// make the ownership of the voxel correct 
 		void getEdgeEnds(size_t edgeIndex,Point3D &a, Point3D &b) const;
 
 
+		//!Convert edge index (only as generted by getCellUniqueEdgeIndex) into a cell & axis value
+		void getEdgeCell(size_t edgeUniqId, size_t &x,size_t &y, size_t &z, size_t &axis ) const;
 
 		//TODO: there is duplicate code between this and getEdgeEnds. Refactor.
 		//!Return the values that are associated with the edge ends, as returned by getEdgeEnds
-		void getEdgeEndApproxVals(size_t edgeUniqId, float &a, float &b ) const;
+		void getEdgeEndApproxVals(size_t edgeUniqId, T  &a, T  &b ) const;
 
 
 		//!Rebin the data by a given rate
@@ -291,15 +305,7 @@ template<class T> class Voxels
 		size_t separableConvolve(const Voxels<T> &templateVec, Voxels<T> &result,
 							 size_t boundMode=BOUND_CLIP); 
 		
-		//!Set this object to a normalised gaussian kernel, centered around the middle of the dataset
-		void setGaussianKernelCube(float stdDev, float bound, size_t sideLen);
 		
-		//!Second derivative by difference approximation 
-		/*! Returns the first order central difference approximation to the
-		 * second derivative. Bound mode can (when implemented) bye used to compute
-		 * appropriate differences.
-		 */
-		void secondDifference(Voxels<T> &result, size_t boundMode=BOUND_CLIP) const;
 
 
 		//!Find the positions of the voxels that are above or below a given value
@@ -311,15 +317,6 @@ template<class T> class Voxels
 
 
 
-		//!Construct a spherical kernel
-		/* The spherical result is located at the centre of the voxel set
-		 * sideLen -The side length is the diameter of the sphere
-		 * bound -the bounding value that the voxels contain themselves within
-		 * val - the value that the voxels take on for a full sphere.
-		 * antialiasingLevel - the number of times to subdivide the voxels into 8 parts 
-		 *  and test these voxels for internal/external. The resultant fraction of  inside/outside voxels is summed.
-		 */
-		void makeSphericalKernel(size_t sideLen, float bound, const T &val, unsigned int antialiasLevel=0);
 
 		//!Return the sizeof value for the T type
 		/*! Maybe there is a better way to do this, I don't know
@@ -338,7 +335,7 @@ template<class T> class Voxels
 		//!Empty the data
 		/*Remove the data from the voxel set
 		 */
-		void clear() { voxels.clear();};
+		void clear() { voxels.reshape(vigra::Shape3(0,0,0));};
 
 		//!Find minimum in dataset
 		T min() const;
@@ -349,20 +346,6 @@ template<class T> class Voxels
 		//!Find both min and max in dataset in the same loop
 		void minMax(T &min, T &max) const;	
 
-		//!Given some coordinates and radii, generate a voxel dataset of solid spheres superimposed
-		/*! The radius specified is in the bounding units
-		 *  clear value optionally wipes the dataset before continuing 
-		 */
-		void fillSpheresByPosition( const std::vector<Point3D> &spherePos, float rad, const T &value, bool doErase=true);
-
-
-		//!Generate a histogram of data values. / operator must be defined for target type
-		/*! Data bins are evenly spaced between min and max. Formula for binning is (val-min())/(max()-min())*histBinCount
-		 */
-		int histogram(std::vector<size_t> &v, size_t histBinCount) const;
-
-		//!Find the largest or smallest n objects
-		void findNExtrema(std::vector<size_t> &x, std::vector<size_t> &y, std::vector<size_t> &z, size_t n, bool largest=true) const;
 
 		//!Generate a dataset that consists of the counts of points in a given voxel
 		/*! Ensure that the voxel scaling factors 
@@ -381,13 +364,6 @@ template<class T> class Voxels
 		void calculateDensity();
 
 		float getBinVolume() const;
-		//!increment the position specified
-		inline void increment(size_t x, size_t y, size_t z){
-			//Typecast everything to at least 64 bit uints.
-			voxels[(size_t)z*(size_t)binCount[1]*(size_t)binCount[0]
-				+(size_t)y*(size_t)binCount[0] + (size_t)x]++;}
-	
-		void setCallbackMethod(bool (*cb)(bool)) {callback = cb;}
 
 		//!Element wise division	
 		void operator/=(const Voxels<T> &v);
@@ -396,26 +372,15 @@ template<class T> class Voxels
 		
 		bool operator==(const Voxels<T> &v) const;
 
+		size_t size() const { return voxels.size();}
 };
 
 //!Convert one type of voxel into another by assignment operator
 template<class T, class U>
 void castVoxels(const Voxels<T> &src, Voxels<U> &dest)
 {
-	size_t x,y,z;
-
-	//Resize the dest
-	src.getSize(x,y,z);
-	
-	dest.resize(x,y,z,src.getBounds());
-
-	size_t numEntries=x*y*z;
-#pragma omp parallel for 
-	for(MP_INT_TYPE ui=0; ui <(MP_INT_TYPE)numEntries; ui++)
-	{
-		dest.setEntry(ui,src.getData(ui));
-	}
-
+	//TODO:  Remove me!
+	src=dest;
 }
 
 //!Use one counting type to sum counts in a voxel of given type
@@ -423,17 +388,13 @@ template<class T, class U>
 void sumVoxels(const Voxels<T> &src, U &counter)
 {
 	size_t nx,ny,nz;
-
-	src.getSize(nx,ny,nz);
-	
 	counter=0;
-	for(size_t ui=0; ui<nx; ui++)
+	src.getSize(nx,ny,nz);
+
+	size_t nMax=src.size();
+	for(size_t ui=0; ui<nMax; ui++)
 	{
-		for(size_t uj=0; uj<ny; uj++)	
-		{
-			for(size_t uk=0; uk<nz; uk++)
-				counter+=src.getData(ui,uj,uk);
-		}
+		counter+=src.getData(ui);
 	}
 
 }
@@ -457,11 +418,10 @@ void Voxels<T>::clone(Voxels<T> &newVox) const
 	newVox.binCount[1]=binCount[1];
 	newVox.binCount[2]=binCount[2];
 
-	newVox.voxels.resize(voxels.size());
+	newVox.voxels=voxels;
 	newVox.minBound=minBound;
 	newVox.maxBound=maxBound;
 
-	std::copy(voxels.begin(),voxels.end(),newVox.voxels.begin());
 
 }
 
@@ -473,8 +433,12 @@ void Voxels<T>::setPoint(const Point3D &point,const T &val)
 	for(size_t ui=0;ui<3;ui++)
 		pos[ui] = (size_t)round(point[ui]*(float)binCount[ui]);
 
+#ifdef DEBUG
+	vigra::Shape3 s= voxels.shape();
+	ASSERT(pos[0]<=s[0] && pos[1] <= s[1] && pos[2] < s[2]);
+#endif
 
-	voxels[pos[2]*binCount[1]*binCount[0] + pos[1]*binCount[0] + pos[0]]=val; 
+	voxels[vigra::Shape3(pos[0],pos[1],pos[2])] = val;
 }
 
 template<class T>
@@ -484,7 +448,7 @@ void Voxels<T>::setData(size_t x, size_t y,
 	ASSERT(voxels.size());
 
 	ASSERT( x < binCount[0] && y < binCount[1] && z < binCount[2]);
-	voxels[z*binCount[1]*binCount[0] + y*binCount[0] + x]=val; 
+	voxels[vigra::Shape3(x,y,z)]=val;
 }
 
 template<class T>
@@ -501,16 +465,21 @@ T Voxels<T>::getPointData(const Point3D &point) const
 {
 	ASSERT(voxels.size());
 	size_t pos[3];
+	Point3D offsetFrac;
+	offsetFrac=point-minBound;
 	for(size_t ui=0;ui<3;ui++)
-		pos[ui] = (size_t)round(point[ui]*(float)binCount[ui]);
+	{
+		offsetFrac[ui]/=(maxBound[ui]-minBound[ui]);
+		pos[ui] = (size_t)round(offsetFrac[ui]*(float)binCount[ui]);
+	}	
 
-	return voxels[pos[2]*binCount[1]*binCount[0] + pos[1]*binCount[0] + pos[0]]; 
+	return voxels[vigra::Shape3(pos[0],pos[1],pos[2])];
 }
 
 template<class T>
 Point3D Voxels<T>::getPoint(size_t x, size_t y, size_t z) const
 {
-	ASSERT(x < binCount[0] && y<binCount[1] && z<binCount[2]);
+	//ASSERT(x < binCount[0] && y<binCount[1] && z<binCount[2]);
 
 	return Point3D((float)x/(float)binCount[0]*(maxBound[0]-minBound[0]) + minBound[0],
 			(float)y/(float)binCount[1]*(maxBound[1]-minBound[1]) + minBound[1],
@@ -534,9 +503,8 @@ void Voxels<T>::getSize(size_t &x, size_t &y, size_t &z) const
 	z=binCount[2];
 }
 
-
 template<class T>
-size_t Voxels<T>::getEdgeIndex(size_t x,size_t y, size_t z, unsigned int index) const
+size_t Voxels<T>::deprecatedGetEdgeUniqueIndex(size_t x,size_t y, size_t z, unsigned int index) const
 {
 	//This provides a reversible mapping of x,y,z 
 	//X aligned edges are first
@@ -637,24 +605,167 @@ size_t Voxels<T>::getEdgeIndex(size_t x,size_t y, size_t z, unsigned int index)
 
 }
 
+/*
 template<class T>
-void Voxels<T>::getEdgeEnds(size_t edgeUniqId, Point3D &a, Point3D &b ) const
+size_t Voxels<T>::getEdgeUniqueIndex(size_t x,size_t y, size_t z, unsigned int index) const
 {
-	//Invert the mapping generated by the edgeUniqId function
-	//to retrieve the XYZ and axis values
-	size_t x,y,z;
+	//This provides a reversible mapping of x,y,z 
+	//X aligned edges are first
+	//Y second
+	//Z third
+	
+
+	//Consider each parallel set of edges (eg all the X aligned edges)
+	//to be the dual grid of the actual grid. From this you can visualise the
+	//cell centres moving -1/2 -/12 units in the direction normal to the edge direction
+	//to produce the centres of the edge. An additional vertex needs to be created at
+	//the end of each dimension not equal to the alignement dim.
+
+
+	//  		    ->ASCII ART TIME<-
+	// In each individual cube, the offsets look like this:
+        //		------------7-----------
+        //		\		       |\ .
+        //		|\ 		       | \ .
+        //		| 10		       |  11
+        //		|  \		       |   \ .
+        //		|   \		       |    \ .
+        //		|    \ --------6-------------|
+        //		|     |                |     |
+        //	      2 |                3     |     |
+        //		|     |                |     |
+        //		|     |                |     |
+        //		|     |                |     |
+        //		|     0                |     1
+        //		\-----|----5-----------      |
+        //	 	 \    |                 \    |
+        //	 	  8   |                  9   |
+        //	 	   \  |                   \  |
+        //		    \ |---------4------------
+	//
+	//   	^x
+	//  z|\	|
+	//    \ |
+	//     \-->y
+	//
+
+	switch(index)
+	{
+		//X axis aligned
+		//--
+		case 0:
+			break;	
+		case 1:
+			y++; // one across in Y
+			break;	
+		case 2:
+			z++;//One across in Z
+			break;	
+		case 3:
+			y++;
+			z++;
+			break;	
+		//--
+
+		//Y Axis aligned
+		//--
+		case 4:
+			break;	
+		case 5:
+			z++;
+			break;	
+		case 6:
+			x++;
+			break;	
+		case 7:
+			z++;
+			x++;
+			break;	
+		//--
+		
+		//Z Axis aligned
+		//--
+		case 8:
+			break;	
+		case 9:
+			y++;
+			break;	
+		case 10:
+			x++;
+			break;	
+		case 11:
+			x++;
+			y++;
+			break;	
+		//--
+	}
+
+	unsigned int axis = index/4;
+	size_t result = 3*(z + y*(binCount[2]+1) + x*(binCount[2]+1)*(binCount[1]+1)) + axis;
+	
+	return result;
+
+}
+*/
+template<class T>
+size_t Voxels<T>::getCellUniqueEdgeIndex(size_t x,size_t y, size_t z, unsigned int index) const
+{
+	//This provides a reversible mapping of x,y,z 
+	//X aligned edges are first
+	//Y second
+	//Z third
+	
+
+	//Consider each parallel set of edges (eg all the X aligned edges)
+	//to be the dual grid of the actual grid. From this you can visualise the
+	//cell centres moving -1/2 -/12 units in the direction normal to the edge direction
+	//to produce the centres of the edge. An additional vertex needs to be created at
+	//the end of each dimension not equal to the alignement dim.
+
+
+	//  		    ->ASCII ART TIME<-
+	// In each individual cube, the offsets look like this:
+        //		------------7-----------
+        //		\		       |\ .
+        //		|\ 		       | \ .
+        //		| 10		       |  11
+        //		|  \		       |   \ .
+        //		|   \		       |    \ .
+        //		|    \ --------6-------------|
+        //		|     |                |     |
+        //	      2 |                3     |     |
+        //		|     |                |     |
+        //		|     |                |     |
+        //		|     |                |     |
+        //		|     0                |     1
+        //		\-----|----5-----------      |
+        //	 	 \    |                 \    |
+        //	 	  8   |                  9   |
+        //	 	   \  |                   \  |
+        //		    \ |---------4------------
+	//
+	//   	^x
+	//  z|\	|
+	//    \ |
+	//     \-->y
+	//
 
-	int index; // Per voxel edge number. See ascii art in getEdgeidx
-	index=edgeUniqId %12;
+	size_t cellIdx = 12*(z + y*(binCount[2]+1) + x*(binCount[2]+1)*(binCount[1]+1)) ;
 
-	//Drop the non-owner part of the voxel
-	index/=4;
-	index*=4;
+	cellIdx+=index;
+	return cellIdx;
+
+}
 
-	size_t tmp;
-	tmp=edgeUniqId;
-	tmp/=12; // shift out the index multiplier
+template<class T>
+void Voxels<T>::getEdgeCell(size_t edgeUniqId, size_t &x,size_t &y, size_t &z, size_t &axis ) const
+{
+	//Invert the mapping generated by the edgeUniqId function
+	//to retrieve the XYZ and axis values
+	//--
+	axis=(edgeUniqId%12)/4;
 
+	size_t tmp = edgeUniqId/12;
 	x = tmp/((binCount[2]+1)*(binCount[1]+1));
 	tmp-=x*((binCount[2]+1)*(binCount[1]+1));
 
@@ -662,32 +773,36 @@ void Voxels<T>::getEdgeEnds(size_t edgeUniqId, Point3D &a, Point3D &b ) const
 	tmp-=y*(binCount[2]+1);
 
 	z=tmp;
-
-
-
+	//--
 
 	ASSERT(x< binCount[0]+1 && y<binCount[1]+1 && z<binCount[2]+1);
+}
+template<class T>
+void Voxels<T>::getEdgeEnds(size_t edgeUniqId, Point3D &a, Point3D &b ) const
+{
+	size_t x,y,z;
+	size_t axis;
+	getEdgeCell(edgeUniqId,x,y,z,axis);
 
 	Point3D delta=getPitch();
 	Point3D cellCentre=getPoint(x,y,z);
-	
 
 	//Generate ends of edge, as seen in ascii diagram in uniqueID
-	switch(index)
+	switch(axis)
 	{
 		case 0:
-			//|| x, lo y, lo z.	
+			//|| x
 			a=cellCentre;
 			b=cellCentre + Point3D(delta[0],0,0);
 			break;
 
-		case 4:
-			//|| y, low x, low z.	
+		case 1:
+			//|| y
 			a=cellCentre;
 			b=cellCentre + Point3D(0,delta[1],0);
 			break;
-		case 8:
-			//|| z, lo x, lo y.	
+		case 2:
+			//|| z
 			a=cellCentre; 
 			b=cellCentre + Point3D(0,0,delta[2]);
 			break;
@@ -699,243 +814,75 @@ void Voxels<T>::getEdgeEnds(size_t edgeUniqId, Point3D &a, Point3D &b ) const
 #ifdef DEBUG
 	BoundCube bc;
 	bc.setBounds(getMinBounds(), getMaxBounds());
-	bc.expand(sqrt(std::numeric_limits<float>::epsilon()));
+	bc.expand(sqrtf(std::numeric_limits<float>::epsilon()));
 	ASSERT(bc.containsPt(a) && bc.containsPt(b));
 #endif
 }
 
 template<class T>
-void Voxels<T>::getEdgeEndApproxVals(size_t edgeUniqId, float &a, float &b ) const
+void Voxels<T>::getEdgeEndApproxVals(size_t edgeUniqId, T  &a, T  &b ) const
 {
-	//Invert the mapping generated by the edgeUniqId function
-	//to retrieve the XYZ and axis values
+
+	//TODO: Speed me up? Could not use
+	// 	other routines to do this access.
+	//	I think some redundant calculations are done
+	Point3D ptA,ptB;
+	getEdgeEnds(edgeUniqId,ptA,ptB);
 	size_t x,y,z;
+	getIndex(x,y,z,ptA);	
+	a = getPointData(ptA);
+	b=getPointData(ptB);
+}
 
-	int index; // Per voxel edge number. See ascii art in getEdgeidx
-	index=edgeUniqId %12;
+template<class T>
+void Voxels<T>::getAxisBounds(size_t axis, float &minV, float &maxV ) const
+{
+	maxV=maxBound[axis];
+	minV=minBound[axis];
+}
 
-	size_t tmp;
-	tmp=edgeUniqId;
-	tmp/=12; // shift out the index multiplier
+template<class T>
+size_t Voxels<T>::resize(size_t x, size_t y, size_t z, const Point3D &newMinBound, const Point3D &newMaxBound) 
+{
+	binCount[0] = x;
+	binCount[1] = y;
+	binCount[2] = z;
 
-	x = tmp/((binCount[2]+1)*(binCount[1]+1));
-	tmp-=x*((binCount[2]+1)*(binCount[1]+1));
 
-	y=tmp/(binCount[2]+1);
-	tmp-=y*(binCount[2]+1);
+	minBound=newMinBound;
+	maxBound=newMaxBound;
 
-	z=tmp;
+	try
+	{
+	voxels.reshape(vigra::Shape3(x,y,z));
+	}
+	catch(...)
+	{
+		return 1;
+	}
 
+	return 0;
+}
 
-	ASSERT(x< binCount[0]+1 && y<binCount[1]+1 && z<binCount[2]+1);
+template<class T>
+size_t Voxels<T>::resize(const Voxels<T> &oth)
+{
+	return resize(oth.binCount[0],oth.binCount[1],oth.binCount[2],oth.minBound,oth.maxBound);
+}
 
+template<class T>
+size_t Voxels<T>::resizeKeepData(size_t newX, size_t newY, size_t newZ, 
+			unsigned int direction, const Point3D &newMinBound, const Point3D &newMaxBound, const T &fill,bool doFill)
+{
 
-	//undo the index shuffle from getEdgeIndex
-	switch(index)
-	{
-		//X axis aligned
-		//--
-		case 0:
-			break;	
-		case 1:
-			y--; // one across in Y
-			break;	
-		case 2:
-			z--;//One across in Z
-			break;	
-		case 3:
-			y--;
-			z--;
-			break;	
-		//--
-
-		//Y Axis aligned
-		//--
-		case 4:
-			break;	
-		case 5:
-			z--;
-			break;	
-		case 6:
-			x--;
-			break;	
-		case 7:
-			z--;
-			x--;
-			break;	
-		//--
-		
-		//Z Axis aligned
-		//--
-		case 8:
-			break;	
-		case 9:
-			y--;
-			break;	
-		case 10:
-			x--;
-			break;	
-		case 11:
-			x--;
-			y--;
-			break;	
-		//--
-	}
-	//The values used here are for the "dual" grid 
-	//of voxels centres. So there are at most 2
-	//voxels sharing an edge	
-	switch(index)
-	{
-		case 0:
-		{
-			//Up we go
-			a= getData(x,y,z);
-			b= getData(x+1,y,z);
-			break;
-		}
-		case 1:
-		{
-			//
-			a= getData(x,y+1,z);
-			b= getData(x+1,y+1,z);
-			
-			break;
-		}
-		case 2:
-		{
-			a= getData(x,y,z+1);
-			b= getData(x+1,y,z+1);
-			
-			break;
-		}
-		case 3:
-		{
-			//
-			a= getData(x,y+1,z+1);
-			b= getData(x+1,y+1,z+1);
-			
-			break;
-		}
-		case 4:
-		{
-			a= getData(x,y,z);
-			b= getData(x,y+1,z);
-			//
-			
-			break;
-		}
-		case 5:
-		{
-			a= getData(x,y,z+1);
-			b= getData(x,y+1,z+1);
-			
-			break;
-		}
-		case 6:
-		{
-			a= getData(x+1,y,z);
-			b= getData(x+1,y+1,z);
-			
-			break;
-		}
-		case 7:
-		{
-			a= getData(x+1,y,z+1);
-			b= getData(x+1,y+1,z+1);
-			
-			break;
-		}
-		case 8:
-		{
-			//
-			a= getData(x,y,z);
-			b= getData(x,y,z+1);
-			
-			break;
-		}
-		case 9:
-		{
-			//
-			a= getData(x,y+1,z);
-			b= getData(x,y+1,z+1);
-			
-			break;
-		}
-		case 10:
-		{
-			a= getData(x+1,y,z);
-			b= getData(x+1,y,z+1);
-			//
-			
-			break;
-		}
-		case 11:
-		{
-			a= getData(x+1,y+1,z);
-			b= getData(x+1,y+1,z+1);
-			
-			break;
-		}
-		default:
-			ASSERT(false);
-			
-	}	
-
-}
-
-template<class T>
-void Voxels<T>::getAxisBounds(size_t axis, float &minV, float &maxV ) const
-{
-	maxV=maxBound[axis];
-	minV=minBound[axis];
-}
-
-template<class T>
-size_t Voxels<T>::resize(size_t x, size_t y, size_t z, const Point3D &newMinBound, const Point3D &newMaxBound) 
-{
-	voxels.clear();
-
-	binCount[0] = x;
-	binCount[1] = y;
-	binCount[2] = z;
-
-
-	minBound=newMinBound;
-	maxBound=newMaxBound;
-
-	size_t binCountMax = binCount[0]*binCount[1]*binCount[2];
-	try
-	{
-	voxels.resize(binCountMax);
-	}
-	catch(...)
-	{
-		return 1;
-	}
-
-	return 0;
-}
-
-template<class T>
-size_t Voxels<T>::resize(const Voxels<T> &oth)
-{
-	return resize(oth.binCount[0],oth.binCount[1],oth.binCount[2],oth.minBound,oth.maxBound);
-}
-
-template<class T>
-size_t Voxels<T>::resizeKeepData(size_t newX, size_t newY, size_t newZ, 
-			unsigned int direction, const Point3D &newMinBound, const Point3D &newMaxBound, const T &fill,bool doFill)
-{
-
-	ASSERT(direction==CLIP_LOWER_SOUTH_WEST);
-	ASSERT(callback);
-
-	Voxels<T> v;
-	
-	if(v.resize(newX,newY,newZ))
-		return 1;
-
-	switch(direction)
+	ASSERT(direction==CLIP_LOWER_SOUTH_WEST);
+
+	Voxels<T> v;
+	
+	if(v.resize(newX,newY,newZ))
+		return 1;
+
+	switch(direction)
 	{
 		case CLIP_LOWER_SOUTH_WEST:
 		{
@@ -978,7 +925,7 @@ size_t Voxels<T>::resizeKeepData(size_t newX, size_t newY, size_t newZ,
 
 #pragma omp critical
 					{
-					if(!(*callback)(false))
+					if(*voxelsWantAbort)
 						spin=true;
 					}
 				}
@@ -1004,7 +951,7 @@ size_t Voxels<T>::resizeKeepData(size_t newX, size_t newY, size_t newZ,
 
 #pragma omp critical
 					{
-					if(!(*callback)(false))
+					if(*voxelsWantAbort)
 						spin=true;
 					}
 				}
@@ -1057,18 +1004,11 @@ size_t Voxels<T>::init(size_t nX, size_t nY,
 	binCount[1]=nY;
 	binCount[2]=nZ;
 
-	typedef size_t ull;
-	ull binCountMax;
-       
-	ASSERT(bound.isValid())
 	bound.getBounds(minBound, maxBound);
-	binCountMax= (ull)binCount[0]*(ull)binCount[1]*(ull)binCount[2];
 
-	voxels.resize(binCountMax);
+	voxels.reshape(vigra::Shape3(nX,nY,nZ));
 
-#pragma omp parallel for
-	for(size_t ui=0; ui<binCountMax; ui++) 
-		voxels[ui]=0;
+	voxels=0;
 
 	return 0;
 }
@@ -1178,7 +1118,7 @@ size_t Voxels<T>::writeFile(const char *filename) const
 		return 1;
 
 	
-	for(size_t ui=0; ui<binCount[0]*binCount[1]*binCount[2]; ui++)
+	for(size_t ui=0; ui<voxels.size(); ui++)
 	{
 		T v;
 		v=voxels[ui];
@@ -1194,22 +1134,17 @@ template<class T>
 T Voxels<T>::getSum(const T &initialValue) const
 {
 	ASSERT(voxels.size());
-	T returnVal=initialValue;
-	T val;
-#pragma omp parallel for private(val) reduction(+:returnVal) 
-	for(MP_INT_TYPE ui=0; ui<binCount[0] ; ui++)
-	{
-		for(size_t uj=0; uj<binCount[1]; uj++ )
-		{
-			for(size_t uk=0; uk<binCount[1]; uk++) 
-			{
-				val=getData(ui,uj,uk);	
-				returnVal+=val; 
-			}
-		}
-	}
+	using namespace vigra::acc;
+	AccumulatorChain<T,Sum> s();
 
-	return returnVal;
+
+	T tmp(initialValue);
+	size_t n=voxels.size();
+#pragma omp parallel for
+	for(size_t ui=0;ui<n;ui++)
+		tmp+=voxels[ui];
+
+	return tmp;
 }
 
 template<class T>
@@ -1256,1181 +1191,135 @@ size_t Voxels<T>::count(const T &minIntensity) const
 }
 
 template<class T>
-void Voxels<T>::swap(Voxels<T> &kernel)
+void Voxels<T>::swap(Voxels<T> &other)
 {
-	std::swap(binCount[0],kernel.binCount[0]);
-	std::swap(binCount[1],kernel.binCount[1]);
-	std::swap(binCount[2],kernel.binCount[2]);
+	std::swap(binCount[0],other.binCount[0]);
+	std::swap(binCount[1],other.binCount[1]);
+	std::swap(binCount[2],other.binCount[2]);
 
-	std::swap(voxels,kernel.voxels);
+	voxels.swap(other.voxels);
 	
-	std::swap(maxBound,kernel.maxBound);
-	std::swap(minBound,kernel.minBound);
+	std::swap(maxBound,other.maxBound);
+	std::swap(minBound,other.minBound);
 }
 
-//Helper function for performing convolution
 template<class T>
-void Voxels<T>::localPaddedConvolve(long long ui,long long uj, long long uk, 
-			const Voxels<T> &kernel,Voxels<T> &result, unsigned int mode) const
+T Voxels<T>::getData(size_t x, size_t y, size_t z) const
 {
-	ASSERT(result.binCount[0] == binCount[0] && result.binCount[1]==binCount[1]
-		&& result.binCount[2] == binCount[2]);
+	ASSERT(x < binCount[0] && y < binCount[1] && z < binCount[2]);
+	return	voxels[vigra::Shape3(x,y,z)]; 
+}
 
-	long long halfX,halfY,halfZ;
-#ifdef DEBUG
-	for(size_t i=0;i<3;i++)
-	{
-		ASSERT(kernel.binCount[i] &1);
-	}
-#endif
-	halfX=kernel.binCount[0]/2; halfY=kernel.binCount[1]/2;halfZ=kernel.binCount[2]/2;
+template<class T>
+void Voxels<T>::thresholdForPosition(std::vector<Point3D> &p, const T &thresh, bool lowerEq) const
+{
+	p.clear();
 
-	T tally(0.0);
-	for(size_t ul=0; ul<kernel.binCount[0]; ul++)
-	{
-	for(size_t um=0; um<kernel.binCount[1]; um++)
-	{
-	for(size_t un=0; un<kernel.binCount[2]; un++)
+	if(lowerEq)
 	{
-		tally+=getPaddedData(ui+ul-halfX,uj+um-halfY,uk+un-halfZ,mode)*
-				kernel.getData(ul,um,un);
-	}
-	}
+#pragma omp parallel for 
+		for(size_t ui=0;ui<binCount[0]; ui++)
+		{
+			for(size_t uj=0;uj<binCount[1]; uj++)
+			{
+				for(size_t uk=0;uk<binCount[2]; uk++)
+				{
+					if( getData(ui,uj,uk) < thresh)
+					{
+#pragma omp critical
+						p.push_back(getPoint(ui,uj,uk));
+					}
+
+				}
+			}
+		}
 	}
+	else
+	{
+#pragma omp parallel for 
+		for(size_t ui=0;ui<binCount[0]; ui++)
+		{
+			for(size_t uj=0;uj<binCount[1]; uj++)
+			{
+				for(size_t uk=0;uk<binCount[2]; uk++)
+				{
+					if( getData(ui,uj,uk) > thresh)
+					{
+#pragma omp critical
+						p.push_back(getPoint(ui,uj,uk));
+					}
 
-	result.setData(ui,uj,uk,tally);	
+				}
+			}
+		}
+	}
 }
 
-template<typename T>
-size_t Voxels<T>::convolve(const Voxels<T> &kernel, Voxels<T> &result,  size_t boundMode)  const
+template<class T>
+void Voxels<T>::binarise(Voxels<T> &result, const T &thresh, 
+				const T &onThresh, const T &offThresh) const
 {
 
-	//Check the kernel can fit within this est
-	size_t x,y,z;
-	kernel.getSize(x,y,z);
-	if(binCount[0] <x || binCount[1]< y || binCount[2] < z)
-		return 1;
-	
-	long long halfX,halfY,halfZ;
-	halfX=kernel.binCount[0]/2; halfY=kernel.binCount[1]/2;halfZ=kernel.binCount[2]/2;
-
-	//Loop through all of the voxel elements, setting the
-	//result voxels to the convolution of the template
-	//with the data
-
-
-	switch(boundMode)
+	result.resize(binCount[0],binCount[1],
+			binCount[2],minBound,maxBound);
+#pragma omp parallel for
+	for(size_t ui=0;ui<(size_t)binCount[0]; ui++)
 	{
-		case BOUND_CLIP:
+		for(size_t uj=0;uj<binCount[1]; uj++)
 		{
-			//Calculate the bounding size of the resultant box
-			Point3D resultMinBound, resultMaxBound;
-			resultMinBound=minBound*Point3D((float)(binCount[0]-x)/(float)binCount[0],
-				(float)(binCount[1]-y)/(float)binCount[1],(float)(binCount[2]-z)/(float)binCount[2] );
-			resultMaxBound=maxBound*Point3D((float)(binCount[0]-x)/(float)binCount[0],
-				(float)(binCount[1]-y)/(float)binCount[1],(float)(binCount[2]-z)/(float)binCount[2] );
-			//Set up the result box
-			size_t sX,sY,sZ;
-			sX = binCount[0] -x+1;
-			sY = binCount[1] -y+1;
-			sZ = binCount[2] -z+1;
-
-			if(result.resize(sX,sY,sZ,resultMinBound, resultMaxBound))
-				return VOXELS_OUT_OF_MEMORY;
-
-			//loop through each element
-			//Forgive the poor indenting, but the looping is very deep
-			//FIXME: there is a shift in the output when doing this?
-			for (size_t ui=0;ui<(size_t)(binCount[0]-x+1); ui++)
-			{
-			for (size_t uj=0;uj<binCount[1]-y+1; uj++)
-			{
-			for (size_t uk=0;uk<binCount[2]-z+1; uk++)
+			for(size_t uk=0;uk<binCount[2]; uk++)
 			{
-				T tally;
-				tally=T(0.0);
-
-				//Convolve this element with the kernel 
-				for(size_t ul=0; ul<x; ul++)
-				{
-				for(size_t um=0; um<y; um++)
-				{
-				for(size_t un=0; un<z; un++)
+				if( getData(ui,uj,uk) < thresh)
+					result.setData(ui,uj,uk,offThresh);
+				else
 				{
-					tally+=(getData(ui+ ul,uj + um,uk + un))*
-							kernel.getData(ul,um,un);
-				}
-				}
+					result.setData(ui,uj,uk,onThresh);
 				}
-
-				result.setData(ui,uj,uk,tally);	
-			}
 			}
-			
-			}
-			break;
 		}
-		case BOUND_MIRROR:
-		case BOUND_HOLD:
-		case BOUND_DERIV_HOLD:
-		{
-			//Calculate the bounding size of the resultant box
-			//Set up the result box
-			if(result.resize(binCount[0],binCount[1],binCount[2],minBound,maxBound))
-				return VOXELS_OUT_OF_MEMORY;
+	}
+}
 
-			//loop through each element in the interior
-			//Forgive the poor indenting, but the looping is very deep
-			{
-			for (long long ui=halfX;ui<(size_t)(binCount[0]-x+1); ui++)
-			{
-			for (long long uj=halfY;uj<binCount[1]-y+1; uj++)
-			{
-			for (long long uk=halfZ;uk<binCount[2]-z+1; uk++)
-			{
-				T tally;
-				tally=T(0.0);
 
-				//Convolve this element with the kernel 
-				for(long long ul=0; ul<x; ul++)
-				{
-				for(long long um=0; um<y; um++)
-				{
-				for(long long un=0; un<z; un++)
-				{
-					tally+=(getData(ui+ ul-halfX,uj + um -halfY,uk + un -halfZ))*
-							kernel.getData(ul,um,un);
-				}
-				}
-				}
-
-				result.setData(ui,uj,uk,tally);	
-			}
-			}
-			}
-			}
-
-			//Lower Boundaries
-			//----------
-			//x- boundary
-			for(long long ui=0;ui<halfX;ui++)
-			{
-			for(long long uj=0;uj<binCount[1];uj++)
-			{
-			for(long long uk=0;uk<binCount[2];uk++)
-			{
-				localPaddedConvolve(ui,uj,uk,kernel,result,boundMode);
-			}
-			}
-			}
-			
-			//y- boundary
-			for(long long ui=0;ui<binCount[0];ui++)
-			{
-			for(long long uj=0;uj<halfY;uj++)
-			{
-			for(long long uk=0;uk<binCount[2];uk++)
-			{
-				localPaddedConvolve(ui,uj,uk,kernel,result,boundMode);
-			}
-			}
-			}
-			
-			//z- boundary
-			for(long long ui=0;ui<binCount[0];ui++)
-			{
-			for(long long uj=0;uj<binCount[1];uj++)
-			{
-			for(long long uk=0;uk<halfZ;uk++)
-			{
-				localPaddedConvolve(ui,uj,uk,kernel,result,boundMode);
-			}
-			}
-			}
-			//----------
-	
-			//Upper boundary
-			//----------
-			//x+ boundary
-			for(long long ui=binCount[0]-x+1;ui<binCount[0];ui++)
-			{
-			for(long long uj=halfY;uj<binCount[1];uj++)
-			{
-			for(long long uk=halfZ;uk<binCount[2];uk++)
-			{	
-				localPaddedConvolve(ui,uj,uk,kernel,result,boundMode);
-			}
-			}
-			}
-
-			//y+ boundary
-			for(long long ui=halfX;ui<binCount[0];ui++)
-			{
-			for(long long uj=binCount[1]-y+1;uj<binCount[1];uj++)
-			{
-			for(long long uk=halfZ;uk<binCount[2];uk++)
-			{
-				localPaddedConvolve(ui,uj,uk,kernel,result,boundMode);
-			}
-			}
-			}
-			
-			//z+ boundary
-			for(long long ui=halfX;ui<binCount[0];ui++)
-			{
-			for(long long uj=halfY;uj<binCount[1];uj++)
-			{
-			for(long long uk=binCount[2]-z+1;uk<binCount[2];uk++)
-			{
-				localPaddedConvolve(ui,uj,uk,kernel,result,boundMode);
-			}
-			}
-			}
-			//----------
-			
-
-
-			break;
-		}
-		default:
-			ASSERT(false);
-			return 2;
-	}
-
-	return 0;
-}
-
-template<class T>
-size_t Voxels<T>::separableConvolve(const Voxels<T> &kernel, Voxels<T> &result,  size_t boundMode) 
-{
-	ASSERT(voxels.size());
-
-	//Loop through all of the voxel elements, setting the
-	//result voxels to the convolution of the template
-	//with the data 
-
-
-	Point3D resultMinBound, resultMaxBound;
-	size_t x,y,z;
-	unsigned long long half;
-	kernel.getSize(x,y,z);
-	half=x/2;
-	//Kernel needs to be cubic
-	ASSERT(x==y && y == z && (z%2));
-
-	if(boundMode!=BOUND_CLIP)
-	{
-		
-		resultMinBound=minBound;
-		resultMaxBound=maxBound;
-
-		ASSERT(result.binCount[0] == binCount[0] &&
-				result.binCount[1] == binCount[1] &&
-				result.binCount[2] == binCount[2]);
-
-		T tally;
-
-		//Separable kernels can be repeatably convolved
-		//ie, f*g can be expressed as (f*g(x) + 
-		//
-
-
-		if(x < binCount[0]/2)
-		{	
-			//Convolve in X direction only
-			for(size_t uj=0;uj<binCount[1];uj++)
-			{
-			for(size_t uk=0;uk<binCount[2];uk++)
-			{
-				for(unsigned long long ui=0;ui<x; ui++)
-				{
-					tally=T(0.0);
-					for(unsigned long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui+ul,uj,uk,boundMode))*
-								kernel.getData(ul+half,half,half);
-
-					result.setData(ui,uj,uk,tally);
-				}
-				
-				for(unsigned long long ui=binCount[0]-x;ui<binCount[0]; ui++)
-				{
-					tally=T(0.0);
-					for(unsigned long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui+ul,uj,uk,boundMode))*
-								kernel.getData(ul+half,half,half);
-					result.setData(ui,uj,uk,tally);
-				}
-
-				//Unpadded section
-				for(size_t ui=x;ui<binCount[0]-x; ui++)
-				{
-					tally=T(0.0);
-					for(unsigned long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui+ul,uj,uk,boundMode))*
-								kernel.getData(ul+half,half,half);
-					
-					result.setData(ui,uj,uk,result.getData(ui,uj,uk)+tally);
-				}
-			}
-
-			}
-		}
-		else
-		{
-			for(size_t uj=0;uj<binCount[1];uj++)
-			{
-			for(size_t uk=0;uk<binCount[2];uk++)
-			{
-			//Kernel is bigger than dataset
-			for(unsigned long long ui=0;ui<binCount[0]; ui++)
-			{
-				tally=T(0.0);
-				for(unsigned long long ul=-half;ul<half; ul++)
-					tally+=(getPaddedData(ui+ul,uj,uk,boundMode))*
-							kernel.getData(ul+half,half,half);
-
-				result.setData(ui,uj,uk,tally);
-			}
-			}
-			}
-		}
-		
-		swap(result);
-
-		if(y < binCount[1]/2)
-		{	
-			//Convolve in Y direction only
-			for(size_t ui=0;ui<binCount[0];ui++)
-			{
-			for(size_t uk=0;uk<binCount[2];uk++)
-			{
-				for(unsigned long long uj=0;uj<y; uj++)
-				{
-					tally=T(0.0);
-					for(unsigned long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui,uj+ul,uk,boundMode))*
-								kernel.getData(half,half+ul,half);
-					result.setData(ui,uj,uk,tally);
-				}
-				
-				for(unsigned long long uj=binCount[1]-y;uj<binCount[1]; uj++)
-				{
-					tally=T(0.0);
-					for(unsigned long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui,uj+ul,uk,boundMode))*
-								kernel.getData(half,half+ul,half);
-					result.setData(ui,uj,uk,tally);
-				}
-
-				//Unpadded section
-				for(size_t uj=y;uj<binCount[1]-y; uj++)
-				{
-					tally=T(0.0);
-					for(unsigned long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui,uj+ul,uk,boundMode))*
-								kernel.getData(half,half+ul,half);
-
-					result.setData(ui,uj,uk,result.getData(ui,uj,uk)+tally);
-				}
-			}
-
-			}
-		}
-		else
-		{
-			for(size_t ui=0;ui<binCount[0];ui++)
-			{
-			for(size_t uk=0;uk<binCount[2];uk++)
-			{
-			//Kernel is bigger than dataset
-			for(unsigned long long uj=0;uj<binCount[1]; uj++)
-			{
-				tally=T(0.0);
-				for(unsigned long long ul=-half;ul<half; ul++)
-					tally+=(getPaddedData(ui,uj+ul,uk,boundMode))*
-							kernel.getData(half,half+ul,half);
-
-				result.setData(ui,uj,uk,tally);
-			}
-			}
-			}
-		}
-	
-		swap(result);
-		
-		if(z < binCount[2]/2)
-		{	
-			//Convolve in Z direction onlbinCount[0]
-			for(size_t ui=0;ui<binCount[0];ui++)
-			{
-			for(size_t uj=0;uj<binCount[1];uj++)
-			{
-				for(long long uk=0;uk<z; uk++)
-				{
-					tally=T(0.0);
-					for(long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui,uj,uk+ul,boundMode))*
-								kernel.getData(half,half,half+ul);
-					result.setData(ui,uj,uk,tally);
-				}
-				
-				for(long long uk=binCount[2]-z;uk<binCount[2]; uk++)
-				{
-					tally=T(0.0);
-					for(long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui,uj,uk+ul,boundMode))*
-								kernel.getData(half,half,half+ul);
-					result.setData(ui,uj,uk,tally);
-				}
-
-				//Unpadded section
-				for(size_t uk=z;uk<binCount[2]-z; uk++)
-				{
-					tally=T(0.0);
-					for(long long ul=-half;ul<half; ul++)
-						tally+=(getPaddedData(ui,uj,uk+ul,boundMode))*
-								kernel.getData(half,half,half+ul);
-
-					result.setData(ui,uj,uk,result.getData(ui,uj,uk)+tally);
-				}
-			}
-
-			}
-		}
-		else
-		{
-			for(size_t ui=0;ui<binCount[0];ui++)
-			{
-			for(size_t uj=0;uj<binCount[1];uj++)
-			{
-			//Kernel is bigger than dataset
-			for(unsigned long long uk=0;uk<binCount[2]; uk++)
-			{
-				tally=T(0.0);
-				for(unsigned long long ul=-half;ul<half; ul++)
-					tally+=(getPaddedData(ui,uj,uk+ul,boundMode))*
-							kernel.getData(half,half,half+ul);
-
-				result.setData(ui,uj,uk,tally);
-			}
-			}
-			}
-		}	
-	}
-	else
-	{
-	
-		//Check the kernel can fit within this datasest
-		if(binCount[0] <x || binCount[1]< y || binCount[2] < z)
-			return 1;
-		resultMinBound=minBound*Point3D((float)(binCount[0]-x)/(float)binCount[0],
-			(float)(binCount[1]-y)/(float)binCount[1],(float)(binCount[2]-z)/(float)binCount[2] );
-		resultMaxBound=maxBound*Point3D((float)(binCount[0]-x)/(float)binCount[0],
-			(float)(binCount[1]-y)/(float)binCount[1],(float)(binCount[2]-z)/(float)binCount[2] );
-		//FIXME: IMPLEMENT ME!
-		ASSERT(false);
-	}
-
-	return 0;
-}
-
-
-template<class T>
-T Voxels<T>::getData(size_t x, size_t y, size_t z) const
-{
-	typedef size_t ull;
-	ASSERT(x < binCount[0] && y < binCount[1] && z < binCount[2]);
-	ull off; //byte offset
-
-	//Typecast everything to at least 64 bit uints.
-	off=(ull)z*(ull)binCount[1]*(ull)binCount[0];
-	off+=(ull)y*(ull)binCount[0] + (ull)x;
-
-	ASSERT(off < voxels.size());
-	return	voxels[off]; 
-}
-
-template<class T>
-T Voxels<T>::getPaddedData(long long x, long long y, long long z, unsigned int padMode) const
-{
-	if(x >=0 && x<(long long)binCount[0] && y>=0 && y<(long long)binCount[1] && z>=0 && z <(long long)binCount[2])
-	{
-		return getData(x,y,z);
-	}
-
-	//OK, so we are not inside the dataset -- we have to guess
-	
-	switch(padMode)
-	{
-		case BOUND_ZERO:
-			return T(0);
-		case BOUND_MIRROR:
-		{
-
-			if(x<0)
-				x=-x;
-			
-			if(y<0)
-				y=-y;
-			
-			if(z<0)
-				z=-z;
-		
-			if(x >=binCount[0])
-				x=binCount[0]-(x-binCount[0]+1);
-			if(y >=binCount[1])
-				y=binCount[1]-(y-binCount[1]+1);
-			if(z >=binCount[2])
-				z=binCount[2]-(z-binCount[2]+1);
-
-			
-			return  getData(x,y,z);
-		}
-		default:
-			//Should only get here if we have not implemented the bound mode
-			ASSERT(false);
-	}
-
-	ASSERT(false);
-}
-
-template<class T>
-void Voxels<T>::setGaussianKernelCube(float stdDev, float bound, size_t sideLen)
-{
-	T obj;
-
-	//Equi-variance multivariate normal distribution.
-	//https://en.wikipedia.org/wiki/Multivariate_normal_distribution#Density_function
-	// with identity covariance. (non-degenerate case)
-	// det|sigma| == (stddev^3)
-	const float product = 1.0/(pow(2.0*M_PI*stdDev,3.0/2.0));
-
-	float scale;
-	const float halfLen = sideLen/2;
-
-	resize(sideLen,sideLen,sideLen);
-
-	setBounds(Point3D(-bound/2.0f,-bound/2.0f,-bound/2.0f),
-		Point3D(bound/2.0f,bound/2.0f,bound/2.0f));
-
-
-	//2*det(covariance matrix)
-	const float twoDetCov=2.0*stdDev*stdDev*stdDev;
-
-	scale=bound/sideLen;
-	scale*=scale;
-	//Loop through the data, setting 
-	// using probability density function
-	for(size_t ui=0;ui<sideLen;ui++)
-	{
-		for(size_t uj=0;uj<sideLen;uj++)
-		{
-			for(size_t uk=0;uk<sideLen;uk++)
-			{
-				size_t offset;
-				offset = (size_t)(((float)ui+0.5f-halfLen)*((float)ui+0.5f-halfLen) + 
-					((float)uj+0.5f-halfLen)*((float)uj+0.5f-halfLen) + 
-					((float)uk+0.5f-halfLen)*((float)uk+0.5f-halfLen));
-
-				obj = T(product*exp(-scale*((float)offset)/(twoDetCov)));
-				setData(ui,uj,uk,obj);
-			}
-		}
-	}
-}
-
-template<class T>
-void Voxels<T>::secondDifference(Voxels<T> &result, size_t boundMode) const
-{
-	//Only clipping mode is supported at this time
-	ASSERT(boundMode=BOUND_CLIP);
-	//Note: Central difference loses two values from each edge
-	ASSERT(binCount[0] > 2 && binCount[1] > 2 && binCount[2] > 2);
-	float xSqr,ySqr,zSqr;
-
-	xSqr = (maxBound[0]-minBound[0])/binCount[0];
-
-	ySqr = (maxBound[1]-minBound[1])/binCount[1];
-
-	zSqr = (maxBound[2]-minBound[2])/binCount[2];
-
-
-
-	//Allocate the second difference result, with a bin wdith
-	//Note: We are bastardising the definition of xSqr for convenience here
-	//xSqr at this time is simply the grid pitch (same for ySqr,zSqr)
-
-	result.resize(binCount[0]-2,binCount[1]-2,binCount[2]-2,
-					Point3D((binCount[0]-2)*xSqr,
-						(binCount[1]-2)*ySqr,
-						(binCount[2]-2)*zSqr));
-
-	
-	xSqr*=xSqr;
-	ySqr*=ySqr;
-	zSqr*=zSqr;
-
-
-	//Calculate del^2
-	T d;
-#pragma omp parallel for private(d)
-	for(MP_INT_TYPE ui=1; ui<binCount[0]-1; ui++)
-	{
-		for(MP_INT_TYPE  uj=1; uj<binCount[1]-1; uj++)
-		{
-			for(MP_INT_TYPE uk=1; uk<binCount[2]-1; uk++)
-			{
-			
-				//dx by first order central difference
-				d=T((getData(ui+1,uj,uk) - 2.0*getData(ui,uj,uk) + getData(ui-1,uj,uk))/(xSqr));
-				
-				//dy
-				d+=T((getData(ui,uj+1,uk) - 2.0*getData(ui,uj,uk) + getData(ui,uj-1,uk))/(ySqr));
-				
-				//dz
-				d+=T((getData(ui,uj,uk+1) - 2.0*getData(ui,uj,uk) + getData(ui,uj,uk-1))/(zSqr));
-				result.setData(ui-1,uj-1,uk-1,d);
-			}
-		}
-	}
-
-}
-
-template<class T>
-void Voxels<T>::thresholdForPosition(std::vector<Point3D> &p, const T &thresh, bool lowerEq) const
-{
-	p.clear();
-
-	if(lowerEq)
-	{
-#pragma omp parallel for 
-		for(MP_INT_TYPE ui=0;ui<binCount[0]; ui++)
-		{
-			for(size_t uj=0;uj<binCount[1]; uj++)
-			{
-				for(size_t uk=0;uk<binCount[2]; uk++)
-				{
-					if( getData(ui,uj,uk) < thresh)
-					{
-#pragma omp critical
-						p.push_back(getPoint(ui,uj,uk));
-					}
-
-				}
-			}
-		}
-	}
-	else
-	{
-#pragma omp parallel for 
-		for(MP_INT_TYPE ui=0;ui<binCount[0]; ui++)
-		{
-			for(size_t uj=0;uj<binCount[1]; uj++)
-			{
-				for(size_t uk=0;uk<binCount[2]; uk++)
-				{
-					if( getData(ui,uj,uk) > thresh)
-					{
-#pragma omp critical
-						p.push_back(getPoint(ui,uj,uk));
-					}
-
-				}
-			}
-		}
-	}
-}
-
-template<class T>
-void Voxels<T>::binarise(Voxels<T> &result, const T &thresh, 
-				const T &onThresh, const T &offThresh) const
-{
-
-	result.resize(binCount[0],binCount[1],
-			binCount[2],minBound,maxBound);
-#pragma omp parallel for
-	for(MP_INT_TYPE ui=0;ui<(MP_INT_TYPE)binCount[0]; ui++)
-	{
-		for(size_t uj=0;uj<binCount[1]; uj++)
-		{
-			for(size_t uk=0;uk<binCount[2]; uk++)
-			{
-				if( getData(ui,uj,uk) < thresh)
-					result.setData(ui,uj,uk,offThresh);
-				else
-				{
-					result.setData(ui,uj,uk,onThresh);
-				}
-			}
-		}
-	}
-}
-
-template<class T>
-void Voxels<T>::rebin(Voxels<T> &result, size_t rate, size_t clipDir) const
-{
-	//Check that the binsize can be reduced by this factor
-	//or clipping allowed
-	ASSERT(clipDir || ( !(binCount[0] % rate) && !(binCount[1] % rate)
-						&& !(binCount[2] % rate)));
-
-	//Datasets must be bigger than  their clipping direction
-	ASSERT(binCount[0] > rate && binCount[1] && rate && binCount[2] > rate);
-
-	
-	size_t newBin[3];
-	newBin[0]=binCount[0]/rate;
-	newBin[1]=binCount[1]/rate;
-	newBin[2]=binCount[2]/rate;
-
-
-	//Clipping not implmented!
-	ASSERT(clipDir == CLIP_NONE);
-
-	result.resize(newBin[0],newBin[1],newBin[2],getMinBounds(), getMaxBounds());
-	//Draw a progress bar
-	cerr << std::endl << "|";
-	for(unsigned int ui=0; ui<100; ui++)
-		cerr << ".";
-	cerr << "| 100%" << std::endl << "|";
-
-	size_t its=0;
-	unsigned int progress =0;
-	float itsToDo = binCount[0]/rate;
-#pragma omp parallel for shared(progress,its) 
-	for(MP_INT_TYPE ui=0; ui<binCount[0]-rate; ui+=rate)
-	{ 
-#pragma omp critical
-		{
-			its++;
-			//Update progress bar
-			if(progress < (size_t) ((float)its/itsToDo*100) )
-			{
-				cerr <<  ".";
-				progress = (size_t) ((float)its/itsToDo*100);
-			}
-		}
-
-		for(size_t uj=0; uj<binCount[1]-rate; uj+=rate)
-		{
-
-			for(size_t uk=0; uk<binCount[2]-rate; uk+=rate)
-			{
-				double sum;
-				sum=0;
-
-				//Forgive the indenting. This is deep.
-				for(size_t uir=0; uir<rate; uir++)
-				{
-					for(size_t ujr=0; ujr<rate; ujr++)
-					{
-						for(size_t ukr=0; ukr<rate; ukr++)
-						{
-							ASSERT(ui+uir < binCount[0]);
-							ASSERT(uj+ujr < binCount[1]);
-							ASSERT(uk+ukr < binCount[2]);
-							sum+=getData(ui+uir,uj+ujr,uk +ukr);
-						}
-					}
-				}
-				
-			
-				sum/=(double)(rate*rate*rate);
-
-				result.setData(ui/rate,uj/rate,uk/rate,T(sum));
-			}
-		}
-	}
-
-	//Fill out the progress bar
-	while(progress++ <100)
-		cerr << ".";
-	
-	cerr << "| done" << std::endl;
-	
-	
-}
-
-template<class T>
-void Voxels<T>::makeSphericalKernel(size_t sideLen, float bound, const T &val, unsigned int antialiasLevel) 
-{
-
-	const size_t halfLen = sideLen/2;
-	float sqrSideLen=(float)halfLen*(float)halfLen;
-	resize(sideLen,sideLen,sideLen,Point3D(bound,bound,bound));
-
-	if(!antialiasLevel)
-	{
-		//Loop through the data
-		//This could be sped up using decrementing whiles, 
-		//at the cost of losing the openMP bit
-#pragma omp parallel for shared(sqrSideLen)
-		for(MP_INT_TYPE ui=0;ui<sideLen;ui++)
-		{
-			for(size_t uj=0;uj<sideLen;uj++)
-			{
-				for(size_t uk=0;uk<sideLen;uk++)
-				{
-					float offset;
-					//Calcuate r^2
-					offset = (ui-halfLen)*(ui-halfLen) + (uj-halfLen)*(uj-halfLen) + (uk-halfLen)*(uk-halfLen);
-
-					if(offset < (float)sqrSideLen)
-						setData(ui,uj,uk,val);
-					else
-						setData(ui,uj,uk,0);
-				}
-			}
-		}
-	
-	}
-	else
-	{
-		//We want an antialiased sphere. This is a little more tricky
-#pragma omp parallel for shared(sqrSideLen)
-		for(MP_INT_TYPE ui=0;ui<sideLen;ui++)
-		{
-			for(size_t uj=0;uj<sideLen;uj++)
-			{
-				for(size_t uk=0;uk<sideLen;uk++)
-				{
-					float offset[8];
-					bool insideSphere,fullCalc;
-					//Calcuate r^2 from sphere centre for each corner
-					for(unsigned int off=0;off<8; off++)
-					{
-						float x,y,z;
-						x= ui + 2*(off &1)-1;
-						y= uj + (off &2)-1;
-						z= uk + 0.5*(off &4)-1;
-						offset[off] =  (x - halfLen)*(x-halfLen) + (y-halfLen)*(y-halfLen)
-								+ (z-halfLen)*(z-halfLen);
-					}
-
-					insideSphere= offset[0] < (float)sqrSideLen;
-					fullCalc=false;
-
-					//Spin through each to check if this is a border-straddling voxel
-					for(unsigned int off=1;off<8; off++)
-					{
-						if( (offset[off] < (float)sqrSideLen && !insideSphere) ||
-							(offset[off]  > (float)sqrSideLen && insideSphere))
-						{
-							//We have to do a full volume fraction
-							//calculation to compute the value of this voxel
-							fullCalc=true;
-							break;
-						}
-					}
-
-
-					if(fullCalc)
-					{
-						//Compute volume fraction
-
-						//The method we use is a recursive divide & measure approach
-						//We chop the voxel into eight half size voxels, then check 
-						//to see which lie in the sphere, and which dont.
-						std::stack<std::pair<Point3D, unsigned int > > positionStack;
-						float value,x,y,z;
-						value=0;
-						//Push this voxel's 8 sub-voxel's centres onto the stack
-						//to kick things off
-						for(int off=0;off<8; off++) //CRITICAL that off is an int for bitmask
-						{
-							//the right hand part of the addition should
-							//flip between -0.5 and +0.5 alternately
-							x= ui + 0.5*(2*(off &1)-1);
-							y= uj + 0.5*((off &2)-1);
-							z= uk + 0.5*(0.5*(off &4)-1);
-
-							positionStack.push(std::make_pair(Point3D(x,y,z),0));
-						}
-
-						//Level 0 corresponds to 1/8th of the original voxel. Level n = 1/(2^3(n+1)) 
-						//each voxel has side length L_v = originalLen/(2^(level+1))
-						while(!positionStack.empty())
-						{
-							unsigned int thisLevel;
-							float thisLen;
-							Point3D thisCentre;
-
-							thisCentre = positionStack.top().first;
-							thisLevel = positionStack.top().second;
-							positionStack.pop();
-
-							
-							//Side length for this voxel
-							thisLen = pow(2.0,-((int)thisLevel+1))*halfLen;
-							//Calculate r^2 from sphere centre for each corner
-							for(int off=0;off<8; off++) //critical that off is int for bitmasking
-							{
-								x= thisCentre[0] + (2*(off &1)-1)*thisLen;
-								y= thisCentre[1] + ((off &2)-1)*thisLen;
-								z= thisCentre[2] + (0.5*(off &4)-1)*thisLen;
-
-								offset[off] =  (x - halfLen)*(x-halfLen) + (y-halfLen)*(y-halfLen)
-										+ (z-halfLen)*(z-halfLen);
-							}
-
-							insideSphere= offset[0] < (float)sqrSideLen;
-							fullCalc=false;
-
-							//Spin through each to check if this is a border-straddling voxel
-							for(int off=1;off<8; off++)
-							{
-								if( (offset[off] < (float)sqrSideLen && !insideSphere) ||
-									(offset[off]  > (float)sqrSideLen && insideSphere))
-								{
-									//We have to do a full volume fraction
-									//calculation to compute the value of this voxel
-									fullCalc=true;
-									break;
-								}
-							}
-
-							//OK, the action to take now depends on whether we need to subdivide or not
-							if(thisLevel < antialiasLevel && fullCalc)
-							{
-								for(int off=0;off<8; off++) //Critical that off is signed for bitmask
-								{
-									x= thisCentre[0] + (2*(off &1)-1)*thisLen;
-									y= thisCentre[1] + ((off &2)-1)*thisLen;
-									z= thisCentre[2] + (0.5*(off &4)-1)*thisLen;
-
-									positionStack.push(std::make_pair(Point3D(x,y,z),thisLevel+1));
-								}
-							}
-							else
-							{
-								if(fullCalc)
-								{
-									//Lets just call it a half shall we
-									value+=pow(2.0,-(((int)thisLevel+1)*3+1));
-
-								}
-								else
-								{
-									//Voxel is either wholly within, or without
-									//If inside sphere, add to internal volume
-									//otherwise we "add zero" - ie do nothing
-									if(insideSphere)
-										value+=pow(2.0,-((int)thisLevel+1)*3);
-								}
-							}
-							
-						}
-					
-				
-						setData(ui,uj,uk,value*val);
-					}
-					else
-						setData(ui,uj,uk,((int)insideSphere)*val);
-				}
-			}
-		}
-	}
-	
-}
 
 template<class T>
 T Voxels<T>::min() const
 {
 	ASSERT(voxels.size());
-	size_t numPts = binCount[0]*binCount[1]*binCount[2];
-	T minVal;
-	minVal = voxels[0];
-#ifdef _OPENMP
-	//Unfortunately openMP doesn't lend itself well here
-	//But we can code around it.
-	size_t maxThr = omp_get_max_threads();	
-	T minArr[maxThr];
-	//Init all mins
-	for(size_t ui=0; ui<maxThr; ui++)
-		minArr[ui] = voxels[0];
-
-	//parallel min search	
-	#pragma omp parallel for shared(minArr)
-	for(MP_INT_TYPE ui=0;ui<(MP_INT_TYPE)numPts; ui++)
-		minArr[omp_get_thread_num()] = std::min(minArr[omp_get_thread_num()],voxels[ui]);	
-
-	//find min of mins
-	for(size_t ui=0;ui<maxThr; ui++)
-		minVal=std::min(minArr[ui],minVal);
-#else
-
-	for(size_t ui=0;ui<numPts; ui++)
-		minVal=std::min(minVal,voxels[ui]);
-#endif
-	return minVal;
-}
 
-template<class T>
-T Voxels<T>::max() const
-{
-	ASSERT(voxels.size());
-	size_t numPts = binCount[0]*binCount[1]*binCount[2];
-	T maxVal;
-	maxVal = voxels[0];
-#ifdef _OPENMP
-	//Unfortunately openMP doesn't lend itself well here
-	//But we can code around it.
-	size_t maxThr = omp_get_max_threads();	
-	T maxArr[maxThr];
-	//Init all maxs
-	for(size_t ui=0; ui<maxThr; ui++)
-		maxArr[ui] = voxels[0];
-
-	//parallel max search	
-	#pragma omp parallel for shared(maxArr)
-	for(MP_INT_TYPE ui=0;ui<(MP_INT_TYPE)numPts; ui++)
-		maxArr[omp_get_thread_num()] = std::max(maxArr[omp_get_thread_num()],voxels[ui]);	
-
-	//find max of maxs
-	for(size_t ui=0;ui<maxThr; ui++)
-		maxVal=std::max(maxArr[ui],maxVal);
-#else
-
-	for(size_t ui=0;ui<numPts; ui++)
-		maxVal=std::max(maxVal,voxels[ui]);
-#endif
-	return maxVal;
+	using namespace vigra::acc;
+	AccumulatorChain<T,Select<Minimum> > s;
+	extractFeatures(voxels.begin(),voxels.end(),s);
+	return get<Minimum>(s);
 }
 
 template<class T>
-void Voxels<T>::minMax(T &minVal,T &maxVal) const
+T Voxels<T>::max() const
 {
 	ASSERT(voxels.size());
-	size_t numPts = binCount[0]*binCount[1]*binCount[2];
-	maxVal=voxels[0];
-	minVal=voxels[0];
-#ifdef _OPENMP
-	//Unfortunately openMP doesn't lend itself well here
-	//But we can code around it.
-	size_t maxThr = omp_get_max_threads();	
-	T minArr[maxThr],maxArr[maxThr];
-	//Init all maxs
-	for(size_t ui=0; ui<maxThr; ui++)
-	{
-		maxArr[ui] = voxels[0];
-		minArr[ui]=voxels[0];
-	}
-
-	//parallel max search	
-	#pragma omp parallel for shared(maxArr)
-	for(MP_INT_TYPE ui=0;ui<(MP_INT_TYPE)numPts; ui++)
-	{
-		maxArr[omp_get_thread_num()] = std::max(maxArr[omp_get_thread_num()],voxels[ui]);	
-		minArr[omp_get_thread_num()] = std::min(minArr[omp_get_thread_num()],voxels[ui]);	
-	}
-
-	//find max of maxs
-	for(size_t ui=0;ui<maxThr; ui++)
-	{
-		maxVal=std::max(maxArr[ui],maxVal);
-		minVal=std::min(minArr[ui],minVal);
-	}
-#else
-
-	for(size_t ui=0;ui<numPts; ui++)
-	{
-		maxVal=std::max(maxVal,voxels[ui]);
-		minVal=std::min(minVal,voxels[ui]);
-	}
-#endif
+	
+	using namespace vigra::acc;
+	AccumulatorChain<T,Select<Maximum> > s;
+	extractFeatures(voxels.begin(),voxels.end(),s);
+	return get<Maximum>(s);
 }
 
 
 template<class T>
-void Voxels<T>::fillSpheresByPosition( const std::vector<Point3D> &spherePos, float rad, const T &value, bool doErase)
+void Voxels<T>::minMax(T &min, T&max) const
 {
-
-	//I haven't though this through for non cubic datasets
-	ASSERT((maxBound[0]-minBound[0]) == (maxBound[1]-minBound[1]));
-	ASSERT((maxBound[1]-minBound[1]) == (maxBound[2]-minBound[2]));
-	ASSERT(binCount[0] == binCount[1]);
-	ASSERT(binCount[1] == binCount[2]);
-
-	//Size of rectangular box that contains the sphere
-	size_t numBlocks[3];
-	numBlocks[0] = (size_t)(2.0*rad*binCount[0]/(maxBound[0]-minBound[0]));
-	numBlocks[1] = (size_t)(2.0*rad*binCount[1]/(maxBound[1]-minBound[1]));
-	numBlocks[2] = (size_t)(2.0*rad*binCount[2]/(maxBound[2]-minBound[2]));
-
-	//Construct the sphere kernel
-	std::vector<int> vX,vY,vZ;
-	float sqrRad=(float)numBlocks[0]*(float)numBlocks[0]/4.0f;
-#pragma omp parallel for
-	for(MP_INT_TYPE ui=0;ui<numBlocks[0]; ui++)
-	{
-		for(size_t uj=0;uj<numBlocks[1]; uj++)
-		{
-			for(size_t uk=0;uk<numBlocks[2]; uk++)
-			{
-				long long delta[3];
-				float sqrDist;
-				
-				delta[0]=ui-numBlocks[0]/2;
-				delta[1]=uj-numBlocks[1]/2;
-				delta[2]=uk-numBlocks[2]/2;
-
-				sqrDist=((float)delta[0]*(float)delta[0]) + 
-					((float)delta[1]*(float)delta[1]) + 
-					((float)delta[2]*(float)delta[2]);
-
-				if(sqrDist < sqrRad)
-				{
-#pragma omp critical
-					{
-					vX.push_back(delta[0]);
-					vY.push_back(delta[1]);
-					vZ.push_back(delta[2]);
-					}
-				}
-				
-
-			}
-		}
-	}
-
-
-	//Clear the dataset
-	if(doErase)
-		fill(T(0.0));
-#pragma omp parallel for	
-	for(MP_INT_TYPE ui=0; ui<spherePos.size(); ui++)
-	{
-		size_t sphereCentre[3];
+	ASSERT(voxels.size());
 	
-		getIndex(sphereCentre[0],sphereCentre[1],sphereCentre[2],spherePos[ui]);
-
-		//Calculate the points that lie within the sphere
-		for(size_t uj=0;uj<vX.size(); uj++)
-		{
-			long long p[3];
-			
-			p[0] = sphereCentre[0] + vX[uj];
-			p[1] = sphereCentre[1] + vY[uj];
-			p[2] = sphereCentre[2] + vZ[uj];
-
-			if(p[0] < binCount[0] && p[0] > 0 &&
-				p[1] < binCount[1] && p[1] > 0 &&
-				p[2] < binCount[2] && p[2] > 0)
-			{	
-#pragma omp critical
-				setData(p[0],p[1],p[2],value);
-			}
-
-		}
-
-	}
+	using namespace vigra::acc;
+	AccumulatorChain<T,Select<Minimum,Maximum> > s;
+	extractFeatures(voxels.begin(),voxels.end(),s);
+	min=get<Minimum>(s);
+	max=get<Maximum>(s);
 }
 
-
 template<class T>
 int Voxels<T>::countPoints( const std::vector<Point3D> &points, bool noWrap, bool doErase)
 {
-	ASSERT(callback);
 	if(doErase)
 	{
 		fill(0);	
@@ -2438,15 +1327,10 @@ int Voxels<T>::countPoints( const std::vector<Point3D> &points, bool noWrap, boo
 
 	size_t x,y,z;
 
-	unsigned int downSample=MAX_CALLBACK;
 	for(size_t ui=0; ui<points.size(); ui++)
 	{
-		if(!downSample--)
-		{
-			if(!(*callback)(false))
-				return VOXEL_ABORT_ERR;
-			downSample=MAX_CALLBACK;
-		}
+		if(*voxelsWantAbort)
+			return VOXEL_ABORT_ERR;
 		
 		T value;
 		getIndex(x,y,z,points[ui]);
@@ -2520,13 +1404,13 @@ void Voxels<T>::getIndexWithUpper(size_t &x, size_t &y,
 	//but, as a special case, if the index is the same as our bincount, check
 	//to see if it is positioned on an edge
 	if(x==binCount[0] &&  
-		fabs(p[0] -maxBound[0]) < sqrt(std::numeric_limits<float>::epsilon()))
+		fabs(p[0] -maxBound[0]) < sqrtf(std::numeric_limits<float>::epsilon()))
 		x--;
 	if(y==binCount[1] &&  
-		fabs(p[1] -maxBound[1]) < sqrt(std::numeric_limits<float>::epsilon()))
+		fabs(p[1] -maxBound[1]) < sqrtf(std::numeric_limits<float>::epsilon()))
 		y--;
 	if(z==binCount[2] &&  
-		fabs(p[2] -maxBound[2]) < sqrt(std::numeric_limits<float>::epsilon()))
+		fabs(p[2] -maxBound[2]) < sqrtf(std::numeric_limits<float>::epsilon()))
 		z--;
 
 }
@@ -2534,198 +1418,14 @@ void Voxels<T>::getIndexWithUpper(size_t &x, size_t &y,
 template<class T>
 void Voxels<T>::fill(const T &v)
 {
-	size_t nBins = binCount[0]*binCount[1]*binCount[2];
-
-#pragma omp parallel for
-	for(MP_INT_TYPE ui=0;ui<(MP_INT_TYPE)nBins; ui++)
-		voxels[ui]=v;
-}
-
-
-template<class T>
-int Voxels<T>::histogram(std::vector<size_t> &v, size_t histBinCount) const
-{
-	ASSERT(callback);
-
-	T maxVal=max();
-	T minVal=min();
-	
-#ifdef _OPENMP
-	//We need an array for each thread to store results
-	size_t *vals;
-
-	vals = new size_t[omp_get_max_threads()*histBinCount];
-
-	//Zero out the array
-	for(size_t ui=0;ui<omp_get_max_threads()*histBinCount; ui++)
-		*(vals+ui)=0;
-	
-
-	bool spin=true;
-	//Do it -- loop through, writing to each threads "scratch pad"	
-	//to prevent threads interfering with one another
-#pragma omp parallel for
-	for(MP_INT_TYPE ui=0; ui<binCount[0]; ui++)
-	{
-		if(spin)
-			continue;
-		size_t *basePtr;
-		size_t offset;
-		basePtr=vals + omp_get_thread_num()*histBinCount;
-		for(size_t uj=0; uj <binCount[1]; uj++)
-		{
-			for(size_t uk=0; uk <binCount[2]; uk++)
-			{
-				offset = (getData(ui,uj,uk)-minVal)/(maxVal -minVal)*(histBinCount-1);
-				
-				basePtr[offset]++;
-			}
-		}
-#pragma omp critical
-		{
-		if(!(*callback)(false))
-			spin=true;
-		}
-
-	}
-
-	if(spin)
-		return VOXEL_ABORT_ERR;
-	//Merge the values
-	v.resize(histBinCount);
-	for(size_t thr=0;thr<omp_get_max_threads(); thr++)
-	{
-		size_t *basePtr;
-		basePtr=vals + thr*histBinCount;
-		for(size_t ui=0;ui<histBinCount; ui++)
-		{
-			v[ui]+=basePtr[ui];
-		}
-	}
-
-#else
-	//We need an array for each thread to store results
-	size_t *vals=new size_t[histBinCount];
-
-	for(size_t ui=0;ui<histBinCount; ui++)
-		vals[ui]=0;
-	size_t bc;
-	bc=binCount[0];
-	bc*=binCount[1];
-	bc*=binCount[2];
-	for(size_t ui=0; ui <bc; ui++)
-	{
-		size_t offset;
-		offset = (size_t)((getData(ui)-minVal)/(maxVal -minVal)*histBinCount);
-		vals[offset]++ ;
-	}
-
-	v.resize(histBinCount);
-	for(size_t ui=0;ui<histBinCount; ui++)
-		v[ui]=vals[ui];
-		
-#endif
-	delete[] vals;	
-
-
-	ASSERT(v.size() == histBinCount);
-
-	return 0;	
-}
-
-template<class T>
-void Voxels<T>::findNExtrema(std::vector<size_t> &x, std::vector<size_t> &y, 
-			std::vector<size_t> &z, size_t n, bool largest) const
-{
-	//Could be better if we didn't use indexed data acquisition (record position)
-	std::deque<size_t> bSx,bSy,bSz;
-
-	if(voxels.empty())
-		return;
-	
-	T curBest;
-	curBest=getData(0);
-	
-	//It is theoretically possible to rewrite this without locking (critical sections).
-	if(largest)
-	{
-		
-		for(size_t ui=0;ui<binCount[0]; ui++)
-		{
-			for(size_t uj=0;uj<binCount[1]; uj++)
-			{
-				for(size_t uk=0;uk<binCount[2]; uk++)
-				{
-					
-					if ( getData(ui,uj,uk) > curBest)
-					{
-#pragma omp critical 
-						{
-						bSx.push_front(ui);
-						bSy.push_front(uj);
-						bSz.push_front(uk);
-						curBest=getData(ui,uj,uk);
-						}
-					}
-					
-				}
-			}
-		}
-		
-	}
-	else
-	{
-		for(size_t ui=0;ui<binCount[0]; ui++)
-		{
-			for(size_t uj=0;uj<binCount[1]; uj++)
-			{
-				for(size_t uk=0;uk<binCount[2]; uk++)
-				{
-					
-					if ( getData(ui,uj,uk) < curBest)
-					{
-#pragma omp critical 
-						{
-						
-						bSx.push_front(ui);
-						bSy.push_front(uj);
-						bSz.push_front(uk);
-						curBest=getData(ui,uj,uk);
-
-						}
-						
-					}
-					
-				}
-			}
-		}
-		
-	}
-	
-
-	ASSERT(bSx.size() == bSy.size() == bSz.size());
-
-	x.resize(n);
-	y.resize(n);
-	z.resize(n);
-	while(n--)
-	{
-		x[n] =bSx.back(); 
-		y[n] =bSy.back(); 
-		z[n] =bSz.back(); 
-	
-		bSx.pop_back();	
-		bSy.pop_back();	
-		bSz.pop_back();	
-	}
+	voxels=v;
 }
 
-
 //Obtain a slice of the voxel data. Data output will be in column order
 // p[posB*nA + posA]. Input slice must be sufficiently sized and allocated
 // to hold the output data
 template<class T>
-void Voxels<T>::getSlice(size_t normalAxis, size_t offset, T *p,size_t boundMode) const
+void Voxels<T>::getSlice(size_t normalAxis, size_t offset, T *p) const
 {
 	ASSERT(normalAxis < 3);
 
@@ -2758,83 +1458,44 @@ void Voxels<T>::getSlice(size_t normalAxis, size_t offset, T *p,size_t boundMode
 	}
 		
 
-	if(offset < binCount[normalAxis])
+	//We are within bounds, use normal access functions
+	switch(normalAxis)
 	{
-		//We are within bounds, use normal access functions
-		switch(normalAxis)
+		case 0:
 		{
-			case 0:
+			for(size_t ui=0;ui<binCount[dimA];ui++)
 			{
-				for(size_t ui=0;ui<binCount[dimA];ui++)
-				{
-					for(size_t uj=0;uj<binCount[dimB];uj++)
-						p[uj*nA + ui] =	getData(offset,ui,uj);
-				}
-				break;
-			}
-			case 1:
-			{	
-				for(size_t ui=0;ui<binCount[dimA];ui++)
-				{
-					for(size_t uj=0;uj<binCount[dimB];uj++)
-						p[uj*nA + ui] =	getData(ui,offset,uj);
-				}
-				break;
+				for(size_t uj=0;uj<binCount[dimB];uj++)
+					p[uj*nA + ui] =	getData(offset,ui,uj);
 			}
-			case 2:
+			break;
+		}
+		case 1:
+		{	
+			for(size_t ui=0;ui<binCount[dimA];ui++)
 			{
-				for(size_t ui=0;ui<binCount[dimA];ui++)
-				{
-					for(size_t uj=0;uj<binCount[dimB];uj++)
-						p[uj*nA + ui] =	getData(ui,uj,offset);
-				}
-				break;
+				for(size_t uj=0;uj<binCount[dimB];uj++)
+					p[uj*nA + ui] =	getData(ui,offset,uj);
 			}
-			default:
-				ASSERT(false);
+			break;
 		}
-	}
-	else
-	{
-		//We are outside the cube bounds, use the padded access functions
-		switch(normalAxis)
+		case 2:
 		{
-			case 0:
-			{
-				for(size_t ui=0;ui<binCount[dimA];ui++)
-				{
-					for(size_t uj=0;uj<binCount[dimB];uj++)
-						p[uj*nA + ui] =	getPaddedData(offset,ui,uj,boundMode);
-				}
-				break;
-			}
-			case 1:
-			{	
-				for(size_t ui=0;ui<binCount[dimA];ui++)
-				{
-					for(size_t uj=0;uj<binCount[dimB];uj++)
-						p[uj*nA + ui] =	getPaddedData(ui,offset,uj,boundMode);
-				}
-				break;
-			}
-			case 2:
+			for(size_t ui=0;ui<binCount[dimA];ui++)
 			{
-				for(size_t ui=0;ui<binCount[dimA];ui++)
-				{
-					for(size_t uj=0;uj<binCount[dimB];uj++)
-						p[uj*nA + ui] =	getPaddedData(ui,uj,offset,boundMode);
-				}
-				break;
+				for(size_t uj=0;uj<binCount[dimB];uj++)
+					p[uj*nA + ui] =	getData(ui,uj,offset);
 			}
-			default:
-				ASSERT(false);
+			break;
 		}
+		default:
+			ASSERT(false);
 	}
 }
 
 template<class T>
-void Voxels<T>::getSlice(size_t normal, float offset, 
-		T *p, size_t interpMode,size_t boundMode) const
+void Voxels<T>::getInterpSlice(size_t normal, float offset, 
+		T *p, size_t interpMode) const
 {
 	ASSERT(offset <=1.0f && offset >=0.0f);
 
@@ -2845,7 +1506,7 @@ void Voxels<T>::getSlice(size_t normal, float offset,
 		{
 			size_t slicePos;
 			slicePos=roundf(offset*binCount[normal]);
-			getSlice(normal,slicePos,p,boundMode);
+			getSlice(normal,slicePos,p);
 			break;
 		}
 		case SLICE_INTERP_LINEAR:
@@ -2861,8 +1522,8 @@ void Voxels<T>::getSlice(size_t normal, float offset,
 				
 				pLower  = new T[numEntries];
 
-				getSlice(normal,sliceLower,pLower,boundMode);
-				getSlice(normal,sliceUpper,p,boundMode);
+				getSlice(normal,sliceLower,pLower);
+				getSlice(normal,sliceUpper,p);
 
 				//Get the decimal part of the float
 				float integ;
@@ -2881,18 +1542,42 @@ void Voxels<T>::getSlice(size_t normal, float offset,
 
 }
 
+template<class T>
+void Voxels<T>::isotropicGaussianSmooth(float stdev,float windowRatio)
+{
+	//perform in-place smoothing
+	vigra::ConvolutionOptions<3> opt  = vigra::ConvolutionOptions<3>().filterWindowSize(windowRatio);
+	vigra::gaussianSmoothMultiArray(vigra::srcMultiArrayRange(voxels),
+			vigra::destMultiArray(voxels),stdev,opt);
+
+}
+
+template<class T>
+void Voxels<T>::laplaceOfGaussian(float stdev, float windowRatio)
+{
+	//perform in-place smoothing
+	vigra::ConvolutionOptions<3> opt  = vigra::ConvolutionOptions<3>().filterWindowSize(windowRatio);
+	vigra::laplacianOfGaussianMultiArray(vigra::srcMultiArrayRange(voxels),
+			vigra::destMultiArray(voxels),stdev,opt);
+
+}
 
 template<class T>
 void Voxels<T>::operator/=(const Voxels<T> &v)
 {
 	ASSERT(v.voxels.size() == voxels.size());
-#pragma omp parallel for
-	for (size_t i = 0; i < voxels.size(); i++)
+
+	//don't use the built-in /, this 
+	// can generate inf values (which is correct)
+	// that we want to avoid.
+	for(size_t ui=0;ui<voxels.size();ui++)
 	{
-		if(v.voxels[i])
-			voxels[i]/=(v.voxels[i]);
+		if(v.voxels[ui] )
+			voxels[ui]/=v.voxels[ui];
 		else
-			voxels[i] =0;
+		{
+			ASSERT(!voxels[ui]);
+		} 
 	}
 }	
 
@@ -2900,10 +1585,20 @@ void Voxels<T>::operator/=(const Voxels<T> &v)
 template<class T>
 void Voxels<T>::operator/=(const T &v)
 {
-	ASSERT(v);
-#pragma omp parallel for
-	for (size_t i = 0; i < voxels.size(); i++)
-		voxels[i]/=v;
+	ASSERT(v.voxels.size() == voxels.size());
+
+	//don't use the built-in /, this 
+	// can generate inf values (which is correct)
+	// that we want to avoid.
+	for(size_t ui=0;ui<voxels.size();ui++)
+	{
+		if( v > T(0) )
+			voxels[ui]/=v;
+		else
+		{
+			voxels[ui]=0;
+		} 
+	}
 }	
 
 template<class T>
@@ -2916,16 +1611,9 @@ bool Voxels<T>::operator==(const Voxels<T> &v) const
 			return false;
 	}
 
-	for(size_t ui=0;ui<voxels.size();ui++)
-	{
-		if(v.getData(ui) != getData(ui))
-			return false;
-	}
-
-	return true;
+	return v.voxels == voxels;
 }
 
-
 //===
 
 
diff --git a/src/common/xmlHelper.cpp b/src/common/xmlHelper.cpp
index 2518266..7bcba58 100644
--- a/src/common/xmlHelper.cpp
+++ b/src/common/xmlHelper.cpp
@@ -1,6 +1,6 @@
 /* 
  * XmlHelper.cpp : libXML2 wrapper code
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/common/xmlHelper.h b/src/common/xmlHelper.h
index 8568ad3..88352e6 100644
--- a/src/common/xmlHelper.h
+++ b/src/common/xmlHelper.h
@@ -1,6 +1,6 @@
 /* 
  * XMLHelper.h - libXML2 wrapper functions
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/gl/cameras.cpp b/src/gl/cameras.cpp
index 7d368d7..4e2f5f3 100644
--- a/src/gl/cameras.cpp
+++ b/src/gl/cameras.cpp
@@ -1,6 +1,6 @@
 /*
  *	cameras.cpp - opengl camera implementations
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -606,7 +606,7 @@ void CameraLookAt::getProperties(CameraProperties &p) const
 	cp.key=CAMERA_KEY_LOOKAT_ORIGIN;
 	p.addEntry(cp);
 
-	//Add camea target pt
+	//Add came target pt
 	stream_cast(cp.data,target);
 	cp.name=TRANS("Target");
 	cp.type=PROPERTY_TYPE_POINT3D;
diff --git a/src/gl/cameras.h b/src/gl/cameras.h
index 6dd25c8..78a506b 100644
--- a/src/gl/cameras.h
+++ b/src/gl/cameras.h
@@ -1,6 +1,6 @@
 /*
  *	cameras.h - 3D cameras for opengl
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gl/drawables.cpp b/src/gl/drawables.cpp
index 543c98f..08b6f4a 100644
--- a/src/gl/drawables.cpp
+++ b/src/gl/drawables.cpp
@@ -1,6 +1,6 @@
 /*
  *	drawables.cpp - opengl drawable objects cpp file
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -24,7 +24,9 @@
 
 #include "common/colourmap.h"
 
+#include "common/voxels.h"
 
+#include "glDebug.h"
 
 
 const float DEPTH_SORT_REORDER_EPSILON = 1e-2;
@@ -56,7 +58,7 @@ void drawCone(const Point3D &axisVec, const Point3D &origin,
 {
 	Point3D axis;
 	axis =axisVec;
-	if(axis.sqrMag() < sqrt(std::numeric_limits<float>::epsilon()))
+	if(axis.sqrMag() < sqrtf(std::numeric_limits<float>::epsilon()))
 		axis=Point3D(0,0,1);
 	else
 		axis.normalise();
@@ -72,7 +74,7 @@ void drawCone(const Point3D &axisVec, const Point3D &origin,
 	
 	Point3D *ptArray = new Point3D[numSegments];
 
-	const float ROT_TOL=sqrt(std::numeric_limits<float>::epsilon()) ;
+	const float ROT_TOL=sqrtf(std::numeric_limits<float>::epsilon()) ;
 
 	//Only rotate if the angle is nonzero (note 2PI wraparound is possible from acos)
 	if((tiltAngle > ROT_TOL	|| fabs(tiltAngle - 2*M_PI) > ROT_TOL) && 
@@ -233,7 +235,7 @@ DrawableObj::~DrawableObj()
 }
 	
 	
-float DrawableObj::getHighContrastValue() const
+float DrawableObj::getHighContrastValue() 
 {
 	//Perform luminence check on background to try to create most appropriate
 	// colour
@@ -401,7 +403,7 @@ void DrawVector::draw() const
 	{
 		//Back off the distance a little, because otherwise the line can poke out
 		// the sides of the cone.
-		float backoffFactor = std::max(radius/sqrt(vector.sqrMag()),0.0f);
+		float backoffFactor = std::max(radius/sqrtf(vector.sqrMag()),0.0f);
 		Point3D tmpVec=vector*(1.0f-backoffFactor) + origin;
 		
 		if(doubleEnded)
@@ -429,7 +431,7 @@ void DrawVector::draw() const
 	glPopAttrib();
 
 	//If we only wanted the line, then we are done here.
-	if(arrowSize < sqrt(std::numeric_limits<float>::epsilon()) || !drawArrow)
+	if(arrowSize < sqrtf(std::numeric_limits<float>::epsilon()) || !drawArrow)
 		return ;
 
 	//Now compute & draw the cone tip
@@ -546,6 +548,21 @@ void DrawQuad::setVertex(unsigned int v, const Point3D &p)
 	vertices[v] = p;
 }
 
+void DrawQuad::setColour(float rNew, float gNew, float bNew, float aNew)
+{
+	ASSERT(rNew >=0 && rNew <=1.0f);
+	ASSERT(gNew >=0 && gNew <=1.0f);
+	ASSERT(bNew >=0 && bNew <=1.0f);
+	ASSERT(aNew >=0 && aNew <=1.0f);
+	for(unsigned int ui=0;ui<4; ui++)
+	{
+		r[ui]=rNew;		
+		g[ui]=gNew;		
+		b[ui]=bNew;		
+		a[ui]=aNew;		
+	}
+}
+
 Point3D DrawQuad::getOrigin() const
 {
 	return Point3D::centroid(vertices,4);
@@ -575,7 +592,7 @@ void DrawQuad::recomputeParams(const vector<Point3D> &vecs,
 	}
 }
 
-DrawTexturedQuad::DrawTexturedQuad() :textureData(0), textureId((unsigned int)-1), noColour(false)
+DrawTexturedQuad::DrawTexturedQuad() :textureData(0), textureId((unsigned int)-1), noColour(false) , needsBinding(true)
 {
 }
 
@@ -597,6 +614,11 @@ DrawTexturedQuad::~DrawTexturedQuad()
 
 void DrawTexturedQuad::draw() const
 {
+	if(needsBinding)
+	{
+		rebindTexture();
+	}
+
 	ASSERT(glIsTexture(textureId));
 	
 	glEnable(GL_TEXTURE_2D);
@@ -605,20 +627,31 @@ void DrawTexturedQuad::draw() const
 	
 	
 	glBindTexture(GL_TEXTURE_2D,textureId);
-
-	if(!noColour)
-		glColor4f(1.0f,1.0f,1.0f,1.0f);
-	
 	const float COORD_SEQ_X[]={ 0,0,1,1};
 	const float COORD_SEQ_Y[]={ 0,1,1,0};
-	glBegin(GL_QUADS);
+
+	if(!noColour)
+	{
+		glBegin(GL_QUADS);
 		for(size_t ui=0;ui<4;ui++)
 		{
+			glColor4f(r[ui],g[ui],b[ui],a[ui]);
 			glTexCoord2f(COORD_SEQ_X[ui],COORD_SEQ_Y[ui]);
 			glVertex3fv(vertices[ui].getValueArr());
 		}
-	glEnd();
-
+		glEnd();
+	}	
+	else
+	{
+		glBegin(GL_QUADS);
+			for(size_t ui=0;ui<4;ui++)
+			{
+				glColor4f(1.0f,1.0f,1.0f,a[ui]);
+				glTexCoord2f(COORD_SEQ_X[ui],COORD_SEQ_Y[ui]);
+				glVertex3fv(vertices[ui].getValueArr());
+			}
+		glEnd();
+	}
 	glPopAttrib();
 	glDisable(GL_TEXTURE_2D);	
 }
@@ -648,7 +681,7 @@ void DrawTexturedQuad::resize(size_t numX, size_t numY,
 
 }
 
-void DrawTexturedQuad::rebindTexture(unsigned int mode)
+void DrawTexturedQuad::rebindTexture(unsigned int mode) const
 {
 	ASSERT(texPool);
 	ASSERT(textureData);
@@ -668,8 +701,7 @@ void DrawTexturedQuad::rebindTexture(unsigned int mode)
 	glTexImage2D(GL_TEXTURE_2D,0,mode,nX,nY,
 		0,mode,GL_UNSIGNED_BYTE,textureData);
 
-
-
+	needsBinding=false;	
 }
 
 void DrawTexturedQuad::setData(size_t x, size_t y, unsigned char *entry)
@@ -809,7 +841,7 @@ void DrawCylinder::draw() const
 	if(!q || !qCap[0] || !qCap[1])
 		return;
 
-	//Cross product desired drection with default
+	//Cross product desired direction with default
 	//direction to produce rotation vector
 	Point3D dir(0.0f,0.0f,1.0f);
 
@@ -821,8 +853,8 @@ void DrawCylinder::draw() const
 
 	float length=sqrtf(direction.sqrMag());
 	float angle = dir.angle(dirNormal);
-	if(angle < M_PI - sqrt(std::numeric_limits<float>::epsilon()) &&
-		angle > sqrt(std::numeric_limits<float>::epsilon()))
+	if(angle < M_PI - sqrtf(std::numeric_limits<float>::epsilon()) &&
+		angle > sqrtf(std::numeric_limits<float>::epsilon()))
 	{
 		//we need to rotate
 		dir = dir.crossProd(dirNormal);
@@ -1340,7 +1372,7 @@ void DrawGLText::draw() const
 					//the text direction is now the x-axis
 					glTranslatef(advance/2.0f,halfHeight,0);
 					//spin text around its front direction 180 degrees
-					//no need to trnaslate as text sits at its baseline
+					//no need to translate as text sits at its baseline
 					glRotatef(180,0,0,1);
 					//move halfway along text, noting that 
 					//the text direction is now the x-axis
@@ -1757,6 +1789,207 @@ bool DrawTexturedQuadOverlay::setTexture(const char *textureFile)
 	return textureOK;
 }
 
+
+DrawProgressCircleOverlay::DrawProgressCircleOverlay()
+{
+	stepProgress=0;
+	step=0;
+	maxStep=0;
+
+}
+
+DrawProgressCircleOverlay::~DrawProgressCircleOverlay()
+{
+}
+
+void DrawProgressCircleOverlay::reset()
+{
+	stepProgress=0;
+	maxStep=0;
+	totalFilters=0;
+	curFilter=0;	
+}
+
+void DrawProgressCircleOverlay::draw( )const
+{
+	if(!maxStep)
+		return;
+
+	ASSERT(curFilter <=totalFilters);
+	//TODO: Is this redundant? might be already handled
+	// by scene?
+	glMatrixMode(GL_PROJECTION);	
+	glPushMatrix();
+	glLoadIdentity();
+	gluOrtho2D(0, winX, winY, 0);
+	
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	glLoadIdentity();
+
+	glDisable(GL_CULL_FACE);
+	glEnable(GL_DEPTH_TEST);
+	//size of gap to fit, in degrees
+	const float FILTER_SPACING_ANGLE= 20.0f/(float)totalFilters;
+	//Angular step when drawing wheel
+	const float DEG_STEP= 2;
+	
+	//fraction of radius for inner section
+	const float IN_RADIUS_FRACTION=0.85;
+	//compute circle radius
+	float radiusOut,radiusIn;
+	radiusOut = std::min(height,width)/2.0f;
+	radiusIn= radiusOut*IN_RADIUS_FRACTION; 
+
+
+
+	//Allows for gap between each step, and one at start and end
+	float thetaPerFilter = (360.0f - FILTER_SPACING_ANGLE*(totalFilters))/totalFilters;
+		
+	//Draw the complete filters 
+	float curTheta=FILTER_SPACING_ANGLE/2.0f;	
+	for(size_t ui=1;ui<curFilter;ui++)
+	{
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerFilter,true);
+		curTheta+=thetaPerFilter+FILTER_SPACING_ANGLE;
+	}
+
+
+	//Draw the completed Steps
+	float thetaPerStep =  thetaPerFilter/maxStep;
+	for(size_t ui=1;ui<step;ui++)
+	{
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerStep,true);
+		curTheta+=thetaPerStep;
+
+		if(ui < step-1)
+		{
+			//Draw a line to mark the step
+			glColor4f(1.0f,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);
+			glEnd();
+		}
+	}
+	//Draw the current step
+	if(stepProgress == 100)
+	{
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerStep,true);
+	}
+	else if (!stepProgress)
+	{
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerStep,false);
+	}
+	else
+	{
+		//draw two segments, one with the current progress value in the complete style
+		float interpFrac = (float)stepProgress/100.0f;
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerStep*interpFrac,true);
+		// and one with the incomplete style
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta+thetaPerStep*interpFrac, 
+			curTheta+thetaPerStep,false);
+
+	}
+	curTheta+=thetaPerStep;
+	//Draw the remaining incomplete steps
+	for(size_t ui=step+1; ui<=maxStep;ui++)
+	{
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerStep,false);
+		curTheta+=thetaPerStep;
+	}
+
+	curTheta+=FILTER_SPACING_ANGLE;
+
+	//Draw the incomplete filters
+	for(size_t ui=curFilter+1; ui<=totalFilters;ui++)
+	{
+		drawSection(DEG_STEP,
+			IN_RADIUS_FRACTION*radiusIn, 
+			radiusOut,curTheta, 
+			curTheta+thetaPerFilter,false);
+		curTheta+=thetaPerFilter+FILTER_SPACING_ANGLE;
+	}
+	
+	glPopMatrix(); //Pop modelview matrix
+
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+
+	glMatrixMode(GL_MODELVIEW);
+
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
+}
+
+void DrawProgressCircleOverlay::drawSection(unsigned int degreeStep, 
+	float rIn, float rOut,float startTheta, float stopTheta, bool complete) const
+{
+	//TODO: all calulations that call this should use radiians	
+	float startThetaRad = startTheta*M_PI/180.0f;
+	float endThetaRad = stopTheta*M_PI/180.0f;
+	float degStepRad = degreeStep*M_PI/180.0f;
+
+	unsigned int nSegments = (endThetaRad-startThetaRad)/degStepRad;
+
+	if(!nSegments)
+		return;
+
+	float alphaBase,dt;
+	getAnimationStat(alphaBase,dt);	
+	if(alphaBase == 0.0f)
+		return;
+
+
+
+
+	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);
+	else
+		glColor4f(1.0f,1.0f,1.0f,ALPHA_INCOMPLETE);
+
+	//Draw arc
+	glBegin(GL_TRIANGLE_STRIP);
+	float thetaOne=startThetaRad;
+	float thetaTwo=startThetaRad+degStepRad;
+	glVertex2f(position[0] + rIn*cos(thetaOne),position[1] + rIn*sin(thetaOne));
+	for(size_t ui=0;ui<nSegments;ui++)
+	{
+		thetaOne=startThetaRad + ui*degStepRad;;
+		thetaTwo=startThetaRad + (ui+1)*degStepRad;
+		
+		glVertex2f(position[0] + rOut*cos(thetaOne),position[1] + rOut*sin(thetaOne));
+		glVertex2f(position[0] + rIn*cos(thetaTwo),position[1] + rIn*sin(thetaTwo));
+
+
+	}
+	glVertex2f(position[0] + rOut*cos(thetaTwo),position[1] + rOut*sin(thetaTwo));
+	glEnd();
+}
+
+
 DrawAnimatedOverlay::DrawAnimatedOverlay()
 {
 	fadeIn=0.0f;
@@ -1783,30 +2016,47 @@ bool DrawAnimatedOverlay::setTexture(const vector<string> &texFiles,
 	return textureOK;
 }
 
-void DrawAnimatedOverlay::draw() const
+void DrawAnimatedOverlay::getAnimationStat(float &alpha , float &animDeltaTime) const
 {
-	if(!textureOK)
-		return;
-
-	float texCoordZ;
-	float alphaVal=1.0f;
-	{
+		
 	timeval t;
 	gettimeofday(&t,NULL);
-	float animDeltaTime=(float)(t.tv_sec - animStartTime.tv_sec) +
+	animDeltaTime=(float)(t.tv_sec - animStartTime.tv_sec) +
 		(t.tv_usec-animStartTime.tv_usec)/1.0e6;
 
+
 	//Skip if we wish to show later
 	if(animDeltaTime < delayBeforeShow)
+	{
+		alpha= 0;
 		return;
+	}
 
+	animDeltaTime-=delayBeforeShow;
 
-	if(fadeIn > 0.0f && (delayBeforeShow + fadeIn > animDeltaTime) ) 
-		alphaVal= (animDeltaTime - delayBeforeShow )/(fadeIn) ;
+	if(fadeIn > 0.0f && (fadeIn > animDeltaTime) ) 
+	{
+		alpha= (animDeltaTime )/(fadeIn) ;
+	}
+	else
+		alpha= 1.0f;
+	
+}
 
+void DrawAnimatedOverlay::draw() const
+{
+	if(!textureOK)
+		return;
+
+	float alphaVal, animDeltaTime;
+	getAnimationStat(alphaVal,animDeltaTime);
+
+	if(alphaVal== 0.0f)
+		return;
+	float texCoordZ;
 	texCoordZ=fmod(animDeltaTime,repeatInterval);
 	texCoordZ/=repeatInterval;
-	}
+
 	ASSERT(glIsTexture(textureId));
 	
 	glMatrixMode(GL_PROJECTION);	
@@ -1820,8 +2070,11 @@ void DrawAnimatedOverlay::draw() const
 
 	glEnable(GL_TEXTURE_3D);
 	glBindTexture(GL_TEXTURE_3D,textureId);
-	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MULT); 
-	
+
+	//TODO: Find correct blending mode. Default is good, but may change...	
+//	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); 
+//	glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
+
 	// Draw overlay quad 
 	ASSERT(width == height); // width/height should be the same
 	glColor4f(1.0f,1.0f,1.0f,alphaVal);
@@ -1991,7 +2244,7 @@ DrawPointLegendOverlay::DrawPointLegendOverlay() : enabled(true)
 
 		dQuad.setUseColouring(false);
 
-		//Create a ciruclar texture
+		//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 };
@@ -2051,7 +2304,6 @@ DrawPointLegendOverlay::DrawPointLegendOverlay(const DrawPointLegendOverlay &oth
 	
 	position[0]=oth.position[0];
 	position[1]=oth.position[1];
-	quadSet=oth.quadSet;
 }
 
 void DrawPointLegendOverlay::draw() const
@@ -2070,8 +2322,9 @@ void DrawPointLegendOverlay::draw() const
 
 	float maxTextWidth=0;
 
-	
-	font->FaceSize(1);
+
+	if(font)	
+		font->FaceSize(1);
 	for(unsigned int ui=0; ui<legendItems.size();ui++)
 	{
 		for(;ui<legendItems.size();ui++)
@@ -2170,7 +2423,7 @@ void DrawField3D::setColourMinMax()
 			
 void DrawField3D::draw() const
 {
-	if(alphaVal < sqrt(std::numeric_limits<float>::epsilon()))
+	if(alphaVal < sqrtf(std::numeric_limits<float>::epsilon()))
 		return;
 
 	ASSERT(field);
@@ -2373,7 +2626,7 @@ void DrawIsoSurface::getBoundingBox(BoundCube &b) const
 
 void DrawIsoSurface::draw() const
 {
-	if(a< sqrt(std::numeric_limits<float>::epsilon()))
+	if(a< sqrtf(std::numeric_limits<float>::epsilon()))
 		return;
 
 	if(!cacheOK)
diff --git a/src/gl/drawables.h b/src/gl/drawables.h
index 9d0e1de..fd3776e 100644
--- a/src/gl/drawables.h
+++ b/src/gl/drawables.h
@@ -1,6 +1,6 @@
 /*
  *	drawables.h - Opengl drawable objects header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -19,12 +19,6 @@
 #ifndef DRAWABLES_H
 #define DRAWABLES_H
 
-
-//STL includes
-
-
-
-
 //MacOS is "special" and puts it elsewhere
 #ifdef __APPLE__ 
 	#include <OpenGL/glu.h>
@@ -38,6 +32,9 @@
 #include "cameras.h"
 #include "isoSurface.h"
 
+template<class T>
+class Voxels;
+
 //TODO: Work out if there is any way of obtaining the maximum 
 //number of items that can be drawn in an opengl context
 //For now Max it out at 10 million (~120MB of vertex data)
@@ -104,6 +101,7 @@ enum
 	DRAW_TYPE_ISOSURFACE,
 	DRAW_TYPE_AXIS,
 	DRAW_TYPE_LEGENDOVERLAY,
+	DRAW_TYPE_PROGRESSCIRCLE_OVERLAY,
 };
 
 //TODO: It seems unnecessary to have multiple types for the bind
@@ -156,7 +154,7 @@ class DrawableObj
 		//Size of the opengl window
 		static unsigned int winX,winY;
 
-		float getHighContrastValue() const;
+		static float getHighContrastValue();
 	public: 
 		//!Can be selected from openGL viewport interactively?
 		bool canSelect;
@@ -209,7 +207,7 @@ class DrawableObj
 		virtual ~DrawableObj();
 
 		//!If we offer any kind of external pointer interface; use this to do a recomputation as needed. This is needed for selection binding behaviour
-		virtual void recomputeParams(const vector<Point3D> &vecs, const vector<float> &scalars, unsigned int mode) {};
+		virtual void recomputeParams(const std::vector<Point3D> &vecs, const std::vector<float> &scalars, unsigned int mode) {};
 		
 		//!Set the current camera
 		static void setCurCamera(const Camera *c){curCamera=c;};
@@ -418,9 +416,6 @@ class DrawTriangle : public DrawableObj
  */
 class DrawQuad : public DrawableObj
 {
-	private:
-		//!Colours of the vertices (rgba colour model)
-		float r[4],g[4],b[4],a[4];
 	protected:
 		//!Vertices of the quad
 		Point3D vertices[4];
@@ -430,6 +425,8 @@ class DrawQuad : public DrawableObj
 		/*! Lighting for this class is per triangle only no
 		 * per vertex lighting */
 		Point3D normal;
+		//!Colours of the vertices (rgba colour model)
+		float r[4],g[4],b[4],a[4];
 	public:
 		//!Constructor
 		DrawQuad() {};
@@ -449,6 +446,9 @@ class DrawQuad : public DrawableObj
 		void setVertices(const Point3D *);
 		//!Set the colour of a vertex
 		void setColour(unsigned int, float r, float g, float b, float a);
+		//!Set the colour of all vertices
+		void setColour(float r, float g, float b, float a);
+
 		//!Update the normal to the surface from vertices
 		/*!Uses the first 3 vertices to calculate the normal.
 		 */
@@ -461,8 +461,8 @@ class DrawQuad : public DrawableObj
 		
 		//!Recompute the internal parameters using the input vector information
 		// i.e. this is used for (eg) mouse interaction
-		void recomputeParams(const vector<Point3D> &vecs, 
-				const vector<float> &scalars, unsigned int mode);
+		void recomputeParams(const std::vector<Point3D> &vecs, 
+				const std::vector<float> &scalars, unsigned int mode);
 };
 
 class DrawTexturedQuad : public DrawQuad
@@ -475,15 +475,21 @@ class DrawTexturedQuad : public DrawQuad
 		size_t channels;
 		size_t displayMode;
 
+		//FIXME: This should be non-mutable.  We need
+		// to move texture rebinding to a pre-processing step, not at draw time
 		//ID of the texture to use when drawing, -1 if not bound
 		// to opengl
-		unsigned int textureId;
+		mutable unsigned int textureId;
 		
 		//!FTGL font instance
 		FTFont *font;
 		
 		//disallow resetting base colour to white 
 		bool noColour;
+
+		//we can only bind from the main thread.
+		// this is true by default, until the texture is bound
+		mutable bool needsBinding;
 		
 	public:
 		DrawTexturedQuad();
@@ -497,7 +503,7 @@ class DrawTexturedQuad : public DrawQuad
 		//Set the specified pixel in the texture to this value 
 		void setData(size_t x, size_t y, unsigned char *entry);
 		//Send the texture to the video card. 
-		void rebindTexture(unsigned int mode=GL_RGB);	
+		void rebindTexture(unsigned int mode=GL_RGB) const;	
 		
 		void setUseColouring(bool useColouring) {noColour= !useColouring;};
 };
@@ -547,8 +553,8 @@ class DrawSphere : public DrawableObj
 
 		//!Recompute the internal parameters using the input vector information
 		// i.e. this is used for (eg) mouse interaction
-		void recomputeParams(const vector<Point3D> &vecs, 
-				const vector<float> &scalars, unsigned int mode);
+		void recomputeParams(const std::vector<Point3D> &vecs, 
+				const std::vector<float> &scalars, unsigned int mode);
 
 };
 
@@ -609,7 +615,7 @@ class DrawCylinder : public DrawableObj
 		void getBoundingBox(BoundCube &b) const ;
 
 		//!Recompute the internal parameters using the input vector information
-		void recomputeParams(const vector<Point3D> &vecs, const vector<float> &scalars, unsigned int mode);
+		void recomputeParams(const std::vector<Point3D> &vecs, const std::vector<float> &scalars, unsigned int mode);
 
 		virtual bool needsDepthSorting() const;
 
@@ -801,8 +807,8 @@ class DrawGLText : public DrawableObj
 		void setAlignment(unsigned int mode);
 		
 		//Binding parameter recomputation
-		void recomputeParams(const vector<Point3D> &vecs, 
-				const vector<float> &scalars, unsigned int mode);
+		void recomputeParams(const std::vector<Point3D> &vecs, 
+				const std::vector<float> &scalars, unsigned int mode);
 };
 
 
@@ -845,7 +851,7 @@ class DrawRectPrism  : public DrawableObj
 		void getBoundingBox(BoundCube &b) const;
 		
 		//!Recompute the internal parameters using the input vector information
-		void recomputeParams(const vector<Point3D> &vecs, const vector<float> &scalars, unsigned int mode);
+		void recomputeParams(const std::vector<Point3D> &vecs, const std::vector<float> &scalars, unsigned int mode);
 };
 
 struct RGBFloat
@@ -883,7 +889,7 @@ class DrawColourBarOverlay : public DrawableOverlay
 		FTFont *font;
 
 		//!Colours for each element
-		vector<RGBFloat> rgb;
+		std::vector<RGBFloat> rgb;
 		//!Minimum and maximum values for the colour bar (for ticks)
 		float min,max;
 
@@ -896,9 +902,9 @@ class DrawColourBarOverlay : public DrawableOverlay
 
 		virtual unsigned int getType() const {return DRAW_TYPE_COLOURBAR;}
 
-		void setColourVec(const vector<float> &r,
-					const vector<float> &g,
-					const vector<float> &b);
+		void setColourVec(const std::vector<float> &r,
+					const std::vector<float> &g,
+					const std::vector<float> &b);
 		//!Draw object
 		void draw() const;
 
@@ -927,7 +933,6 @@ class DrawTexturedQuadOverlay : public DrawableOverlay
 		void draw() const;
 };
 
-
 //!Multi-frame texture - Animated overlay
 class DrawAnimatedOverlay : public DrawableOverlay
 {
@@ -949,6 +954,8 @@ class DrawAnimatedOverlay : public DrawableOverlay
 		//Time for fadein after show
 		float fadeIn;
 
+	protected:		
+		void getAnimationStat(float &alpha, float &deltaTime) const;
 	public:
 		DrawAnimatedOverlay();
 		~DrawAnimatedOverlay();
@@ -969,7 +976,7 @@ class DrawAnimatedOverlay : public DrawableOverlay
 			{ ASSERT(fadeInTime >=0.0f); fadeIn=fadeInTime;}
 
 		//!Set the texture by name
-		bool setTexture(const vector<string> &textureFiles, float timeRepeat=1.0f);
+		bool setTexture(const std::vector<std::string> &textureFiles, float timeRepeat=1.0f);
 
 		void resetTime() ;
 
@@ -979,6 +986,47 @@ class DrawAnimatedOverlay : public DrawableOverlay
 		bool isOK() const { return textureOK; }
 };
 
+//!Draw a progress (segments with completion) overlay
+class DrawProgressCircleOverlay : public DrawAnimatedOverlay
+{
+
+	//Shows the progress of K filters, each with M steps,  and each
+	// step has a (0-100) progress. Result is drawn as filled arcs.
+	// Each filter is one arc, and this is divided into steps.
+	// each step then fills up
+
+	private:
+		//Progress in the current step, range [0,100]
+		unsigned int stepProgress;
+		//Number of steps in process 
+		unsigned int maxStep;
+		//The step that we are currently in
+		unsigned int step;
+		//Number of filters that are to be analysed 
+		unsigned int totalFilters;
+		//Filter that we are analysing (0->n-1)
+		unsigned int curFilter;
+		
+
+		//Draw a 2D wheel shaped section. Complete variable toggles the style of the drawing from a completed, to a n incompleted segment
+		void drawSection(unsigned int degreeStep, 
+	float rIn, float rOut,float startTheta, float stopTheta, bool complete) const;
+	public:
+		DrawProgressCircleOverlay();
+		~DrawProgressCircleOverlay();
+		void setCurFilter(unsigned int v) { curFilter= v;}
+		void setMaxStep(unsigned int v) { maxStep= v;}
+		void setNumFilters(unsigned int v) { totalFilters= v;}
+		void setProgress(unsigned int newProg) { ASSERT(newProg <=100); stepProgress = newProg;}
+		void setStep(unsigned int v) { ASSERT(v<=maxStep); step= v;}
+
+		void reset();
+
+		static void setWindowSize(unsigned int x, unsigned int y){winX=x;winY=y;};
+		virtual unsigned int getType() const {return DRAW_TYPE_PROGRESSCIRCLE_OVERLAY;}
+		void draw() const;
+};
+
 class DrawPointLegendOverlay : public DrawableOverlay
 {
 	private:
@@ -987,7 +1035,7 @@ class DrawPointLegendOverlay : public DrawableOverlay
 
 	FTFont *font;
 	//Items to draw n overlay, and colour to use to draw
-	vector<pair<string,RGBFloat> > legendItems;
+	std::vector<std::pair<std::string,RGBFloat> > legendItems;
 	bool enabled;
 	public:
 		DrawPointLegendOverlay();
@@ -1000,7 +1048,7 @@ class DrawPointLegendOverlay : public DrawableOverlay
 		void draw() const;
 
 		void clear(); 
-		void addItem(const string &s, float r, float g, float b);
+		void addItem(const std::string &s, float r, float g, float b);
 
 };
 
@@ -1103,7 +1151,7 @@ private:
 
 	Voxels<float> *voxels;	
 
-	mutable vector<TriangleWithVertexNorm> mesh;
+	mutable std::vector<TriangleWithVertexNorm> mesh;
 
 	//!Warning. Although I declare this as const, I do some naughty mutating to the cache.
 	void updateMesh() const;	
diff --git a/src/gl/effect.cpp b/src/gl/effect.cpp
index 89c3eb3..e07bb18 100644
--- a/src/gl/effect.cpp
+++ b/src/gl/effect.cpp
@@ -1,6 +1,6 @@
 /*
  *	effect.cpp - 3D visuals effects implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gl/effect.h b/src/gl/effect.h
index 117f574..0e71e4d 100644
--- a/src/gl/effect.h
+++ b/src/gl/effect.h
@@ -1,6 +1,6 @@
 /*
  *	effect.h - opengl 3D effects header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gl/glDebug.h b/src/gl/glDebug.h
index e975a62..2c87f7f 100644
--- a/src/gl/glDebug.h
+++ b/src/gl/glDebug.h
@@ -1,6 +1,6 @@
 /*
  *	glDebug.h - opengl debugging routines
- *	Copyright (C) 2014, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -35,8 +35,7 @@
 					fprintf(stderr, "glError: %s caught at %s:%u\n", (char *)gluErrorString(err), __FILE__, __LINE__); \
 					err = glGetError(); \
 				} \
-		std::cerr << "glErr Clean " << __FILE__ << ":" << __LINE__ << std::endl; \
-}
+		}
 
 inline int glCurStackDepth(int stackDepthSelector)
 {
diff --git a/src/gl/isoSurface.cpp b/src/gl/isoSurface.cpp
index 0e4714b..20110c9 100644
--- a/src/gl/isoSurface.cpp
+++ b/src/gl/isoSurface.cpp
@@ -1,6 +1,6 @@
 /*
  *	IsoSurface.cpp - Isosurface interface generation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -18,7 +18,19 @@
 
 #include "isoSurface.h"
 
+#include "common/assertion.h"
+#include "common/voxels.h"
+
 #include <map>
+#include <list>
+#include <vector>
+
+
+using std::list;
+using std::map;
+using std::vector;
+using std::pair;
+using std::make_pair;
 
 #ifdef DEBUG
 template<class T1, class T2>
@@ -536,7 +548,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 				if(iEdgeFlags & (1<<iEdge))
 				{
 					//Store a  reference to the vertex position
-					asEdgeVertex[iEdge] = v.getEdgeIndex(iX,iY,iZ,edgeRemap[iEdge]);
+					asEdgeVertex[iEdge] = v.deprecatedGetEdgeUniqueIndex(iX,iY,iZ,edgeRemap[iEdge]);
 				}
 			}
 
@@ -588,7 +600,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 		for(size_t uj=0;uj<3;uj++)
 		{
 			map<size_t,list<size_t> >::iterator it;
-			it = edgeTriMap.find((indexedTriVec[ui].p[uj] >>2 ) << 2);
+			it = edgeTriMap.find(indexedTriVec[ui].p[uj]); 
 			if(it == edgeTriMap.end())
 			{
 
@@ -596,7 +608,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 				seedList.push_back(ui);
 #pragma omp critical
 				edgeTriMap.insert(
-					make_pair((indexedTriVec[ui].p[uj] >>2 ) << 2,seedList));
+					make_pair(indexedTriVec[ui].p[uj],seedList));
 						
 			}
 			else
@@ -616,7 +628,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 		Point3D low,high,voxelFrameIntersection; 
 		float lowF,highF;
 
-		if(pointMap.find((it->first>>2)<<2) != pointMap.end())
+		if(pointMap.find((it->first)) != pointMap.end())
 			continue;
 
 		//Low/high sides of edge's scalar values
@@ -624,7 +636,9 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 
 
 		//Get the edge's low and high end node positions
-		v.getEdgeEnds((it->first>>2)<<2,low,high);
+		v.getEdgeEnds(it->first,low,high);
+
+
 		
 		//OK, now we have that, lets use the values to "lever" the 
 		//solution point note node locations for isosurface 
@@ -642,7 +656,8 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 		}
 
 			
-		pointMap.insert(make_pair((it->first>>2)<<2,voxelFrameIntersection));
+		pointMap.insert(make_pair(it->first,voxelFrameIntersection));
+
 	}	
 
 
@@ -656,7 +671,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 		//Do a lookup of the edge point locations
 		// by mapping the edge indices to the edge points 
 		for(int uj=0;uj<3;uj++)
-			tVec[ui].p[uj] = pointMap.at((indexedTriVec[ui].p[uj]>>2)<<2);
+			tVec[ui].p[uj] = pointMap.at((indexedTriVec[ui].p[uj]));
 
 		if(tVec[ui].isDegenerate())
 		{
@@ -706,7 +721,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 		if(weight > smallNum)
 		{
 			for(int uj=0;uj<3;uj++)
-				pointMap.at((indexedTriVec[ui].p[uj]>>2) <<2)+=origNormal[ui]*weight;
+				pointMap.at((indexedTriVec[ui].p[uj]))+=origNormal[ui]*weight;
 		}
 	}
 
@@ -726,7 +741,7 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 	for(size_t ui=0;ui<indexedTriVec.size();ui++)
 	{
 		for(unsigned int uj=0;uj<3;uj++)
-			tVec[ui].normal[uj] = pointMap.at((indexedTriVec[ui].p[uj]>>2)<<2);
+			tVec[ui].normal[uj] = pointMap.at((indexedTriVec[ui].p[uj]));
 	}	
 	// --
 	
@@ -738,3 +753,63 @@ void marchingCubes(const Voxels<float> &v,float isoValue, vector<TriangleWithVer
 	// http://users.telenet.be/tfautre/softdev/tristripper/
 
 }
+
+#ifdef DEBUG
+#include "common/mesh.h"
+bool testIsoSurface()
+{
+	Voxels<float> data;
+
+	data.resize(4,4,4);
+	data.fill(0);
+	data.setData(1,1,1,1.0);
+
+
+	vector<TriangleWithVertexNorm> tVec;
+	marchingCubes(data,0.5,tVec);
+
+	TEST(!tVec.empty(),"isosurface exists");
+
+	
+
+	//Should be 2 rect. pyramids back to back, with no bases
+	TEST(tVec.size() == 8, "isosurf. triangle count"); 
+	//Ensure that all the points are contained within the original data bounding box
+
+	Point3D pMin,pMax;
+	data.getBounds(pMin,pMax);
+	BoundCube b;
+	b.setBounds(pMin,pMax);
+
+	for(size_t ui=0;ui<tVec.size();ui++)
+	{
+		for(size_t uj=0;uj<3;uj++)
+		{
+			TEST(b.containsPt(tVec[ui].p[uj]),"Mesh contained in voxel bounds");
+		}
+	}
+
+	//Convert the triangle soup into a mesh
+	Mesh debugMesh;
+	TRIANGLE t;
+	for(size_t ui=0;ui<tVec.size();ui++)
+	{
+
+		for(size_t uj=0;uj<3;uj++)
+		{
+			t.p[uj] = debugMesh.nodes.size();
+			debugMesh.nodes.push_back(tVec[ui].p[uj]);
+		}
+		
+		debugMesh.triangles.push_back(t);	
+	}	
+	ASSERT(debugMesh.isSane())
+
+	debugMesh.saveGmshMesh("debug-isosurf.msh");
+
+	//Convert all duplicate vertices into single blob 
+	debugMesh.mergeDuplicateVertices(0.0001);
+	ASSERT(debugMesh.isSane())
+	return true;
+}
+#endif
diff --git a/src/gl/isoSurface.h b/src/gl/isoSurface.h
index 76cc006..86ffaff 100644
--- a/src/gl/isoSurface.h
+++ b/src/gl/isoSurface.h
@@ -1,6 +1,6 @@
 /*
  * isoSurface.h  - Marching cubes implementation 
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -45,5 +45,8 @@ struct TriangleWithIndexedVertices
 void marchingCubes(const Voxels<float> &v,float isoValue, 
 		std::vector<TriangleWithVertexNorm> &tVec);
 
+#ifdef DEBUG
+bool testIsoSurface();
+#endif
 
 #endif
diff --git a/src/gl/scene.cpp b/src/gl/scene.cpp
index e240e7e..c4e927e 100644
--- a/src/gl/scene.cpp
+++ b/src/gl/scene.cpp
@@ -1,6 +1,6 @@
 /*
  *	scene.cpp  - OpenGL 3D static scene implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
 #include "./backend/filter.h"
 
 using std::vector;
+using std::string;
 
 
 //
@@ -38,6 +39,7 @@ unsigned int ANIMATE_PROGRESS_NUMFRAMES=3;
 Scene::Scene() : tempCam(0), cameraSet(true), outWinAspect(1.0f)
 {
 	glewInited=false;
+	visControl=0;
 
 	lastHovered=lastSelected=(unsigned int)(-1);
 	lockInteract=false;
@@ -47,7 +49,6 @@ Scene::Scene() : tempCam(0), cameraSet(true), outWinAspect(1.0f)
 	useEffects=false;
 	showAxis=true;
 	attemptedLoadProgressAnim=false;
-	showProgressAnimation=false;
 	witholdCamUpdate=false;
 
 	//default to black
@@ -60,7 +61,6 @@ Scene::Scene() : tempCam(0), cameraSet(true), outWinAspect(1.0f)
 	lightPosition[2]= 1.0;
 	lightPosition[3]=0.0;
 
-
 	DrawableObj::setTexPool(new TexturePool);
 
 }
@@ -90,6 +90,8 @@ unsigned int Scene::initDraw()
 		glewInited=true;
 	}
 #endif
+
+
 	glClearColor( rBack, gBack, bBack,1.0f );
 	glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT);
 
@@ -158,7 +160,17 @@ unsigned int Scene::initDraw()
 			animFiles[ui]=animFilename;
 		}
 
+
 		progressAnimTex.setTexture(animFiles);
+		//Cycle every this many seconds
+		progressAnimTex.setRepeatTime(6.0f);
+		//Ramp opacity for this long (seconds)
+		progressAnimTex.setFadeInTime(2.0f);
+		//Don't show the animation until this many sceonds
+		progressAnimTex.setShowDelayTime(1.5f);
+		
+		progressCircle.setFadeInTime(1.5f);
+		progressCircle.setShowDelayTime(1.0f);
 
 		updateProgressOverlay();
 	}
@@ -206,19 +218,20 @@ void Scene::updateCam(const Camera *camToUse, bool useIdent=true) const
 
 void Scene::updateProgressOverlay()
 {
-	progressAnimTex.setPosition(0.9*winX,0.9*winY);
+	const float xPos= 0.85*winX;
+	const float yPos=0.85*winY;
+
+	progressAnimTex.setPosition(xPos,yPos);
 	progressAnimTex.setSize(0.1*winX);
-	//Cycle every this many seconds
-	progressAnimTex.setRepeatTime(6.0f);
-	//Ramp opacity for this long (seconds)
-	progressAnimTex.setFadeInTime(1.0f);
-	//Don't show the animation until this many sceonds
-	progressAnimTex.setShowDelayTime(1.0f);
+	//Draw the progress animation bar
+	progressCircle.setPosition(xPos,yPos);
+	progressCircle.setSize(0.15*winX);
 }
 
 void Scene::draw(bool noUpdateCam) 
 {
-
+	glError();
+	ASSERT(visControl);
 	glPushMatrix();
 
 	Camera *camToUse;
@@ -228,7 +241,7 @@ void Scene::draw(bool noUpdateCam)
 		camToUse=activeCam;
 
 	//Inform text about current camera, 
-	// so it can eg billboard if needed
+	// so it can e.g. billboard if needed
 	DrawableObj::setCurCamera(camToUse);
 	DrawableObj::setWindowSize(winX,winY);
 	DrawableObj::setBackgroundColour(rBack,gBack,bBack);
@@ -325,6 +338,7 @@ void Scene::draw(bool noUpdateCam)
 		//Draw progress, if needed
 		drawProgressAnim();
 	}
+	glError();
 }
 
 void Scene::drawObjectVector(const vector<const DrawableObj*> &drawObjs, bool &lightsOn, bool drawOpaques) const
@@ -407,7 +421,7 @@ void Scene::drawOverlays(bool noUpdateCam) const
 	//Set the opengl camera state back into modelview mode
 	if(!noUpdateCam)
 	{
-		//clear projection and o modle matricies
+		//clear projection and modelview matrices
 		glMatrixMode(GL_PROJECTION);
 		glPushMatrix();
 		glLoadIdentity();
@@ -467,6 +481,8 @@ void Scene::drawHoverOverlay()
 	glAlphaFunc(GL_GREATER,0.01f);
 	
 	vector<const SelectionBinding *> binder;
+	//!Devices for interactive object properties
+	const std::vector<SelectionDevice *> &selectionDevices = visControl->getSelectionDevices();
 	for(unsigned int uj=0;uj<selectionDevices.size();uj++)
 	{
 		if(selectionDevices[uj]->getAvailBindings(objects[lastHovered],binder))
@@ -592,13 +608,24 @@ void Scene::drawHoverOverlay()
 
 void Scene::drawProgressAnim() const
 {
-	if(!showProgressAnimation)
+	if(!visControl->state.treeState.isRefreshing())
 		return;
 
+	if(useLighting)
+		glDisable(GL_LIGHTING);
+
+	progressCircle.draw();
 	if(!progressAnimTex.isOK())
+	{
+		if(useLighting)
+			glEnable(GL_LIGHTING);
 		return;
+	}
 
 	progressAnimTex.draw();
+
+	if(useLighting)
+		glEnable(GL_LIGHTING);
 }
 
 void Scene::commitTempCam()
@@ -652,7 +679,6 @@ void Scene::clearAll()
 
 	clearObjs();
 	clearRefObjs();
-	clearBindings();	
 }
 
 void Scene::clearObjs()
@@ -663,13 +689,6 @@ void Scene::clearObjs()
 	lastHovered=-1;
 }
 
-void Scene::clearBindings()
-{
-	//Note that the filter is responsible for cleaning the
-	// bindings. So, we just forget about it, without delete-ing
-	selectionDevices.clear();
-}
-
 
 void Scene::clearRefObjs()
 {
@@ -688,13 +707,6 @@ void Scene::setLightPos(const float *f)
 		lightPosition[ui]=f[ui];
 }
 
-void Scene::setShowProgress(bool show)
-{
-	showProgressAnimation=show;
-	progressAnimTex.resetTime();
-}
-
-
 void Scene::setAspect(float newAspect)
 {
 	outWinAspect=newAspect;
@@ -899,19 +911,13 @@ void Scene::finaliseCam()
 	}
 }
 
-void Scene::addSelectionDevices(const vector<SelectionDevice *> &d)
-{
-	for(unsigned int ui=0;ui<d.size();ui++)
-	{
-#ifdef DEBUG
-		//Ensure that pointers coming in are valid, by attempting to perform an operation on them
-		vector<std::pair<const Filter *,SelectionBinding> > tmp;
-		d[ui]->getModifiedBindings(tmp);
-		tmp.clear();
-#endif
-		selectionDevices.push_back(d[ui]);
-	}
-}
+void Scene::resetProgressAnim()
+{ 	
+	progressAnimTex.resetTime(); 
+	progressCircle.resetTime(); 
+	progressCircle.reset();
+};
+
 
 //Values are in the range [0 1].
 void Scene::applyDevice(float startX, float startY, float curX, float curY,
@@ -927,7 +933,7 @@ void Scene::applyDevice(float startX, float startY, float curX, float curY,
 	ASSERT(lastSelected < objects.size())
 	ASSERT(objects[lastSelected]->canSelect);
 
-	//Grab basis vectors. (up, fowards and 
+	//Grab basis vectors. (up, forwards and 
 	//across from camera view.)
 	//---
 	Point3D forwardsDir,upDir;
@@ -978,6 +984,7 @@ void Scene::applyDevice(float startX, float startY, float curX, float curY,
 	SelectionBinding *binder;
 
 	vector<SelectionBinding*> activeBindings;
+	std::vector<SelectionDevice *> &selectionDevices = visControl->getSelectionDevices();
 	for(unsigned int ui=0;ui<selectionDevices.size();ui++)
 	{
 		if(selectionDevices[ui]->getBinding(
@@ -1010,11 +1017,11 @@ void Scene::applyDevice(float startX, float startY, float curX, float curY,
 	//Inform viscontrol about updates, if we have applied any
 	if(activeBindings.size() && permanent)
 	{
-		visControl->setUpdates();
+		visControl->state.treeState.setUpdates();
 		//If the viscontrol is in the middle of an update,
 		//tell it to abort.
-		if(visControl->isRefreshing())
-			visControl->abort();
+		if(visControl->state.treeState.isRefreshing())
+			visControl->state.treeState.setAbort();
 	}
 
 }
@@ -1028,19 +1035,6 @@ void Scene::getEffects(vector<const Effect *> &eff) const
 }	
 
 
-void Scene::getModifiedBindings(std::vector<std::pair<const Filter *, SelectionBinding> > &bindings) const
-{
-	for(unsigned int ui=0;ui<selectionDevices.size();ui++)
-		selectionDevices[ui]->getModifiedBindings(bindings);
-}
-
-void Scene::resetModifiedBindings()  
-{
-	for(unsigned int ui=0;ui<selectionDevices.size();ui++)
-		selectionDevices[ui]->resetModifiedBindings();
-}
-
-
 void Scene::setEffectVec(vector<Effect *> &e)
 {
 	clearEffects();
diff --git a/src/gl/scene.h b/src/gl/scene.h
index 2057216..35fda75 100644
--- a/src/gl/scene.h
+++ b/src/gl/scene.h
@@ -1,6 +1,6 @@
 /*
  * 	scene.h - Opengl interaction header. 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -22,8 +22,6 @@
 class Scene;
 class VisController;
 class Filter;
-class SelectionDevice;
-class SelectionBinding;
 
 #include "drawables.h"
 
@@ -32,6 +30,7 @@ class SelectionBinding;
 
 #include "glDebug.h"
 
+#include <vector>
 
 //!The scene class brings together elements such as objects, lights, and cameras
 //to enable scene rendering
@@ -47,10 +46,6 @@ class Scene
 		//!Objects used for drawing that will not be destroyed
 		std::vector<const DrawableObj * > refObjects;
 
-
-		//!Bindings for interactive object properties
-		std::vector<SelectionDevice *> selectionDevices;
-
 		//!Various OpenGL effects
 		std::vector<const Effect *> effects;
 
@@ -92,10 +87,10 @@ class Scene
 		//Prevent camera updates from being passed to opengl
 		bool witholdCamUpdate;
 
-		//!Last hoeverd object	
+		//!Last hovered object	
 		unsigned int lastHovered;
 
-		//!Should alpha belnding be used?
+		//!Should alpha blending be used?
 		bool useAlpha;
 		//!Should lighting calculations be performed?
 		bool useLighting;
@@ -108,18 +103,16 @@ class Scene
 		//!Background colour
 		float rBack,gBack,bBack;
 
-		//!Should we a progress animation to the user in 3D?
-		bool showProgressAnimation;
-
 		//!Have we attempted to load the progress animation
 		bool attemptedLoadProgressAnim;
 
-		//texture to use for pgoress animation
+		//texture to use for progress animation
 		DrawAnimatedOverlay progressAnimTex;
 
 		//Lighting vector
 		float lightPosition[4];
 
+		
 		///!Draw the hover overlays
 		void drawHoverOverlay();
 
@@ -141,13 +134,15 @@ class Scene
 		Scene &operator=(const Scene &);
 				
 	public:
+		DrawProgressCircleOverlay progressCircle;
+		
 		//!Constructor
 		Scene();
 		//!Destructor
 		virtual ~Scene();
 
 		//!Set the vis control
-		void setViscontrol(VisController *v) { visControl=v;};
+		void setVisControl(VisController *v) { visControl=v;};
 		//!Draw the objects in the active window. May adjust cameras and compute bounding as needed.
 		void draw(bool noUpdateCam=false);
 
@@ -160,18 +155,16 @@ class Scene
 		void clearObjs();
 		//! Clear the reference object vector
 		void clearRefObjs();
-		//!Clear object bindings vector
-		void clearBindings();
 
 		//!Do we have overlay items?
 		bool hasOverlays() const;	
 
 		//!Obtain the scene's light coordinates in camera relative space
-		// requires an array os size 4  (xyzw)
+		// requires an array of size 4  (xyzw)
 		void getLightPos(float *f) const;
 		
 		//!Obtain the scene's light coordinates in camera relative space
-		// requires an array os size 4  (xyzw)
+		// requires an array of size 4  (xyzw)
 		void setLightPos(const float *f);
 
 		//!Set the aspect ratio of the output window. Required.
@@ -194,11 +187,10 @@ class Scene
 		void addRefDrawable(const DrawableObj *);
 	
 
-		bool setProgressAnimation(const vector<string> &animFiles);
+		bool setProgressAnimation(const std::vector<std::string> &animFiles);
 
-		void setShowProgress(bool show) ;
+		void resetProgressAnim() ;
 
-		bool getShowProgress() const { return showProgressAnimation; }
 
 		//!remove a drawable object
 		void removeDrawable(unsigned int);
@@ -251,9 +243,6 @@ class Scene
 		//if nothing, returns -1
 		unsigned int glSelect(bool storeSelection=true);
 
-		//!Add selection devices to the scene.
-		void addSelectionDevices(const std::vector<SelectionDevice *> &d);
-
 		//!Clear the current selection devices 
 		void clearDevices();
 
@@ -265,7 +254,7 @@ class Scene
 
 		// is interaction currently locked?
 		bool isInteractionLocked()  const { return lockInteract;}
-		//!Prevent user interactoin
+		//!Prevent user interaction
 		void lockInteraction(bool amLocking=true) { lockInteract=amLocking;};
 		//!Set selection mode true=select on, false=select off.
 		//All this does internally is modify how drawing works.
@@ -274,12 +263,12 @@ class Scene
 		//!Set the hover mode to control drawing
 		void setHoverMode(bool hMode) { hoverMode=hMode;};
 
-		//!Return the last object over whichthe cursor was hovered	
+		//!Return the last object over which the cursor was hovered	
 		void setLastHover(unsigned int hover) { lastHovered=hover;};
 		//!Get the last selected object from call to glSelect()
 		unsigned int getLastSelected() const { return lastSelected;};
 	
-		//!Return the last object over whichthe cursor was hovered	
+		//!Return the last object over which the cursor was hovered	
 		unsigned int getLastHover() const { return lastHovered;};
 		//!Duplicates the internal camera vector. return value is active camera
 		//in returned vector
@@ -287,12 +276,6 @@ class Scene
 		//!Get a copy of the effects pointers
 		void getEffects(std::vector<const Effect *> &effects) const; 
 
-		//!Return any devices that have been modified since their creation
-		void getModifiedBindings(std::vector<std::pair<const Filter *,SelectionBinding > > &bindings) const;
-
-		//!Reset any modifiecations to bindings back to the unmodified state
-		void resetModifiedBindings();
-
 		//!Set whether to use alpha blending
 		void setAlpha(bool newAlpha) { useAlpha=newAlpha;};
 
@@ -307,7 +290,7 @@ class Scene
 		//!Set window size
 		void setWinSize(unsigned int x, unsigned int y) {winX=x;winY=y; updateProgressOverlay();}
 
-		//!Get the scene boundinng box
+		//!Get the scene bounding box
 		BoundCube getBound() const { return boundCube;}
 
 		//!Set the background colour
@@ -329,7 +312,7 @@ class Scene
 		 * and will be deleted during destruction, clear, or next setEffectVec call
 		 * input vector will be cleared.
 		 */
-		void setEffectVec(vector<Effect *> &e);
+		void setEffectVec(std::vector<Effect *> &e);
 
 		//!Add an effect
 		unsigned int addEffect(Effect *e);
@@ -339,7 +322,7 @@ class Scene
 		//!Clear effects vector
 		void clearEffects();
 
-		static string getGlVersion() { return  string((char *)glGetString(GL_VERSION)); }
+		static std::string getGlVersion() { return  std::string((char *)glGetString(GL_VERSION)); }
 };
 
 #endif
diff --git a/src/gl/select.cpp b/src/gl/select.cpp
index 78e8b1c..339b2ff 100644
--- a/src/gl/select.cpp
+++ b/src/gl/select.cpp
@@ -1,6 +1,6 @@
 /*
  *	select.cpp  - filter selection binding implementation 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gl/select.h b/src/gl/select.h
index b333c1c..abf9d97 100644
--- a/src/gl/select.h
+++ b/src/gl/select.h
@@ -1,6 +1,6 @@
 /*
  * 	select,h - Opengl interaction header. 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gl/textures.cpp b/src/gl/textures.cpp
index 2ce8500..54a1632 100644
--- a/src/gl/textures.cpp
+++ b/src/gl/textures.cpp
@@ -1,6 +1,6 @@
 /*
  *	textures.cpp - texture wrapper class implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -135,7 +135,7 @@ void TexturePool::genTexID(unsigned int &texID, size_t texType)
 {
 	texture tex;
 	tex.data=0;
-  
+ 
 	glGenTextures(1,&tex.glID);
 	texID = tex.glID;
 	
diff --git a/src/gl/textures.h b/src/gl/textures.h
index b89b0db..158c5ab 100644
--- a/src/gl/textures.h
+++ b/src/gl/textures.h
@@ -1,6 +1,6 @@
 /*
  * 	textures.h - Texture control classes header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/art.h b/src/gui/art.h
index 5346792..81c68cf 100644
--- a/src/gui/art.h
+++ b/src/gui/art.h
@@ -1,6 +1,6 @@
 /*
  * art.h  - Program icons header
- * Copyright (C) 2013  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/gui/cropPanel.cpp b/src/gui/cropPanel.cpp
index f322775..f8827fd 100644
--- a/src/gui/cropPanel.cpp
+++ b/src/gui/cropPanel.cpp
@@ -1,6 +1,6 @@
 /*
  *	wxCropPanel.cpp - cropping window  for user interaction
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/cropPanel.h b/src/gui/cropPanel.h
index f19f23d..b3b87e7 100644
--- a/src/gui/cropPanel.h
+++ b/src/gui/cropPanel.h
@@ -1,6 +1,6 @@
 /*
  *	wxCropPanel.cpp - cropping window  for user interaction
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/ExportPos.cpp b/src/gui/dialogs/ExportPos.cpp
index dc21e1f..c1e661c 100644
--- a/src/gui/dialogs/ExportPos.cpp
+++ b/src/gui/dialogs/ExportPos.cpp
@@ -1,6 +1,6 @@
 /*
  *	ExportPos.cpp  - POS file export dialog implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -33,24 +33,11 @@ wxWindow *exportPosYieldWindow=0;
 bool abortOp;
 wxStopWatch *exportPosDelayTime=0;
 
-bool yieldCallback(bool)
-{
-	const unsigned int YIELD_MS=75;
-
-	ASSERT(exportPosDelayTime);
-	//Rate limit the updates
-	if(exportPosDelayTime->Time() > YIELD_MS)
-	{
-		wxSafeYield(exportPosYieldWindow);
-		exportPosDelayTime->Start();
-	}
-
-	return !abortOp;
-}
-
 
 using std::list;
 using std::pair;
+using std::string;
+using std::vector;
 
 enum
 {
@@ -149,11 +136,13 @@ void ExportPosDialog::initialiseData(FilterTree &f)
 	vector<const Filter*> dummyPersist;
 	upWxTreeCtrl(filterTree,treeData,filterMap,dummyPersist,0);	
 
+	vector<SelectionDevice *> devices;
 	ProgressData p;
-	//TODO: Is trashing the devices a problem? do we have to restore them??
-	std::vector<SelectionDevice *> dummyDevices;
 	std::vector<std::pair<const Filter *, string > > consoleStrings;
-	filterTree.refreshFilterTree(outputData,dummyDevices,consoleStrings,p,yieldCallback);
+
+	ATOMIC_BOOL wantAbort;
+	wantAbort=false;
+	filterTree.refreshFilterTree(outputData,devices,consoleStrings,p,wantAbort);
 
 	//Delete all filter items that came out of refresh, other than ion streams
 	filterTree.safeDeleteFilterList(outputData,STREAM_TYPE_IONS,true);
diff --git a/src/gui/dialogs/ExportPos.h b/src/gui/dialogs/ExportPos.h
index 47bb091..d907791 100644
--- a/src/gui/dialogs/ExportPos.h
+++ b/src/gui/dialogs/ExportPos.h
@@ -1,6 +1,6 @@
 /*
  *	ExportPos.h - Point data export dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/ExportRngDialog.cpp b/src/gui/dialogs/ExportRngDialog.cpp
index 800389a..0879a39 100644
--- a/src/gui/dialogs/ExportRngDialog.cpp
+++ b/src/gui/dialogs/ExportRngDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	ExportRngDialog.cpp  - "Range" data export dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/ExportRngDialog.h b/src/gui/dialogs/ExportRngDialog.h
index 13859f4..fb517a7 100644
--- a/src/gui/dialogs/ExportRngDialog.h
+++ b/src/gui/dialogs/ExportRngDialog.h
@@ -1,6 +1,6 @@
 /*
  *	ExportRngDialog.h - Range data export dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/StashDialog.cpp b/src/gui/dialogs/StashDialog.cpp
index 23b5987..7300f25 100644
--- a/src/gui/dialogs/StashDialog.cpp
+++ b/src/gui/dialogs/StashDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	StashDialog.cpp - filter "Stash" tree editing and viewing dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -25,9 +25,12 @@
 
 #include "./backend/viscontrol.h"
 
+#include <stack>
+
 using std::pair;
 using std::string;
 using std::stack;
+using std::vector;
 // begin wxGlade: ::extracode
 
 // end wxGlade
@@ -122,7 +125,7 @@ void StashDialog::OnListKeyDown(wxListEvent &event)
 				if ( item == -1 )
 					break;
 
-				visControl->eraseStash(listStashes->GetItemData(item));
+				visControl->state.eraseStash(listStashes->GetItemData(item));
 			}
 			
 			//Update the filter list
@@ -148,35 +151,31 @@ void StashDialog::OnTreeSelChange(wxTreeEvent &event)
 void StashDialog::updateList()
 {
 	//Generate the stash selection list
-	//
-	vector<pair<string,unsigned int> > stashes;
-
-	visControl->getStashes(stashes);
 
 	//Clear the existing list
 	listStashes->Freeze();
 	listStashes->DeleteAllItems();
-	
+
+	unsigned int nStashes=visControl->state.getStashCount();	
 	//Fill it with "stash" entries
 	//Add columns to report listviews
-	for (unsigned int ui=0; ui<stashes.size(); ui++)
+	for (unsigned int ui=0; ui<nStashes; ui++)
 	{
 		string strTmp;
-		FilterTree t;
+		pair<std::string,FilterTree> stash;
 		long itemIdx;
 		
 		//First item is the stash name
-		itemIdx = listStashes->InsertItem(ui,(stashes[ui].first));
+		itemIdx = listStashes->InsertItem(ui,stash.first);
 
 		//Second column is num filters
-		
-		visControl->getStashTree(stashes[ui].second,t);
-		stream_cast(strTmp,t.size());
+		visControl->state.copyStashedTree(ui,stash);
+		stream_cast(strTmp,stash.second.size());
 		listStashes->SetItem(itemIdx,1,(strTmp));
 
 		//Set the stash ID as the list data item
 		//this is the key to the stash val
-		listStashes->SetItemData(itemIdx,stashes[ui].second);	
+		listStashes->SetItemData(itemIdx,ui);	
 	}
 	listStashes->Thaw();
 }
@@ -205,7 +204,7 @@ void StashDialog::updateGrid()
 	unsigned int filterIdx = ((wxTreeUint *)tData)->value;
 
 	FilterTree t;
-	visControl->getStashTree(stashId,t);
+	visControl->state.copyStashedTree(stashId,t);
 
 	Filter *targetFilter=0;
 	unsigned int pos=0;
@@ -267,7 +266,7 @@ void StashDialog::updateTree()
 	if(!getStashIdFromList(stashId))
 		return;
 
-	visControl->getStashTree(stashId,curTree);
+	visControl->state.copyStashedTree(stashId,curTree);
 
 
 	uniqueIds.clear();
@@ -339,7 +338,7 @@ void StashDialog::OnBtnRemove(wxCommandEvent &event)
 		if ( item == -1 )
 			break;
 
-		visControl->eraseStash(listStashes->GetItemData(item));
+		visControl->state.eraseStash(listStashes->GetItemData(item));
 		updateList();
 		updateTree();
 		updateGrid();
diff --git a/src/gui/dialogs/StashDialog.h b/src/gui/dialogs/StashDialog.h
index fda4183..0eda6e0 100644
--- a/src/gui/dialogs/StashDialog.h
+++ b/src/gui/dialogs/StashDialog.h
@@ -1,6 +1,6 @@
 /*
  * 	stashdialog.h - "Stash" filter storage edit dialog header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateFilterDialog.cpp b/src/gui/dialogs/animateFilterDialog.cpp
index c98b803..7bc70b1 100644
--- a/src/gui/dialogs/animateFilterDialog.cpp
+++ b/src/gui/dialogs/animateFilterDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	animateFilterDialog.cpp - Framewise animation of filter properties
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -97,6 +97,10 @@ const char * comboRange_choices[RANGE_FORMAT_NUM_OPTIONS] =
 using std::string;
 using std::cout;
 using std::endl;
+using std::pair;
+using std::vector;
+using std::set;
+using std::map;
 
 
 template<class T>
@@ -385,7 +389,7 @@ void ExportAnimationDialog::setAnimationState(const PropertyAnimator &prop,
 
 	//Sync this into the dialog 
 	// -> we need to examine the animation state, and only
-	// keeo paths we recognise, rewriting the id values
+	// keep paths we recognise, rewriting the id values
 	propertyAnimator.updateMappings(idRemap);
 
 }
@@ -649,7 +653,7 @@ void ExportAnimationDialog::OnFilterGridCellSelected(wxPropertyGridEvent &event)
 	wxTreeItemData *tData=filterTreeCtrl->GetItemData(tId);
 	filterId = ((wxTreeUint *)tData)->value;
 
-	//grab tyhe key from the property grid
+	//grab the key from the property grid
 	size_t key;
 	std::string keyStr;
 	keyStr=event.GetProperty()->GetName();
diff --git a/src/gui/dialogs/animateFilterDialog.h b/src/gui/dialogs/animateFilterDialog.h
index 1f4cdac..541f975 100644
--- a/src/gui/dialogs/animateFilterDialog.h
+++ b/src/gui/dialogs/animateFilterDialog.h
@@ -1,6 +1,6 @@
 /*
  *	animateFilterDialog - GUI for animate filter
- *	Copyright (C) 2013, D. Haley, A Ceguerra 
+ *	Copyright (C) 2015, D. Haley, A Ceguerra 
  
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -102,24 +102,24 @@ public:
     //! Obtain the current state from the animation (keyframes)
     // the second element provides the mappings for the property animator to 
     // filter tree path locations
-    void getAnimationState(PropertyAnimator &prop,  vector<pair<string,size_t> > &pathMapping) const ;
+    void getAnimationState(PropertyAnimator &prop, std::vector<std::pair<std::string,size_t> > &pathMapping) const ;
     //!Obtain the current state from the animation
     void setAnimationState(const PropertyAnimator &prop,
-		    const vector<pair<string,size_t> > &pathMapping);
+		    const std::vector<std::pair<std::string,size_t> > &pathMapping);
 
     //!Obtain the filter tree path string->animation ID mapping
-    void getPathMapping(vector<pair<string,size_t> > &mapping, bool allowMissing=false) const;
+    void getPathMapping(std::vector<std::pair<std::string,size_t> > &mapping, bool allowMissing=false) const;
 
     void setDefImSize(unsigned int w, unsigned int h) ; 
 private:
     //!Tree of filters that can be manipulated
     const FilterTree *filterTree;
     //!Mapping from ID of filter to the pointer in the filter tree
-    map<size_t,Filter *> filterMap;
+    std::map<size_t,Filter *> filterMap;
 
     //!Mapping to allow for converting entry of RNG selection combo into
     // the correct range enum value
-    map<string,size_t> rangeMap;
+    std::map<std::string,size_t> rangeMap;
     //!Default height/width desired for output images
     size_t imageWidth,imageHeight;
     bool imageSizeOK;
@@ -127,9 +127,9 @@ private:
     PropertyAnimator propertyAnimator;
 
     //!Working directory for outputting data
-    string workDir;
+    std::string workDir;
 
-    string imagePrefix;
+    std::string imagePrefix;
     //!True if any con
     bool existsConflicts;
     //!true if the user has selected image output functionality
diff --git a/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.cpp b/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.cpp
index 2f16b25..8b3df35 100644
--- a/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.cpp
+++ b/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	choiceKeyFrameDialog.cpp - Keyframe selection for optioned properties
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.h b/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.h
index f48da3f..89527b9 100644
--- a/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.h
+++ b/src/gui/dialogs/animateSubDialogs/choiceKeyFrameDialog.h
@@ -1,6 +1,6 @@
 /*
  *	choiceKeyFrameDialog.h - Keyframe selection for optioned properties
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp b/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp
index 25a0b9c..109b51f 100644
--- a/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp
+++ b/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	colourKeyFrameDialog.cpp - Colour property keyframe selection dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.h b/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.h
index 2533fb8..e55bc0b 100644
--- a/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.h
+++ b/src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.h
@@ -1,6 +1,6 @@
 /*
  *	colourKeyFrameDialog.h -Colour property keyframe selection dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateSubDialogs/realKeyFrameDialog.h b/src/gui/dialogs/animateSubDialogs/realKeyFrameDialog.h
index e79ef1e..40cfa64 100644
--- a/src/gui/dialogs/animateSubDialogs/realKeyFrameDialog.h
+++ b/src/gui/dialogs/animateSubDialogs/realKeyFrameDialog.h
@@ -1,6 +1,6 @@
 /*
  *	realKeyFrameDialog.h - Real value keyframe selection dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp b/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp
index 559288c..47fd4b2 100644
--- a/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp
+++ b/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	stringKeyFrameDialog.cpp - String value keyframe selection dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.h b/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.h
index cf27da9..30cd850 100644
--- a/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.h
+++ b/src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.h
@@ -1,6 +1,6 @@
 /*
  *	stringKeyFrameDialog.h -String value keyframe selection dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/autosaveDialog.cpp b/src/gui/dialogs/autosaveDialog.cpp
index 0ed075c..6e9dd36 100644
--- a/src/gui/dialogs/autosaveDialog.cpp
+++ b/src/gui/dialogs/autosaveDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	autosaveDialog.cpp - Selection of autosaves for hard program restarts
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/autosaveDialog.h b/src/gui/dialogs/autosaveDialog.h
index ad0e7ce..4e45a82 100644
--- a/src/gui/dialogs/autosaveDialog.h
+++ b/src/gui/dialogs/autosaveDialog.h
@@ -1,6 +1,6 @@
 /*
  *	autosaveDialog.h - Selection dialog on recovery from autosave files
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/filterErrorDialog.cpp b/src/gui/dialogs/filterErrorDialog.cpp
index 0b8b192..9452bb7 100644
--- a/src/gui/dialogs/filterErrorDialog.cpp
+++ b/src/gui/dialogs/filterErrorDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	filterErrorDialog.cpp - Dialog for displaying error notices computed from filter tree
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/filterErrorDialog.h b/src/gui/dialogs/filterErrorDialog.h
index 9737f6d..02fe6d5 100644
--- a/src/gui/dialogs/filterErrorDialog.h
+++ b/src/gui/dialogs/filterErrorDialog.h
@@ -1,6 +1,6 @@
 /*
  *	filterErrorDialog.h - Dialog for displaying error notices computed from filter tree
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/prefDialog.cpp b/src/gui/dialogs/prefDialog.cpp
index e482e79..93dde90 100644
--- a/src/gui/dialogs/prefDialog.cpp
+++ b/src/gui/dialogs/prefDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	prefDialog.cpp - mathgl plot wrapper class
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/prefDialog.h b/src/gui/dialogs/prefDialog.h
index 6991786..7aaef11 100644
--- a/src/gui/dialogs/prefDialog.h
+++ b/src/gui/dialogs/prefDialog.h
@@ -1,7 +1,7 @@
 // -*- C++ -*- generated by wxGlade HG on Fri Dec  3 22:26:29 2010
 /*
  * prefDialog.h  - program preferences management dialog
- * Copyright (C) 2011  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/rangeEditDialog.cpp b/src/gui/dialogs/rangeEditDialog.cpp
index f865e91..06aa414 100644
--- a/src/gui/dialogs/rangeEditDialog.cpp
+++ b/src/gui/dialogs/rangeEditDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	rangeEditDialog.h - Point data export dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -29,7 +29,10 @@
 #include <set>
 
 using std::pair;
-using std::endl;
+using std::vector;
+using std::map;
+using std::set;
+using std::string;
 
 // begin wxGlade: ::extracode
 enum
@@ -419,13 +422,11 @@ void RangeEditorDialog::setCurrentRange(size_t forceSelected)
 			currentRange=0;
 			return;
 		}
-		cerr << "Selected item not wxNOT_FOUND" << endl;
 
 		curPlotID=listToPlotIDs[selectedItem];
 	}
 	else
 	{
-		cerr << "Not force selected" << endl;
 		curPlotID=listToPlotIDs[forceSelected];
 	}
 	
@@ -511,7 +512,7 @@ void RangeEditorDialog::generateListEntries()
 			continue;	
 
 		//Append the plot to the list in the user interface,
-		// with the plot Id embeded in the element
+		// with the plot Id embedded in the element
 		int idx;
 		idx=listPlots->Append((title));
 		listToPlotIDs[idx]=plotIDs[ui];
@@ -1103,7 +1104,7 @@ void RangeEditorDialog::OnGridRangesCellChange(wxGridEvent &event)
 				return;
 			}
 			
-			//Disallow inversio of range start/end
+			//Disallow inversion of range start/end
 			if(f >=currentRange->getRange(rangeId).second)
 			{
 				event.Veto();
@@ -1124,7 +1125,7 @@ void RangeEditorDialog::OnGridRangesCellChange(wxGridEvent &event)
 				return;
 			}
 			
-			//Disallow inversoin of range start/end
+			//Disallow inversion of range start/end
 			if(f <=currentRange->getRange(rangeId).first)
 			{
 				event.Veto();
diff --git a/src/gui/dialogs/rangeEditDialog.h b/src/gui/dialogs/rangeEditDialog.h
index 94c30ea..e6f0ddb 100644
--- a/src/gui/dialogs/rangeEditDialog.h
+++ b/src/gui/dialogs/rangeEditDialog.h
@@ -164,7 +164,7 @@ private:
     void generatePlotRegions();
 
     //Generate the list of overlays
-    void generateOverlayList(const vector<OVERLAY_DATA> &overlays) ;
+    void generateOverlayList(const std::vector<OVERLAY_DATA> &overlays) ;
 
     //Rebuild the range entry grid
     void generateRangeEntries(size_t rowVisibleHint=(size_t)-1);
diff --git a/src/gui/dialogs/resolutionDialog.cpp b/src/gui/dialogs/resolutionDialog.cpp
index da66edd..db10b1c 100644
--- a/src/gui/dialogs/resolutionDialog.cpp
+++ b/src/gui/dialogs/resolutionDialog.cpp
@@ -1,6 +1,6 @@
 /*
  *	resolutionDialog.cpp - Resolution chooser dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/dialogs/resolutionDialog.h b/src/gui/dialogs/resolutionDialog.h
index 2a6c8f4..96bf988 100644
--- a/src/gui/dialogs/resolutionDialog.h
+++ b/src/gui/dialogs/resolutionDialog.h
@@ -1,6 +1,6 @@
 /*
  *	resolutionDialog.h - Resolution chooser dialog
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/gui/glPane.cpp b/src/gui/glPane.cpp
index 8f66c3f..30b739a 100644
--- a/src/gui/glPane.cpp
+++ b/src/gui/glPane.cpp
@@ -1,6 +1,6 @@
 /*
  *	glPane.cpp - OpenGL panel implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@
     #define GL_BGR 0x80E0
 #endif
 
+using std::string;
 
 enum
 {
@@ -117,13 +118,13 @@ bool BasicGLPane::displaySupported() const
 
 void BasicGLPane::setSceneInteractionAllowed(bool enabled)
 {
-	currentScene.lockInteraction(!enabled);
+	currentScene->lockInteraction(!enabled);
 }
 
 unsigned int  BasicGLPane::selectionTest(const wxPoint &p,bool &shouldRedraw)
 {
 
-	if(currentScene.isInteractionLocked())
+	if(currentScene->isInteractionLocked())
 	{
 		shouldRedraw=false;
 		return -1; 
@@ -144,8 +145,8 @@ unsigned int  BasicGLPane::selectionTest(const wxPoint &p,bool &shouldRedraw)
 	gluPickMatrix(p.x, oldViewport[3]-p.y,5, 5, oldViewport);
 	glMatrixMode(GL_MODELVIEW);
 
-	int lastSelected = currentScene.getLastSelected();
-	int selectedObject=currentScene.glSelect();
+	int lastSelected = currentScene->getLastSelected();
+	int selectedObject=currentScene->glSelect();
 
 	//If the object selection hasn't changed, we don't need to redraw
 	//if it has changed, we should redraw
@@ -165,7 +166,7 @@ unsigned int  BasicGLPane::selectionTest(const wxPoint &p,bool &shouldRedraw)
 unsigned int  BasicGLPane::hoverTest(const wxPoint &p,bool &shouldRedraw)
 {
 
-	if(currentScene.isInteractionLocked())
+	if(currentScene->isInteractionLocked())
 	{
 		shouldRedraw=false;
 		return -1;
@@ -182,15 +183,15 @@ unsigned int  BasicGLPane::hoverTest(const wxPoint &p,bool &shouldRedraw)
 	gluPickMatrix(p.x, oldViewport[3]-p.y,5, 5, oldViewport);
 	glMatrixMode(GL_MODELVIEW);
 
-	unsigned int lastHover = currentScene.getLastHover();
-	unsigned int hoverObject=currentScene.glSelect(false);
+	unsigned int lastHover = currentScene->getLastHover();
+	unsigned int hoverObject=currentScene->glSelect(false);
 
 	//FIXME: Should be able to make this more efficient	
 	shouldRedraw =  lastHover!=(unsigned int)-1;
 
 	//Set the scene's hover value
-	currentScene.setLastHover(hoverObject);
-	currentScene.setHoverMode(hoverObject != (unsigned int)-1);
+	currentScene->setLastHover(hoverObject);
+	currentScene->setHoverMode(hoverObject != (unsigned int)-1);
 
 	//Restore the previous matirx
 	glPopMatrix();
@@ -226,7 +227,7 @@ void BasicGLPane::mouseMoved(wxMouseEvent& event)
 
 	if(selectionMode )
 	{
-		if(currentScene.isInteractionLocked())
+		if(currentScene->isInteractionLocked())
 		{
 			event.Skip();
 			return;
@@ -263,7 +264,7 @@ void BasicGLPane::mouseMoved(wxMouseEvent& event)
 		GetClientSize(&w, &h);
 
 
-		currentScene.applyDevice((float)draggingStart.x/(float)w,
+		currentScene->applyDevice((float)draggingStart.x/(float)w,
 					(float)draggingStart.y/(float)h,
 					 p.x/(float)w,p.y/(float)h,
 					 keyFlags,mouseFlags,
@@ -300,8 +301,8 @@ void BasicGLPane::mouseMoved(wxMouseEvent& event)
 	{
 		//Commit the current temp cam using the last camera rate
 		//and then restart the motion.
-		if(!lastMoveShiftDown && currentScene.haveTempCam())
-			currentScene.commitTempCam();
+		if(!lastMoveShiftDown && currentScene->haveTempCam())
+			currentScene->commitTempCam();
 
 		camMultRate*=5.0f;
 
@@ -312,8 +313,8 @@ void BasicGLPane::mouseMoved(wxMouseEvent& event)
 	{
 		//Commit the current temp cam using the last camera rate
 		//and then restart the motion.
- 		if(lastMoveShiftDown && currentScene.haveTempCam())
-			currentScene.commitTempCam();
+ 		if(lastMoveShiftDown && currentScene->haveTempCam())
+			currentScene->commitTempCam();
 
 		lastMoveShiftDown=false;
 	}
@@ -348,22 +349,22 @@ void BasicGLPane::mouseMoved(wxMouseEvent& event)
 	switch(camMode)
 	{
 		case CAM_TRANSLATE:
-			currentScene.discardTempCam();
-			currentScene.setTempCam();
-			currentScene.getTempCam()->translate(lrMove,-udMove);
+			currentScene->discardTempCam();
+			currentScene->setTempCam();
+			currentScene->getTempCam()->translate(lrMove,-udMove);
 			break;
 		case CAM_PIVOT:
-			currentScene.discardTempCam();
-			currentScene.setTempCam();
-			currentScene.getTempCam()->pivot(lrMove,udMove);
+			currentScene->discardTempCam();
+			currentScene->setTempCam();
+			currentScene->getTempCam()->pivot(lrMove,udMove);
 			break;
 		case CAM_MOVE:
-			currentScene.setTempCam();
-			currentScene.getTempCam()->move(lrMove,udMove);
+			currentScene->setTempCam();
+			currentScene->getTempCam()->move(lrMove,udMove);
 			break;
 		case CAM_ROLL:
-			currentScene.setTempCam();
-			currentScene.getTempCam()->roll(atan2(udMove,lrMove));
+			currentScene->setTempCam();
+			currentScene->getTempCam()->roll(atan2(udMove,lrMove));
 						
 			break;	
 		default:
@@ -374,7 +375,7 @@ void BasicGLPane::mouseMoved(wxMouseEvent& event)
 	if(!event.m_leftDown)
 	{
 		dragging=false;
-		currentScene.commitTempCam();
+		currentScene->commitTempCam();
 	}
 	
 	haveCameraUpdates=true;
@@ -392,7 +393,7 @@ void BasicGLPane::mouseDown(wxMouseEvent& event)
 	//a temp cam is activated in the scene, or a binding refresh is underway,
 	//which is currently considered bad
 	if(!dragging && !applyingDevice && !selectionMode 
-			&& !currentScene.isInteractionLocked())
+			&& !currentScene->isInteractionLocked())
 	{
 		//Check to see if the user has clicked an object in the scene
 		bool redraw;
@@ -400,11 +401,11 @@ void BasicGLPane::mouseDown(wxMouseEvent& event)
 
 
 		//If the selected object is valid, then
-		//we did select an object. Treat this as a seletion event
-		if(currentScene.getLastSelected() != (unsigned int)-1)
+		//we did select an object. Treat this as a selection event
+		if(currentScene->getLastSelected() != (unsigned int)-1)
 		{
 			selectionMode=true;
-			currentScene.setSelectionMode(true);
+			currentScene->setSelectionMode(true);
 		}
 		else
 		{
@@ -445,11 +446,11 @@ void BasicGLPane::mouseWheelMoved(wxMouseEvent& event)
 
 	cameraMoveRate*=CAMERA_SCROLL_RATE;
 	//Move by specified delta
-	currentScene.getActiveCam()->forwardsDolly(cameraMoveRate);
+	currentScene->getActiveCam()->forwardsDolly(cameraMoveRate);
 
 	//if we are using a temporary camera, update that too
-	if(currentScene.haveTempCam())
-		currentScene.getTempCam()->forwardsDolly(cameraMoveRate);
+	if(currentScene->haveTempCam())
+		currentScene->getTempCam()->forwardsDolly(cameraMoveRate);
 
 	haveCameraUpdates=true;
 	Refresh();
@@ -457,7 +458,7 @@ void BasicGLPane::mouseWheelMoved(wxMouseEvent& event)
 
 void BasicGLPane::mouseReleased(wxMouseEvent& event) 
 {
-	if(currentScene.isInteractionLocked())
+	if(currentScene->isInteractionLocked())
 	{
 		event.Skip();
 		return;
@@ -476,7 +477,7 @@ void BasicGLPane::mouseReleased(wxMouseEvent& event)
 			applyingDevice=true;
 
 
-			currentScene.applyDevice((float)draggingStart.x/(float)w,
+			currentScene->applyDevice((float)draggingStart.x/(float)w,
 						(float)draggingStart.y/(float)h,
 						 p.x/(float)w,p.y/(float)h,
 						 lastKeyFlags,lastMouseFlags,
@@ -486,7 +487,7 @@ void BasicGLPane::mouseReleased(wxMouseEvent& event)
 
 
 			selectionMode=false;
-			currentScene.setSelectionMode(selectionMode);
+			currentScene->setSelectionMode(selectionMode);
 
 			Refresh();
 		}
@@ -495,9 +496,9 @@ void BasicGLPane::mouseReleased(wxMouseEvent& event)
 	}
 	
 
-	if(currentScene.haveTempCam())
-		currentScene.commitTempCam();
-	currentScene.finaliseCam();
+	if(currentScene->haveTempCam())
+		currentScene->commitTempCam();
+	currentScene->finaliseCam();
 
 	haveCameraUpdates=true;
 	dragging=false;
@@ -520,14 +521,14 @@ void BasicGLPane::mouseLeftWindow(wxMouseEvent& event)
 		GetClientSize(&w, &h);
 
 		applyingDevice=true;
-		currentScene.applyDevice((float)draggingStart.x/(float)w,
+		currentScene->applyDevice((float)draggingStart.x/(float)w,
 					(float)draggingStart.y/(float)h,
 					 p.x/(float)w,p.y/(float)h,
 					 lastKeyFlags,lastMouseFlags,
 					 true);
 
 		selectionMode=false;
-		currentScene.setSelectionMode(selectionMode);
+		currentScene->setSelectionMode(selectionMode);
 		Refresh();
 		applyingDevice=false;
 
@@ -538,9 +539,9 @@ void BasicGLPane::mouseLeftWindow(wxMouseEvent& event)
 
 	if(event.m_leftDown)
 	{
-		if(currentScene.haveTempCam())
+		if(currentScene->haveTempCam())
 		{
-			currentScene.commitTempCam();
+			currentScene->commitTempCam();
 			dragging=false;
 		}
 	}
@@ -623,7 +624,7 @@ void BasicGLPane::keyPressed(wxKeyEvent& event)
 				}
 
 				
-				currentScene.ensureVisible(visibleDir);
+				currentScene->ensureVisible(visibleDir);
 				parentStatusBar->SetStatusText(TRANS("Use shift/ctrl-space or double tap to alter reset axis"));
 				parentStatusBar->SetBackgroundColour(*wxCYAN)
 					;
@@ -644,7 +645,7 @@ void BasicGLPane::setGlClearColour(float r, float g, float b)
 	ASSERT(g >= 0.0f && g <= 1.0f);
 	ASSERT(b >= 0.0f && b <= 1.0f);
 	
-	currentScene.setBackgroundColour(r,g,b);
+	currentScene->setBackgroundColour(r,g,b);
 	
 	Refresh();
 }
@@ -665,9 +666,9 @@ void BasicGLPane::keyReleased(wxKeyEvent& event)
 		case WXK_SUBTRACT:
 		{
 			//Do a backwards dolly by fixed amount
-			currentScene.getActiveCam()->forwardsDolly(cameraMoveRate);
-			if(currentScene.haveTempCam())
-				currentScene.getTempCam()->forwardsDolly(cameraMoveRate);
+			currentScene->getActiveCam()->forwardsDolly(cameraMoveRate);
+			if(currentScene->haveTempCam())
+				currentScene->getTempCam()->forwardsDolly(cameraMoveRate);
 			break;
 		}
 		case '+':
@@ -680,9 +681,9 @@ void BasicGLPane::keyReleased(wxKeyEvent& event)
 			cameraMoveRate= -cameraMoveRate;
 			
 			//Do a forwards dolly by fixed amount
-			currentScene.getActiveCam()->forwardsDolly(cameraMoveRate);
-			if(currentScene.haveTempCam())
-				currentScene.getTempCam()->forwardsDolly(cameraMoveRate);
+			currentScene->getActiveCam()->forwardsDolly(cameraMoveRate);
+			if(currentScene->haveTempCam())
+				currentScene->getTempCam()->forwardsDolly(cameraMoveRate);
 			break;
 		}
 		default:
@@ -713,8 +714,8 @@ bool BasicGLPane::prepare3DViewport(int tlx, int tly, int brx, int bry)
 	GLint dims[2]; 
 	glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims); 
 
-	//Ensure that the opengGL function didn't tell us porkies
-	//but double check for the non-debug bulds next line
+	//Ensure that the openGL function didn't tell us porkies
+	//but double check for the non-debug builds next line
 	ASSERT(dims[0] && dims[1]);
 
 	//check for exceeding max viewport and we have some space
@@ -725,8 +726,8 @@ bool BasicGLPane::prepare3DViewport(int tlx, int tly, int brx, int bry)
 	glViewport( tlx, tly, brx-tlx, bry-tly);
 
 	float aspect = (float)(brx-tlx)/(float)(bry-tly);
-	currentScene.setWinSize(brx-tlx,bry-tly);
-	currentScene.setAspect(aspect);
+	currentScene->setWinSize(brx-tlx,bry-tly);
+	currentScene->setAspect(aspect);
 
 	//Set modelview and projection matrices to the identity
 	// matrix
@@ -772,7 +773,7 @@ void BasicGLPane::render( wxPaintEvent& evt )
 	}
 
 	wxPaintDC(this); 
-	currentScene.draw();
+	currentScene->draw();
 	glFlush();
 	SwapBuffers();
 }
@@ -785,7 +786,7 @@ void BasicGLPane::OnEraseBackground(wxEraseEvent &evt)
 void BasicGLPane::updateClearColour()
 {
 	float rClear,gClear,bClear;
-	currentScene.getBackgroundColour(rClear,gClear,bClear);
+	currentScene->getBackgroundColour(rClear,gClear,bClear);
 	//Can't set the opengl window without a proper context
 	ASSERT(paneInitialised);
 	setGlClearColour(rClear,gClear,bClear);
@@ -838,7 +839,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 
 
 	glLoadIdentity();
-	const Camera *cm = currentScene.getActiveCam();
+	const Camera *cm = currentScene->getActiveCam();
 
 	//We cannot seem to draw outside the current viewport.
 	//in a cross platform manner.
@@ -847,20 +848,20 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 	//Initialise tile data
 	TRcontext *tr; 
 	//Inform the tiling system about our camera config
-	float farPlane; 
-	float aspect=currentScene.getAspect();
+	float aspect=currentScene->getAspect();
 	
 	{
+	float farPlane; 
 	tr=generateTileContext(width,height, imageBuffer);
-	BoundCube bc = currentScene.getBound();
+	BoundCube bc = currentScene->getBound();
 	farPlane = 1.5*bc.getMaxDistanceToBox(cm->getOrigin());
 	
 	if(cm->getProjectionMode() == PROJECTION_MODE_PERSPECTIVE)
 	{
 		if(cm->type() == CAM_LOOKAT)
 		{
-			const CameraLookAt *cl =(const CameraLookAt*) currentScene.getActiveCam();
-			trPerspective(tr,cl->getFOV()/2.0,currentScene.getAspect(),
+			const CameraLookAt *cl =(const CameraLookAt*) currentScene->getActiveCam();
+			trPerspective(tr,cl->getFOV()/2.0,currentScene->getAspect(),
 							cl->getNearPlane(),farPlane);
 		}
 		else
@@ -885,7 +886,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 	unsigned int nRow,nCol,nPass;
 	nRow=trGet(tr,TR_ROWS);
 	nCol=trGet(tr,TR_COLUMNS);
-	if(currentScene.hasOverlays())
+	if(currentScene->hasOverlays())
 		nPass = 2;
 	else
 		nPass=1;
@@ -916,19 +917,19 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 	//--------------	
 
 	//HACK: Flip the all but scene's light z coordinate
-	// for some reason, the frustrum has an inversion
-	// somwhere in the coordinate system, and I can't find it!
-	// inverting the tile frustrum ends up with the depth test 
+	// for some reason, the frustum has an inversion
+	// somewhere in the coordinate system, and I can't find it!
+	// inverting the tile frustum ends up with the depth test 
 	// also inverting.
 	const bool FLIP_LIGHT_HACK=true;
 	//x,y,z and w axis.
 	const bool IMPORTANT_AXIS[4]={true,false,true,false};
 
-	//opengl lighthas 4 
+	//opengl light has 4 
 	float oldLightPos[4];
 	if(FLIP_LIGHT_HACK)
 	{
-		currentScene.getLightPos(oldLightPos);
+		currentScene->getLightPos(oldLightPos);
 		float newLightPos[4];
 		for(size_t ui=0;ui<4;ui++)
 		{
@@ -938,7 +939,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 				newLightPos[ui]=-oldLightPos[ui];
 		
 		}
-		currentScene.setLightPos(newLightPos);
+		currentScene->setLightPos(newLightPos);
 	}
 	
 	if(showProgress)
@@ -964,7 +965,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 	
 		//Start the tile
 		trBeginTile(tr);
-		currentScene.draw(true);
+		currentScene->draw(true);
 
 		glPopMatrix();
 
@@ -980,7 +981,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 	if(FLIP_LIGHT_HACK)
 	{
 		//re-set light coordinates
-		currentScene.setLightPos(oldLightPos);
+		currentScene->setLightPos(oldLightPos);
 	}
 
 	trDelete(tr);
@@ -995,7 +996,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 	//PASS 2
 	//--------------	
 
-	if(currentScene.hasOverlays())
+	if(currentScene->hasOverlays())
 	{
 		//alllocate RGBA (4-channel) image
 		imageBuffer= (unsigned char*) malloc(4*(width)*height);
@@ -1012,7 +1013,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 
 	
 		float rClear,gClear,bClear;
-		currentScene.getBackgroundColour(rClear,gClear,bClear);
+		currentScene->getBackgroundColour(rClear,gClear,bClear);
 		glClearColor( rClear, gClear, 
 					bClear,0.0f);
 
@@ -1024,7 +1025,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 			//Start the tile
 			trBeginTile(tr);
 			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
-			currentScene.drawOverlays(true);
+			currentScene->drawOverlays(true);
 			//ending the tile copies
 			// data 
 			haveMoreTiles=trEndTile(tr);
@@ -1043,7 +1044,7 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 		//FIXME: HACK - using "blue screen" effect
 		//don't use background as mask colour.
 		// use depth buffer or gl alpha
-		unsigned char mask[3] = {(unsigned char)rClear*255.0f,
+		const unsigned char mask[3] = {(unsigned char)rClear*255.0f,
 				(unsigned char)gClear*255.0f,(unsigned char)bClear*255.0f};
 		copyRGBAtoWXImage(width,height,imageBuffer,imageOverlay,mask);
 
@@ -1051,6 +1052,9 @@ bool BasicGLPane::saveImage(unsigned int width, unsigned int height,
 
 		combineWxImage(*image,imageOverlay);
 	}
+
+	//Free the tile buffer
+	trDelete(tr);
 	
 	//--------------	
 	bool isOK=image->SaveFile(filename,wxBITMAP_TYPE_PNG);
@@ -1082,7 +1086,7 @@ bool BasicGLPane::saveImageSequence(unsigned int resX, unsigned int resY, unsign
 	//
 
 
-	ASSERT(!currentScene.haveTempCam());
+	ASSERT(!currentScene->haveTempCam());
 	std::string outFile;
 	wxProgressDialog *wxD = new wxProgressDialog(TRANS("Animation progress"), 
 					TRANS("Rendering sequence..."), nFrames,this,wxPD_CAN_ABORT|wxPD_APP_MODAL );
@@ -1091,7 +1095,7 @@ bool BasicGLPane::saveImageSequence(unsigned int resX, unsigned int resY, unsign
 	std::string tmpStr,tmpStrTwo;
 	stream_cast(tmpStrTwo,nFrames);
 
-	Camera *origCam=currentScene.getActiveCam()->clone();
+	Camera *origCam=currentScene->getActiveCam()->clone();
 	
 	
 	for(unsigned int ui=0;ui<nFrames;ui++)
@@ -1109,14 +1113,14 @@ bool BasicGLPane::saveImageSequence(unsigned int resX, unsigned int resY, unsign
 		Camera *modifiedCam;
 		modifiedCam=origCam->clone();
 		modifiedCam->move(angle,0);
-		currentScene.setActiveCam(modifiedCam);
+		currentScene->setActiveCam(modifiedCam);
 
 		//Save the result
 		outFile = string(stlStr(path))+ string("/") + 
 				string(stlStr(prefix))+digitStr+ string(".") + string(stlStr(ext));
 		if(!saveImage(resX,resY,outFile.c_str(),false, false))
 		{
-			currentScene.setActiveCam(origCam);
+			currentScene->setActiveCam(origCam);
 			return false;
 		}
 
@@ -1130,10 +1134,10 @@ bool BasicGLPane::saveImageSequence(unsigned int resX, unsigned int resY, unsign
 		Refresh();
 	}
 
-	currentScene.setActiveCam(origCam);
+	currentScene->setActiveCam(origCam);
 
 	//Discard the current temp. cam to return the scene back to normal
-	currentScene.discardTempCam();
+	currentScene->discardTempCam();
 	wxD->Destroy();
 	
 	wxPaintEvent event;
diff --git a/src/gui/glPane.h b/src/gui/glPane.h
index aca3737..015ccf0 100644
--- a/src/gui/glPane.h
+++ b/src/gui/glPane.h
@@ -1,6 +1,6 @@
 /*
  *	gLPane.h - WxWidgets opengl Pane. 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -32,6 +32,8 @@ private:
 
 	wxGLContext *context;
 
+	Scene *currentScene;
+
 	wxStatusBar *parentStatusBar;
 	wxTimer *parentStatusTimer;
 	unsigned int statusDelay;	
@@ -84,12 +86,11 @@ private:
 public:
 	bool displaySupported() const;
 
+	void setScene(Scene *s) { currentScene=s;}
+
 	//Enable/Disable the scene interaction for user objects?
 	void setSceneInteractionAllowed(bool enabled=true);
 
-	//!The scene object, holds all info about 3D drawable components
-	Scene currentScene;
-
 	//!Must be called before user has a chance to perform interaction
 	void setParentStatus(wxStatusBar *statusBar,
 			wxTimer *timer,unsigned int statDelay) 
@@ -134,7 +135,7 @@ public:
 			wxString &path, wxString &prefix, wxString &extension);
 
 	//!Get the background colour
-	void getGlClearColour(float &r,float &g,float &b) { currentScene.getBackgroundColour(r,g,b);}
+	void getGlClearColour(float &r,float &g,float &b) { currentScene->getBackgroundColour(r,g,b);}
 	// events
 	void mouseMoved(wxMouseEvent& event);
 	void mouseDown(wxMouseEvent& event);
diff --git a/src/gui/mainFrame.cpp b/src/gui/mainFrame.cpp
index ea4aab7..25b6858 100644
--- a/src/gui/mainFrame.cpp
+++ b/src/gui/mainFrame.cpp
@@ -1,6 +1,6 @@
 /*
  *	mainFrame.cpp - Main 3Depict window
- *	Copyright (C) 2014, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -49,6 +49,9 @@ enum
 #include <wx/tipdlg.h>
 
 #include <wx/utils.h>  // Needed for wxLaunchDefaultApplication
+//extra imports
+#undef I // Not sure who is defining this, but it is causing problems - mathgl?
+#include "common/voxels.h"
 
 //Custom program dialog windows
 #include "gui/dialogs/StashDialog.h" //Stash editor
@@ -69,19 +72,30 @@ enum
 //Filter imports
 #include "backend/filters/rangeFile.h"
 #include "backend/filters/dataLoad.h"
-
-
 #include "wx/propertyGridUpdater.h"
 
+#include <vector>
+#include <string>
+#include <utility>
+#include <map>
+#include <list>
+#include <stack>
+
+using std::vector;
+using std::string;
 using std::pair;
+using std::map;
+using std::make_pair;
+using std::list;
 using std::max;
+using std::stack;
 
 //milliseconds before clearing status bar (by invoking a status timer event)
 const unsigned int STATUS_TIMER_DELAY=10000; 
 //Milliseconds between querying viscontrol for needing update
 const unsigned int UPDATE_TIMER_DELAY=50; 
 //Milliseconds between progress bar updates 
-const unsigned int PROGRESS_TIMER_DELAY=100; 
+const unsigned int PROGRESS_TIMER_DELAY=40; 
 //Seconds between autosaves
 const unsigned int AUTOSAVE_DELAY=180; 
 
@@ -374,6 +388,8 @@ MainWindowFrame::MainWindowFrame(wxWindow* parent, int id, const wxString& title
 	programmaticEvent=false;
 	fullscreenState=0;
 	verCheckThread=0;
+	refreshThread=0;
+	refreshControl=0;
 	lastProgressData.reset();
 
 	//Set up the program icon handler
@@ -395,15 +411,12 @@ MainWindowFrame::MainWindowFrame(wxWindow* parent, int id, const wxString& title
 	programmaticEvent=false;
 	currentlyUpdatingScene=false;
 	statusTimer = new wxTimer(this,ID_STATUS_TIMER);
-	progressTimer = new wxTimer(this,ID_PROGRESS_TIMER);
 	updateTimer= new wxTimer(this,ID_UPDATE_TIMER);
+	progressTimer= new wxTimer(this,ID_PROGRESS_TIMER);
 	autoSaveTimer= new wxTimer(this,ID_AUTOSAVE_TIMER);
 	requireFirstUpdate=true;
 
 
-	//Tell the vis controller which window is to remain
-	//semi-active during calls to wxSafeYield from callback system
-	visControl.setYieldWindow(this);
 	//Set up keyboard shortcuts "accelerators"
 	wxAcceleratorEntry entries[1];
 	entries[0].Set(0,WXK_ESCAPE,ID_PROGRESS_ABORT);
@@ -429,9 +442,10 @@ MainWindowFrame::MainWindowFrame(wxWindow* parent, int id, const wxString& title
 		wxErrMsg(this,TRANS("OpenGL Failed"),
 TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please check your video drivers.")  );
 		
-		cerr << TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please check your video drivers.") << endl;
+		std::cerr << TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please check your video drivers.") << std::endl;
 		return;
 	}
+   panelTop->setScene(&visControl.scene);
 
     panelLeft = new wxPanel(splitLeftRight, wxID_ANY);
     notebookControl = new wxNotebook(panelLeft, ID_NOTEBOOK_CONTROL, wxDefaultPosition, wxDefaultSize, wxNB_RIGHT);
@@ -464,7 +478,7 @@ TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please
     fileExport->Append(ID_FILE_EXPORT_IMAGE, TRANS("&Image...\tCtrl+I"), TRANS("Export Current 3D View"), wxITEM_NORMAL);
     fileExport->Append(ID_FILE_EXPORT_IONS, TRANS("Ion&s...\tCtrl+N"), TRANS("Export Ion Data"), wxITEM_NORMAL);
     fileExport->Append(ID_FILE_EXPORT_RANGE, TRANS("Ran&ges...\tCtrl+G"), TRANS("Export Range Data"), wxITEM_NORMAL);
-    fileExport->Append(ID_FILE_EXPORT_FILTER_ANIMATION, TRANS("&Animate Filters...\tCtrl+A"), TRANS("Export Animated Filter"), wxITEM_NORMAL);
+    fileExport->Append(ID_FILE_EXPORT_FILTER_ANIMATION, TRANS("&Animate Filters...\tCtrl+T"), TRANS("Export Animated Filter"), wxITEM_NORMAL);
     fileExport->Append(ID_FILE_EXPORT_ANIMATION, TRANS("Ani&mate Camera...\tCtrl+M"), TRANS("Export Animated Camera"), wxITEM_NORMAL);
     fileExport->Append(ID_FILE_EXPORT_PACKAGE, TRANS("Pac&kage...\tCtrl+K"), TRANS("Export analysis package"), wxITEM_NORMAL);
 
@@ -578,8 +592,6 @@ TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please
     msgs.push_back("No data loaded:");
     msgs.push_back("open file, then add filters");
     treeFilters->setMessages(msgs); 
-    lastRefreshLabel = new wxStaticText(filterTreePane, wxID_ANY, TRANS("Last Outputs"));
-    listLastRefresh = new wxListCtrl(filterTreePane, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSUNKEN_BORDER);
     checkAutoUpdate = new wxCheckBox(filterTreePane, ID_CHECK_AUTOUPDATE, TRANS("Auto Refresh"));
     refreshButton = new wxButton(filterTreePane, wxID_REFRESH, wxEmptyString);
     btnFilterTreeExpand= new wxButton(filterTreePane, ID_BTN_EXPAND, wxT("▼"),wxDefaultPosition,wxSize(30,30));
@@ -650,9 +662,9 @@ TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please
     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));
+ //   checkLimitOutput->SetValue((visControl.getIonDisplayLimit() !=0));
     std::string tmpStr;
-    stream_cast(tmpStr,visControl.getIonDisplayLimit());
+//    stream_cast(tmpStr,visControl.getIonDisplayLimit());
     textLimitOutput = new wxTextCtrl(noteTools, ID_TEXT_LIMIT_POINT_OUT, (tmpStr),
 		    	wxDefaultPosition,wxDefaultSize,wxTE_PROCESS_ENTER );
     checkCaching = new wxCheckBox(noteTools, ID_CHECK_CACHING, TRANS("Filter caching"));
@@ -682,7 +694,7 @@ TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please
     noteFxPanelCrop->Enable(false);
     noteFxPanelStereo->Enable(false);
 #else
-    //Disable Fx panel stereo controls explicitly
+    //Disable effects panel stereo controls explicitly
     comboFxStereoMode->Enable(false);
     sliderFxStereoBaseline->Enable(false);
     checkFxStereoLensFlip->Enable(false);
@@ -715,10 +727,6 @@ TRANS("Unable to initialise the openGL (3D) panel. Program cannot start. Please
     splitTopBottom->SetSashGravity(0.85);
     splitterSpectra->SetSashGravity(0.82);
 
-    //Last Refresh box
-    listLastRefresh->InsertColumn(0,TRANS("Type"));
-    listLastRefresh->InsertColumn(1,TRANS("Num"));
-
     //Set callback for mathgl plot
     panelSpectra->registerUpdateHandler(this,
 		    (UpdateHandler)&MainWindowFrame::onPanelSpectraUpdate);
@@ -786,7 +794,6 @@ MainWindowFrame::~MainWindowFrame()
 
 	//Delete and stop all the timers.
 	delete statusTimer;
-	delete progressTimer;
 	delete updateTimer;
 	delete autoSaveTimer;
 
@@ -910,10 +917,37 @@ BEGIN_EVENT_TABLE(MainWindowFrame, wxFrame)
   
 
     EVT_COMMAND(wxID_ANY, RemoteUpdateAvailEvent, MainWindowFrame::OnCheckUpdatesThread)
+    EVT_COMMAND(wxID_ANY, RefreshCompleteEvent, MainWindowFrame::OnFinishRefreshThread)
     // end wxGlade
 END_EVENT_TABLE();
 
 
+
+RefreshThread::RefreshThread(wxWindow *target,RefreshController *rC)
+		 : wxThread(wxTHREAD_JOINABLE)
+{
+	ASSERT(rC);
+	refreshControl=rC;
+	targetWindow=target;
+}
+
+RefreshThread::~RefreshThread()
+{
+}
+
+void *RefreshThread::Entry()
+{
+  	wxCommandEvent event( RefreshCompleteEvent);
+	event.SetInt(0);
+
+	//pack the unsigned int into the event
+	unsigned int i=refreshControl->refresh();
+	event.SetInt((int)i);
+	wxPostEvent(targetWindow,event);
+
+	return 0;
+}
+
 void MainWindowFrame::OnIdle(wxIdleEvent &evt)
 {
 	if(backFilterPropGrid)
@@ -943,7 +977,7 @@ unsigned int MainWindowFrame::guessFileType(const std::string &dataFile)
 	//Test the extension to determine what we will do
 	//TODO: This is really lazy, and I should use something like libmagic.
 	std::string extStr;
-	extStr=stlStr(ext);
+	extStr=lowercase(stlStr(ext));
 
 	if( extStr == std::string("xml"))
 		return FILE_OPEN_TYPE_XML;
@@ -978,8 +1012,7 @@ bool MainWindowFrame::getTreeFilterId(const wxTreeItemId &tId, size_t &filterId)
 
 void MainWindowFrame::checkAskSaveState()
 {
-
-	if(visControl.hasStateData() && visControl.stateModifyLevel() >=STATE_MODIFIED_ANCILLARY)
+	if(visControl.stateIsModified())
 	{
 		wxMessageDialog wxD (this,
 			TRANS("Current state has not been saved, would you like to save it now?")
@@ -998,17 +1031,15 @@ void MainWindowFrame::checkAskSaveState()
 void MainWindowFrame::OnFileOpen(wxCommandEvent &event)
 {
 	//Do not allow any action if a scene update is in progress
-	if(currentlyUpdatingScene || visControl.isRefreshing())
-		return;
-
+	ASSERT(!refreshThreadActive());
 
 	vector<pair<std::string,std::string> > validTypes;
 	validTypes.push_back(	make_pair(TRANS("Readable files (*.xml, *.pos, *.txt,*.csv, *.ato)"),
-					"*.xml;*.pos;*.txt;*.csv;*.ato") );
-	validTypes.push_back( make_pair(TRANS("XML State File (*.xml)"),"*.xml"));
-	validTypes.push_back( make_pair(TRANS("POS File (*.pos)"),"*.pos"));
-	validTypes.push_back( make_pair(TRANS("LAWATAP ATO File (*.ato)"),"*.ato"));
-	validTypes.push_back( make_pair(TRANS("Text File (*.txt, *.csv)"),"*.csv;*.txt"));
+					"*.xml;*XML;*.pos;*,POS;*.txt;*.TXT;*.csv;*.CSV;*.ato;*.ATO") );
+	validTypes.push_back( make_pair(TRANS("XML State File (*.xml)"),"*.xml;*.XML"));
+	validTypes.push_back( make_pair(TRANS("POS File (*.pos)"),"*.pos;*.POS"));
+	validTypes.push_back( make_pair(TRANS("LAWATAP ATO File (*.ato)"),"*.ato;*.ATO"));
+	validTypes.push_back( make_pair(TRANS("Text File (*.txt, *.csv)"),"*.csv;*.txt;*.CSV;*.TXT"));
 	validTypes.push_back( make_pair(TRANS("All Files (*)"),"*"));
 
 	std::string totalStr;
@@ -1035,9 +1066,9 @@ void MainWindowFrame::OnFileOpen(wxCommandEvent &event)
 	if(guessFileType(filePath) == FILE_OPEN_TYPE_XML)
 		checkAskSaveState();
 	
-	//Force an update with an empty scene	
-	visControl.clear();
-	doSceneUpdate();
+	//Force an update to viscontrol
+	visControl.clearScene();
+	visControl.scene.draw();
 
 	textConsoleOut->Clear();
 	//Get vis controller to update tree control to match internal
@@ -1045,7 +1076,9 @@ void MainWindowFrame::OnFileOpen(wxCommandEvent &event)
 	// have a valid selection
 	size_t filterId;
 	if(getTreeFilterId(treeFilters->GetSelection(),filterId))
+	{
 		visControl.setWxTreeFilterViewPersistence(filterId);
+	}
 	
 	//Load the file
 	if(!loadFile(wxF.GetPath()))
@@ -1063,19 +1096,17 @@ void MainWindowFrame::OnFileOpen(wxCommandEvent &event)
 
 	//If we are using the default camera,
 	//move it to make sure that it is visible
-	if(visControl.numCams() == 1)
-		visControl.ensureSceneVisible(3);
-
-	statusMessage(TRANS("Loaded file."),MESSAGE_INFO);
+	if(visControl.state.getNumCams() == 1)
+	{
+		visControl.scene.ensureVisible(3);
+	}
 
 	panelTop->forceRedraw();
 }
 
 void MainWindowFrame::OnFileMerge(wxCommandEvent &event)
 {
-	//Do not allow any action if a scene update is in progress
-	if(currentlyUpdatingScene || visControl.isRefreshing())
-		return;
+	ASSERT(!refreshThreadActive());
 
 	//Load a file, either a state file, or a new pos file, or text file
 	wxFileDialog wxF(this,TRANS("Select Data or State File..."), wxT(""),
@@ -1100,8 +1131,8 @@ void MainWindowFrame::OnFileMerge(wxCommandEvent &event)
 void MainWindowFrame::OnDropFiles(const wxArrayString &files, int x, int y)
 {
 	//We can't alter the filter state if we are refreshing
-	if(visControl.isRefreshing())
-		return ;
+	if(refreshThreadActive())
+		return;
 
 	textConsoleOut->Clear();
 	wxMouseState wxm = wxGetMouseState();
@@ -1159,10 +1190,10 @@ void MainWindowFrame::OnDropFiles(const wxArrayString &files, int x, int y)
 
 					//Add the filter, using the seelcted
 					// item as the parent
-					visControl.addFilter(f,false,filterId);
+					visControl.state.treeState.addFilter(f,false,filterId);
 
 					//update the tree control
-					visControl.updateWxTreeCtrl(treeFilters);
+					updateWxTreeCtrl(treeFilters);
 				}
 				else
 				{
@@ -1210,15 +1241,18 @@ void MainWindowFrame::OnDropFiles(const wxArrayString &files, int x, int y)
 	{
 		//If we are using the default camera,
 		//move it to make sure that it is visible
-		if(visControl.numCams() == 1)
-			visControl.ensureSceneVisible(3);
+		if(visControl.state.getNumCams() == 1)
+		{
+			visControl.scene.ensureVisible(3);
+		}
 	}
 }
 
 bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate)
 {
+	ASSERT(!refreshThreadActive());
+
 	//Don't try to alter viscontrol if we are refreshing. That would be bad.
-	ASSERT(!visControl.isRefreshing());
 	
 	std::string dataFile = stlStr(fileStr);
 	unsigned int fileType=guessFileType(dataFile);
@@ -1228,7 +1262,7 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 		std::stringstream ss;
 		
 		//Load the file as if it were an XML file
-		if(!visControl.loadState(dataFile.c_str(),ss,merge))
+		if(!visControl.state.load(dataFile.c_str(),merge,ss))
 		{
 			std::string str;
 			str=ss.str();
@@ -1245,7 +1279,7 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 		}
 
 
-		if(visControl.hasHazardousContents())
+		if(visControl.state.treeState.getTreeRef().hasHazardousContents())
 		{
 			wxMessageDialog wxD(this,
 						TRANS("This state file contains filters that can be unsafe to run\nDo you wish to remove these before continuing?.") 
@@ -1255,7 +1289,7 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 			wxD.SetEscapeId(wxID_NO);
 
 			if(wxD.ShowModal()!= wxID_NO)
-				visControl.stripHazardousContents();
+				visControl.state.treeState.stripHazardousContents();
 
 		}
 
@@ -1263,31 +1297,15 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 		if(panelTop->isInited())
 			panelTop->updateClearColour();
 
-		checkViewWorldAxis->Check(visControl.getAxisVisible());
-
-		{
-		//Update the camera dropdown
-		vector<std::string > camNames;
-		visControl.getCamData(camNames);
-
-		comboCamera->Clear();
-		for(unsigned int ui=1;ui<camNames.size();ui++)
-		{
-			//Do not delete as this will be deleted by wx
-			comboCamera->Append((camNames[ui]),
-					(wxClientData *)new wxListUint(ui));	
-			//If this is the active cam (1) set the selection and (2) remember
-			//the ID
-			if(ui == visControl.getActiveCamId())
-				comboCamera->SetSelection(ui-1);
-		}
+		checkViewWorldAxis->Check(visControl.scene.getWorldAxisVisible());
 
+		visControl.updateCameraComboBox(comboCamera);
 		//Only update the camera grid if we have a valid uniqueID
-		if(camNames.size() > 1)
+		if(visControl.state.getNumCams() > 1)
 		{
-			//Use the remembered ID to update the grid.
+			//Use the active cam to update the grid.
 			visControl.updateCameraPropGrid(gridCameraProperties,
-						visControl.getActiveCamId());
+						visControl.state.getActiveCam());
 		}
 		else
 		{
@@ -1295,7 +1313,6 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 			gridCameraProperties->Clear();
 			comboCamera->SetValue((TRANS(cameraIntroString)));
 		}
-		}
 
 		//reset the stash combo box
 		comboStash->SetValue((TRANS(stashIntroString)));
@@ -1303,34 +1320,19 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 
 		//Check to see if we have any effects that we need to enable
 		vector<const Effect*> effs;
-		panelTop->currentScene.getEffects(effs);
+		visControl.scene.getEffects(effs);
 		if(!effs.empty())
 		{
 			//OK, we have some effects; we will need to update the UI
 			updateFxUI(effs);
 		}
 
-
-
 		fileSave->Enable(true);
-
-		
 		
 		//Update the stash combo box
-		comboStash->Clear();
-	
-		std::vector<std::pair<std::string,unsigned int > > stashList;
-		visControl.getStashes(stashList);
-		for(unsigned int ui=0;ui<stashList.size(); ui++)
-		{
-			wxListUint *u;
-			u = new wxListUint(stashList[ui].second);
-			comboStash->Append((stashList[ui].first),(wxClientData *)u);
-			ASSERT(comboStash->GetClientObject(comboStash->GetCount()-1));
-		}
+		visControl.updateStashComboBox(comboStash);
 
 		gridFilterPropGroup->Clear();
-
 	}
 	else 
 	{
@@ -1357,16 +1359,17 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 		// tree control.
 		// adding filters will invalidate IDs, so this needs to be set now
 		size_t filterId;
+		
 		if(getTreeFilterId(treeFilters->GetSelection(),filterId))
 			visControl.setWxTreeFilterViewPersistence(filterId);
 
 		//Append a new filter to the filter tree
 		fTree.addFilter(posFilter,0);
-		visControl.addFilterTree(fTree,true,0);
+		visControl.state.treeState.addFilterTree(fTree,true,0);
 
 	}	
 
-	visControl.updateWxTreeCtrl(treeFilters);
+	updateWxTreeCtrl(treeFilters);
 
 	if(!noUpdate)
 		return doSceneUpdate();
@@ -1377,7 +1380,7 @@ bool MainWindowFrame::loadFile(const wxString &fileStr, bool merge,bool noUpdate
 void MainWindowFrame::OnRecentFile(wxCommandEvent &event)
 {
 
-	if(currentlyUpdatingScene || visControl.isRefreshing())
+	if(refreshThreadActive())
 		return;
 
 	wxString f(recentHistory->GetHistoryFile(event.GetId() - wxID_FILE1));
@@ -1390,6 +1393,7 @@ void MainWindowFrame::OnRecentFile(wxCommandEvent &event)
 		// tree control.
 		// adding filters will invalidate IDs, so this needs to be set now
 		size_t filterId;
+
 		if(getTreeFilterId(treeFilters->GetSelection(),filterId))
 			visControl.setWxTreeFilterViewPersistence(filterId);
 
@@ -1401,13 +1405,10 @@ void MainWindowFrame::OnRecentFile(wxCommandEvent &event)
 			//See if the user wants to save the current state
 			if(guessFileType(stlStr(f)) == FILE_OPEN_TYPE_XML)
 				checkAskSaveState();
-	
+
+			//start the loading sequence. Note that this is done
+			// in a rear thread, so we cannot be totally sure it worked yet	
 			loadOK=loadFile(f);	
-			if(loadOK)
-			{
-				statusMessage(TRANS("Loaded file."),MESSAGE_INFO);
-				panelTop->forceRedraw();
-			}
 		}
 		
 		if(!loadOK)
@@ -1421,15 +1422,15 @@ void MainWindowFrame::OnRecentFile(wxCommandEvent &event)
 		setSaveStatus();
 
 		//make sure camera is properly centred
-		if(visControl.numCams() == 1)
-			visControl.ensureSceneVisible(3);
+		if(visControl.state.getNumCams() == 1)
+			visControl.scene.ensureVisible(3);
 	}
 
 }
 
 void MainWindowFrame::OnFileSave(wxCommandEvent &event)
 {
-	std::string saveFilename=visControl.getFilename();
+	std::string saveFilename=visControl.state.getFilename();
 
 	//Save menu should not be selectable if there is no file to save to.
 	ASSERT(!saveFilename.empty());
@@ -1442,7 +1443,7 @@ void MainWindowFrame::OnFileSave(wxCommandEvent &event)
 	
 	std::map<string,string> dummyMap;
 	//Try to save the viscontrol state
-	if(!visControl.saveState(saveFilename.c_str(),dummyMap))
+	if(!visControl.state.save(saveFilename.c_str(),dummyMap,false))
 	{
 		wxErrMsg(this,TRANS("Save error"),TRANS("Unable to save. Check output destination can be written to."));
 	}
@@ -1463,7 +1464,6 @@ void MainWindowFrame::OnFileSave(wxCommandEvent &event)
 
 void MainWindowFrame::OnFileExportPlot(wxCommandEvent &event)
 {
-
 	if(!panelSpectra->getNumVisible())
 	{
 		wxErrMsg(this,TRANS("Unable to save"),
@@ -1715,20 +1715,27 @@ void MainWindowFrame::OnFileExportVideo(wxCommandEvent &event)
 void MainWindowFrame::setLockUI(bool locking=true,
 		unsigned int lockMode=WINDOW_LOCK_REFRESH)
 {
+	unsigned int nUndo,nRedo;
+	nUndo=visControl.state.treeState.getUndoSize();
+	nRedo=visControl.state.treeState.getRedoSize();
 	switch(lockMode)
 	{
 		case WINDOW_LOCK_REFRESH:
 		{
-			comboFilters->Enable(!locking && visControl.numFilters());
-			refreshButton->Enable(!locking && visControl.numFilters());;
+			unsigned int nFilters;
+			nFilters = visControl.state.treeState.size();
+			comboFilters->Enable(!locking && nFilters);
+			refreshButton->Enable(!locking && nFilters);
 			btnFilterTreeErrs->Enable(!locking);
+			treeFilters->Enable(!locking);	
 
 
-			editUndoMenuItem->Enable(!locking && visControl.getUndoSize());
-			editRedoMenuItem->Enable(!locking && visControl.getRedoSize());
+			editUndoMenuItem->Enable(!locking && nUndo);
+			editRedoMenuItem->Enable(!locking && nRedo);
+		
 			fileMenu->Enable(ID_FILE_OPEN,!locking);
 			fileMenu->Enable(ID_FILE_MERGE,!locking);
-			
+		
 			gridFilterPropGroup->Enable(!locking);
 			comboStash->Enable(!locking);
 
@@ -1774,10 +1781,9 @@ void MainWindowFrame::setLockUI(bool locking=true,
 
 			comboStash->Enable(!locking);
 			treeFilters->Enable(!locking);
-			
-			editUndoMenuItem->Enable(!locking && visControl.getUndoSize());
-			editRedoMenuItem->Enable(!locking && visControl.getRedoSize());
 
+			editUndoMenuItem->Enable(!locking && nUndo);
+			editRedoMenuItem->Enable(!locking && nRedo);
 
 			fileMenu->Enable(ID_FILE_OPEN,!locking);
 			fileMenu->Enable(ID_FILE_MERGE,!locking);
@@ -1822,15 +1828,17 @@ void MainWindowFrame::setLockUI(bool locking=true,
 
 void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 {
-
 	//Don't let the user run the animation dialog if they have
 	// no filters open
-	if(!visControl.numFilters())
+	if(!visControl.state.treeState.size())
 	{
 		statusMessage(TRANS("Cannot animate with no filters."));
 		return;
 	}
 
+	//Cannot proceed until refresh is completed or aborted
+	if(refreshThreadActive())
+		return;
 
 
 	int w, h;
@@ -1846,23 +1854,17 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 	// Getting/Setting animation state requires the filtertree to
 	// be under exportDialog's control
 	FilterTree treeWithCache;
-
 	//Steal the filter tree, and give the pointer to the export dialog
 	// viscontrol now has an empty tree, so watch out.
-	visControl.swapFilterTree(treeWithCache);
-
+	visControl.state.treeState.swapFilterTree(treeWithCache);
+	//supply a copy of the filter tree (w/o cache) to export dialog
 	exportDialog->setTree(treeWithCache);
 
-	//FIXME: HACK - this needs to be called twice
-	// due to synchronisation problems with 
-	// ExportAnimationDialog::setAnimationState.
-	exportDialog->prepare();
-
 	//Set the saved animation properties, as needed
 	{
 	PropertyAnimator p;
 	vector<pair<string,size_t> > pathMap;
-	visControl.getAnimationState(p,pathMap);
+	visControl.state.getAnimationState(p,pathMap);
 	if(p.getMaxFrame())
 	{
 		exportDialog->setAnimationState(p,pathMap);
@@ -1871,6 +1873,7 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 	
 	exportDialog->prepare();
 
+
 	//Display Animate dialog
 	bool dialogErr;
 	dialogErr=(exportDialog->ShowModal() == wxID_CANCEL);
@@ -1882,9 +1885,9 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 	exportDialog->getAnimationState(propAnim,pathMap);
 	
 	//restore the cache to viscontrol
-	visControl.swapFilterTree(treeWithCache);
+	visControl.state.treeState.swapFilterTree(treeWithCache);
 
-	visControl.setAnimationState(propAnim,pathMap);
+	visControl.state.setAnimationState(propAnim,pathMap);
 	}
 
 	//Stop processing here if user aborted
@@ -1894,6 +1897,7 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 		return;
 	}
 
+
 	//Stop timer based events, and lock UI
 	//--
 	updateTimer->Stop();
@@ -1917,9 +1921,6 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 	bool needAbortDlg=false;
 
 
-	//Ensure that viscontrol returns control to progress window
-	visControl.setYieldWindow(prog);
-
 	//Modify the tree.
 	for(size_t ui=0;ui<numFrames;ui++)
 	{
@@ -1929,7 +1930,7 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 
 		bool needsUp;
 		//steal tree, including caches, from viscontrol
-		visControl.swapFilterTree(treeWithCache);
+		visControl.state.treeState.swapFilterTree(treeWithCache);
 		
 		//Modify the tree, as needed, altering cached data
 		if(!exportDialog->getModifiedTree(ui,treeWithCache,needsUp))
@@ -1942,7 +1943,7 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 		}
 
 		//restore tree to viscontrol
-		visControl.swapFilterTree(treeWithCache);
+		visControl.state.treeState.swapFilterTree(treeWithCache);
 
 		//Perform update
 		if(needsUp || !exportDialog->wantsOnlyChanges())
@@ -1950,9 +1951,11 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 			typedef std::vector<const FilterStreamData * >  STREAMOUT;
 			std::list<FILTER_OUTPUT_DATA> outData;
 			std::list<STREAMOUT> outStreams;
+			std::vector<std::pair<const Filter *, std::string> > cMessages;
+			ProgressData progData;
 
 			//First try to refresh the tree
-			if(visControl.refreshFilterTree(outData))
+			if(visControl.state.treeState.refresh(outData,cMessages,progData))
 			{
 				std::string tmpStr;
 				stream_cast(tmpStr,ui);
@@ -1973,34 +1976,23 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 				if(exportDialog->wantsImages())
 				{
 
-					vector<SelectionDevice *> dummy;
-					//update the output streams, but do not release
-					// the contents.
-					if(visControl.doUpdateScene(outStreams,dummy,false))
+					// Update the  scene contents.
+					visControl.updateScene(outStreams,false);
+					panelTop->forceRedraw();
+					//Attempt to save the image to disk
+					if(!panelTop->saveImage(exportDialog->getImageWidth(),
+						exportDialog->getImageHeight(),
+						exportDialog->getFilename(ui,FILENAME_IMAGE).c_str(),false,false))
 					{
 						pair<string,string> errMsg;
 						string tmpStr;
 						stream_cast(tmpStr,ui);
-						errMsg.first=TRANS("Scene generation failed");
-						errMsg.second = TRANS("Unable to generate scene for frame ");
+						errMsg.first=TRANS("Unable to save");
+						errMsg.second = TRANS("Image save failed for frame ");
 						errMsg.second+=tmpStr;
 						throw errMsg;
 					}
-					else
-					{
-						if(!panelTop->saveImage(exportDialog->getImageWidth(),
-							exportDialog->getImageHeight(),
-							exportDialog->getFilename(ui,FILENAME_IMAGE).c_str(),false,false))
-						{
-							pair<string,string> errMsg;
-							string tmpStr;
-							stream_cast(tmpStr,ui);
-							errMsg.first=TRANS("Unable to save");
-							errMsg.second = TRANS("Image save failed for frame ");
-							errMsg.second+=tmpStr;
-							throw errMsg;
-						}
-					}
+					
 				}
 
 				if(exportDialog->wantsIons())
@@ -2016,7 +2008,7 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 						std::copy(it->begin(),it->end(),mergedStreams.begin() +origSize);
 					}
 
-					if(visControl.exportIonStreams(mergedStreams,exportDialog->getFilename(ui,FILENAME_IONS)))
+					if(IonStreamData::exportStreams(mergedStreams,exportDialog->getFilename(ui,FILENAME_IONS)))
 					{
 						pair<string,string> errMsg;
 						string tmpStr;
@@ -2120,7 +2112,7 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 							v=(const VoxelStreamData*)(*it)[uj];
 							
 							std::string filename = exportDialog->getFilename(ui,FILENAME_VOXEL,offset);
-							if(v->data.writeFile(filename.c_str()))
+							if(v->data->writeFile(filename.c_str()))
 							{
 								pair<string,string> errMsg;
 								string tmpStr;
@@ -2140,20 +2132,19 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 			{
 				errMessage=errMsg.first + "\n" + errMsg.second;
 				//clean up data
-				visControl.safeDeleteFilterList(outData);
+				FilterTree::safeDeleteFilterList(outData);
 				needAbortDlg=true;
 				break;
 			}
 
 			//Clean up date from this run, releasing stream pointers.
-			visControl.safeDeleteFilterList(outData);
+			FilterTree::safeDeleteFilterList(outData);
 			outStreams.clear();
 
 		}
 
 	}
 
-	visControl.setYieldWindow(this);
 	
 	if(needAbortDlg)
 		wxErrMsg(this,TRANS("Animate failed"),errMessage);
@@ -2174,7 +2165,6 @@ void MainWindowFrame::OnFileExportFilterVideo(wxCommandEvent &event)
 	updateTimer->Start(UPDATE_TIMER_DELAY,wxTIMER_CONTINUOUS);
 	autoSaveTimer->Start(AUTOSAVE_DELAY*1000,wxTIMER_CONTINUOUS);
 	//--
-
 }
 
 void MainWindowFrame::OnFileExportPackage(wxCommandEvent &event)
@@ -2244,12 +2234,13 @@ void MainWindowFrame::OnFileExportPackage(wxCommandEvent &event)
 	}
 
 
+
 	//OK, so the folder exists, lets make the XML state file
 	std::string dataFile = string(stlStr(folder)) + "state.xml";
 
 	std::map<string,string> fileMapping;
 	//Try to save the viscontrol state
-	if(!visControl.saveState(dataFile.c_str(),fileMapping,true))
+	if(!visControl.state.save(dataFile.c_str(),fileMapping,true))
 	{
 		wxErrMsg(this,TRANS("Save error"),
 			TRANS("Unable to save. Check output destination can be written to."));
@@ -2287,7 +2278,7 @@ void MainWindowFrame::OnFileExportPackage(wxCommandEvent &event)
 			
 			if(wantDebugPack && isPosFile && filesize > CHUNKSIZE)
 			{
-				ifstream inputF(it->second.c_str(),std::ios::binary);
+				std::ifstream inputF(it->second.c_str(),std::ios::binary);
 				if(!inputF)
 				{
 					copyError=true;
@@ -2298,7 +2289,7 @@ void MainWindowFrame::OnFileExportPackage(wxCommandEvent &event)
 				char *c = new char[CHUNKSIZE];
 				std::string outfname;
 				outfname=stlStr(folder) + it->first;
-				ofstream of(outfname.c_str(),std::ios::binary);
+				std::ofstream of(outfname.c_str(),std::ios::binary);
 				if(!of)
 				{
 					delete[] c;
@@ -2344,9 +2335,10 @@ void MainWindowFrame::OnFileExportIons(wxCommandEvent &event)
 
 	}
 
+
 	//Steal the filter tree (including caches) from viscontrol
 	FilterTree f;
-	visControl.switchoutFilterTree(f);
+	visControl.state.treeState.switchoutFilterTree(f);
 	
 	//Load up the export dialog
 	ExportPosDialog *exportDialog=new ExportPosDialog(this,wxID_ANY,TRANS("Export"));
@@ -2366,11 +2358,11 @@ void MainWindowFrame::OnFileExportIons(wxCommandEvent &event)
 			//Take control of the filter tree back from the export dialog,
 			// and return it to visControl	
 			exportDialog->swapFilterTree(f);
-			visControl.swapFilterTree(f);
+			visControl.state.treeState.swapFilterTree(f);
 			exportDialog->Destroy();
 			
 			//Need this to reset the ID values
-			visControl.updateWxTreeCtrl(treeFilters);
+			updateWxTreeCtrl(treeFilters);
 			return;	
 		}
 		
@@ -2390,10 +2382,10 @@ void MainWindowFrame::OnFileExportIons(wxCommandEvent &event)
 			//Take control of the filter tree back from the export dialog,
 			// and return it to visControl	
 			exportDialog->swapFilterTree(f);
-			visControl.swapFilterTree(f);
+			visControl.state.treeState.swapFilterTree(f);
 			
 			//Need this to reset the ID values
-			visControl.updateWxTreeCtrl(treeFilters);
+			updateWxTreeCtrl(treeFilters);
 			exportDialog->Destroy();
 			return;
 		}
@@ -2407,7 +2399,7 @@ void MainWindowFrame::OnFileExportIons(wxCommandEvent &event)
 	
 
 	//write the ion streams to disk
-	if(visControl.exportIonStreams(exportVec,dataFile))
+	if(IonStreamData::exportStreams(exportVec,dataFile))
 	{
 		wxErrMsg(this,TRANS("Save error"),
 			TRANS("Unable to save. Check output destination can be written to."));
@@ -2421,13 +2413,13 @@ void MainWindowFrame::OnFileExportIons(wxCommandEvent &event)
 	//Take control of the filter tree back from the export dialog,
 	// and return it to visControl	
 	exportDialog->swapFilterTree(f);
-	visControl.swapFilterTree(f);
+	visControl.state.treeState.swapFilterTree(f);
 
 	//Call ->Destroy to invoke destructor, which will safely delete the
 	//filterstream pointers it generated	
 	exportDialog->Destroy();
 	//Need this to reset the ID values
-	visControl.updateWxTreeCtrl(treeFilters);
+	updateWxTreeCtrl(treeFilters);
 }
 
 void MainWindowFrame::OnFileExportRange(wxCommandEvent &event)
@@ -2444,7 +2436,7 @@ void MainWindowFrame::OnFileExportRange(wxCommandEvent &event)
 
 	vector<const Filter *> rangeData;
 	//Retrieve all the range filters in the viscontrol
-	visControl.getFiltersByType(rangeData,FILTER_TYPE_RANGEFILE);
+	visControl.state.treeState.getFiltersByType(rangeData,FILTER_TYPE_RANGEFILE);
 	//pass this to the range dialog
 	rngDialog->addRangeData(rangeData);
 
@@ -2454,7 +2446,6 @@ void MainWindowFrame::OnFileExportRange(wxCommandEvent &event)
 		return;
 	}
 
-
 	rngDialog->Destroy();
 }
 
@@ -2497,12 +2488,11 @@ void MainWindowFrame::OnFileSaveAs(wxCommandEvent &event)
 	}
 	else
 		dataFile+=".xml";
-	
 
-	bool oldRelPath=visControl.usingRelPaths();
+	bool oldRelPath=visControl.state.getUseRelPaths();
 	//Check to see if we have are using relative paths,
 	//and if so, do any of our filters
-	if(visControl.usingRelPaths() && visControl.hasStateOverrides())
+	if(visControl.state.getUseRelPaths() && visControl.state.hasStateOverrides())
 	{
 		wxMessageDialog wxD(this,TRANS("Files have been referred to using relative paths. Keep relative paths?")
 						,TRANS("Overwrite?"),wxYES|wxNO|wxICON_QUESTION);
@@ -2514,7 +2504,7 @@ void MainWindowFrame::OnFileSaveAs(wxCommandEvent &event)
 		if(wxD.ShowModal() == wxID_NO)
 		{
 			oldRelPath=true;
-			visControl.setUseRelPaths(false);
+			visControl.state.setUseRelPaths(false);
 		}
 
 	}
@@ -2522,7 +2512,7 @@ void MainWindowFrame::OnFileSaveAs(wxCommandEvent &event)
 
 	std::map<string,string> dummyMap;
 	//Try to save the viscontrol state
-	if(!visControl.saveState(dataFile.c_str(),dummyMap))
+	if(!visControl.state.save(dataFile.c_str(),dummyMap,false))
 	{
 		wxErrMsg(this,TRANS("Save error"),
 			TRANS("Unable to save. Check output destination can be written to."));
@@ -2531,7 +2521,7 @@ void MainWindowFrame::OnFileSaveAs(wxCommandEvent &event)
 	{
 		std::string tmpStr;
 		tmpStr=stlStr(wxF.GetPath());
-		visControl.setFilename(tmpStr);
+		visControl.state.setFilename(tmpStr);
 
 		//Update the recent files, and the menu.
 		configFile.addRecentFile(dataFile);
@@ -2542,8 +2532,7 @@ void MainWindowFrame::OnFileSaveAs(wxCommandEvent &event)
 	}
 
 	//Restore the relative path behaviour
-	visControl.setUseRelPaths(oldRelPath);
-	
+	visControl.state.setUseRelPaths(oldRelPath);
 	setSaveStatus();
 }
 
@@ -2556,8 +2545,9 @@ void MainWindowFrame::OnFileExit(wxCommandEvent &event)
 
 void MainWindowFrame::OnEditUndo(wxCommandEvent &event)
 {
-	visControl.popUndoStack();
 	
+	visControl.state.treeState.popUndoStack();
+
 	//Get vis controller to update tree control to match internal
 	// structure. Retain tree selection & visibility if we currently
 	// have a valid selection
@@ -2566,7 +2556,7 @@ void MainWindowFrame::OnEditUndo(wxCommandEvent &event)
 		visControl.setWxTreeFilterViewPersistence(filterId);
 
 	//Update tree control
-	visControl.updateWxTreeCtrl(treeFilters);
+	updateWxTreeCtrl(treeFilters);
 
 	if(getTreeFilterId(treeFilters->GetSelection(),filterId))
 	{
@@ -2576,24 +2566,24 @@ void MainWindowFrame::OnEditUndo(wxCommandEvent &event)
 	else
 	{
 		gridFilterPropGroup->Clear();
-		updateLastRefreshBox();
 	}
 
 
+	
 
 	doSceneUpdate();
 }
 
 void MainWindowFrame::OnEditRedo(wxCommandEvent &event)
 {
-	visControl.popRedoStack();
+	visControl.state.treeState.popRedoStack();
 
 	size_t filterId;
 	if(getTreeFilterId(treeFilters->GetSelection(),filterId))
 		visControl.setWxTreeFilterViewPersistence(filterId);
 
 	//Update tree control
-	visControl.updateWxTreeCtrl(treeFilters);
+	updateWxTreeCtrl(treeFilters);
 
 	//If we can still get the ID, lets use it
 	if(getTreeFilterId(treeFilters->GetSelection(),filterId))
@@ -2604,7 +2594,6 @@ void MainWindowFrame::OnEditRedo(wxCommandEvent &event)
 	else
 	{
 		gridFilterPropGroup->Clear();
-		updateLastRefreshBox();
 	}
 
 
@@ -2629,11 +2618,13 @@ void MainWindowFrame::OnEditRange(wxCommandEvent &event)
 	r->getModifiedRanges(modifiedRanges);
 
 	//Pass the modified rangefiles to viscontrol
-	visControl.modifyRangeFiles(modifiedRanges);
+	visControl.state.treeState.modifyRangeFiles(modifiedRanges);
 
 	r->Destroy();
 
+	
 	doSceneUpdate();
+
 }
 
 void MainWindowFrame::OnEditPreferences(wxCommandEvent &event)
@@ -2837,7 +2828,7 @@ void MainWindowFrame::OnViewPlotLegend(wxCommandEvent &event)
 
 void MainWindowFrame::OnViewWorldAxis(wxCommandEvent &event)
 {
-	panelTop->currentScene.setWorldAxisVisible(event.IsChecked());
+	visControl.scene.setWorldAxisVisible(event.IsChecked());
 	panelTop->forceRedraw();
 }
 
@@ -2883,12 +2874,8 @@ void MainWindowFrame::OnHelpContact(wxCommandEvent &event)
 
 void MainWindowFrame::OnButtonStashDialog(wxCommandEvent &event)
 {
-	std::vector<std::pair<std::string,unsigned int > > stashVec;
-	visControl.getStashes(stashVec);
 
-	ASSERT(comboStash->GetCount() == stashVec.size())
-
-	if(stashVec.empty())
+	if(!visControl.state.getStashCount())
 	{
 		statusMessage(TRANS("No filter stashes to edit."),MESSAGE_ERROR);
 		return;
@@ -2902,19 +2889,7 @@ void MainWindowFrame::OnButtonStashDialog(wxCommandEvent &event)
 	s->Destroy();
 
 	//Stash list may have changed. Force update
-	stashVec.clear();
-	visControl.getStashes(stashVec);
-
-	comboStash->Clear();
-	for(unsigned int ui=0;ui<stashVec.size(); ui++)
-	{
-		wxListUint *u;
-		u = new wxListUint(stashVec[ui].second);
-		comboStash->Append((stashVec[ui].first),(wxClientData *)u);
-		ASSERT(comboStash->GetClientObject(comboStash->GetCount()-1));
-	}
-
-	
+	visControl.updateStashComboBox(comboStash);
 }
 
 
@@ -2929,7 +2904,7 @@ void MainWindowFrame::OnHelpAbout(wxCommandEvent &event)
 	info.AddDeveloper(wxT("D. Haley"));	
 	info.AddDeveloper(wxT("A. Ceguerra"));	
 	//GNU GPL v3
-	info.SetCopyright(_T("Copyright (C) 2013 3Depict team\n This software is licenced under the GPL Version 3.0 or later\n This program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it\nunder certain conditions; Please see the file COPYING in the program directory for details"));	
+	info.SetCopyright(_T("Copyright (C) 2015 3Depict team\n This software is licenced under the GPL Version 3.0 or later\n This program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it\nunder certain conditions; Please see the file COPYING in the program directory for details"));	
 
 	info.AddArtist(_T("Thanks go to all who have developed the libraries that I use, which make this program possible.\n This includes the wxWidgets team, Alexy Balakin (MathGL), the FTGL and freetype people, the GNU Scientific Library contributors, the tree.h guy (Kasper Peeters)  and more."));
 
@@ -2977,13 +2952,11 @@ void MainWindowFrame::OnComboStashEnter(wxCommandEvent &event)
 	if(!userText.size())
 		return;
 
-	std::vector<std::pair<std::string,unsigned int > > stashList;
-	visControl.getStashes(stashList);
-
 	unsigned int stashPos = (unsigned int ) -1;
-	for(unsigned int ui=0;ui<stashList.size(); ui++)
+	unsigned int nStashes = visControl.state.getStashCount();
+	for(unsigned int ui=0;ui<nStashes; ui++)
 	{
-		if(stashList[ui].first == userText)
+		if(visControl.state.getStashName(ui)== userText)
 		{
 			stashPos=ui;
 			break;
@@ -2999,42 +2972,16 @@ void MainWindowFrame::OnComboStashEnter(wxCommandEvent &event)
 			return;
 		}
 
-		unsigned int n =visControl.stashFilters(filterId,userText.c_str());
-		n=comboStash->Append((userText),(wxClientData *)new wxListUint(n));
-		ASSERT(comboStash->GetClientObject(n));
-		
+		visControl.state.stashFilters(filterId,userText.c_str());
+		visControl.updateStashComboBox(comboStash);
+			
 		statusMessage(TRANS("Created new filter tree stash"),MESSAGE_INFO);
 
 	}
 	else
 	{
-		//Found it. Restore the existing stash
-		//Find the stash associated with this item
-		int index;
-		index= comboStash->FindString(comboStash->GetValue());
-		ASSERT(index != wxNOT_FOUND);
-		wxListUint *l;
-		l =(wxListUint*)comboStash->GetClientObject(index);
-		//Get the parent filter from the tree selection
-		size_t filterId;
-		if(getTreeFilterId(treeFilters->GetSelection(),filterId))
-		{
-			const Filter *parentFilter=
-				(const Filter *)visControl.getFilterById(filterId);
-		
-			visControl.addStashedToFilters(parentFilter,l->value);
-			
-			visControl.updateWxTreeCtrl(treeFilters,
-							parentFilter);
-
-			statusMessage("",MESSAGE_NONE);
-			if(checkAutoUpdate->GetValue())
-				doSceneUpdate();
-
-		}
-	
-		//clear the text in the combo box
-		comboStash->SetValue(wxT(""));
+		//Stash exists, process as if we selected it
+		OnComboStash(event);
 	}
 
 	//clear the text in the combo box
@@ -3059,11 +3006,11 @@ void MainWindowFrame::OnComboStash(wxCommandEvent &event)
 	{
 		//Get the parent filter pointer	
 		const Filter *parentFilter=
-			(const Filter *)visControl.getFilterById(filterId);
+			visControl.state.treeState.getFilterById(filterId);
 	
-		visControl.addStashedToFilters(parentFilter,l->value);
+		visControl.state.addStashedToFilters(parentFilter,l->value);
 		
-		visControl.updateWxTreeCtrl(treeFilters,
+		updateWxTreeCtrl(treeFilters,
 						parentFilter);
 
 		if(checkAutoUpdate->GetValue())
@@ -3080,8 +3027,7 @@ void MainWindowFrame::OnComboStash(wxCommandEvent &event)
 
 void MainWindowFrame::OnTreeEndDrag(wxTreeEvent &event)
 {
-
-	if(visControl.isRefreshing() )
+	if(refreshThreadActive())
 	{
 		event.Veto();
 		return;
@@ -3099,6 +3045,7 @@ void MainWindowFrame::OnTreeEndDrag(wxTreeEvent &event)
 
 	wxMouseState wxm = wxGetMouseState();
 
+
 	//if we have a parent node to reparent this to	
 	if(newParent.IsOk())
 	{
@@ -3114,20 +3061,23 @@ void MainWindowFrame::OnTreeEndDrag(wxTreeEvent &event)
 			//If command button down (ctrl or clover on mac),
 			//then copy, otherwise move
 			if(wxm.CmdDown())
-				needRefresh=visControl.copyFilter(sId,pId);
+				needRefresh=visControl.state.treeState.copyFilter(sId,pId);
 			else
-				needRefresh=visControl.reparentFilter(sId,pId);
+				needRefresh=visControl.state.treeState.reparentFilter(sId,pId);
 		}	
 	}
 	else 
 	{
+
+		const Filter *fSource = visControl.state.treeState.getFilterById(sId);
+		
 		//Only filters that are a data source are allowed to be in the base.
-		if( visControl.filterIsPureDataSource(sId))
+		if( fSource->isPureDataSource())
 		{
 			if(wxm.CmdDown())
-				needRefresh=visControl.copyFilter(sId,0);
+				needRefresh=visControl.state.treeState.copyFilter(sId,0);
 			else
-				needRefresh=visControl.reparentFilter(sId,0);
+				needRefresh=visControl.state.treeState.reparentFilter(sId,0);
 		}
 		else
 			statusMessage(TRANS("Filter type not a data source - can't be at tree base"),MESSAGE_ERROR);
@@ -3136,7 +3086,7 @@ void MainWindowFrame::OnTreeEndDrag(wxTreeEvent &event)
 	if(needRefresh )
 	{
 		//Refresh the treecontrol
-		visControl.updateWxTreeCtrl(treeFilters);
+		updateWxTreeCtrl(treeFilters);
 
 		//We have finished the drag	
 		statusMessage("",MESSAGE_NONE);
@@ -3149,7 +3099,7 @@ void MainWindowFrame::OnTreeEndDrag(wxTreeEvent &event)
 
 void MainWindowFrame::OnTreeSelectionPreChange(wxTreeEvent &event)
 {
-	if(currentlyUpdatingScene || visControl.isRefreshing())
+	if(refreshThreadActive())
 	{
 		event.Veto();
 		return;
@@ -3159,7 +3109,10 @@ void MainWindowFrame::OnTreeSelectionPreChange(wxTreeEvent &event)
 
 void MainWindowFrame::OnTreeSelectionChange(wxTreeEvent &event)
 {
-	ASSERT(!(currentlyUpdatingScene || visControl.isRefreshing() ));
+	if(programmaticEvent)
+		return;
+
+	ASSERT(!refreshThreadActive())
 
 	size_t filterId;
 	if(!getTreeFilterId(treeFilters->GetSelection(),filterId))
@@ -3171,78 +3124,40 @@ void MainWindowFrame::OnTreeSelectionChange(wxTreeEvent &event)
 	comboFilters->Enable();
 	visControl.updateFilterPropGrid(gridFilterPropGroup, filterId);
 
-	updateLastRefreshBox();
-	
 	panelTop->forceRedraw();
-
 }
 
 
-void MainWindowFrame::updateLastRefreshBox()
-{
-	size_t filterId;
-	if(!getTreeFilterId(treeFilters->GetSelection(),filterId))
-		return;
-
-	if(!visControl.getNumFilters())
-	{
-		listLastRefresh->DeleteAllItems();
-		return;
-	}	
-	//Prevent update flicker by disabling interaction
-	listLastRefresh->Freeze();
-	listLastRefresh->DeleteAllItems();
-	
-	//retrieve the current active filter
-	const Filter *f= visControl.getFilterById(filterId);
-	for(unsigned int ui=0;ui<NUM_STREAM_TYPES; ui++)
-	{
-		//Add items to the listbox in the form "type" "count"
-		//if there is a nonzero number of items for that type
-		std::string n;
-		unsigned int numOut;
-		numOut=f->getNumOutput(ui);
-		if(numOut)
-		{
-			long index;
-			stream_cast(n,numOut);
-			index=listLastRefresh->InsertItem(0,(TRANS(STREAM_NAMES[ui])));
-			listLastRefresh->SetItem(index,1,(n));
-		}
-	}
-	listLastRefresh->Thaw();
-}
-
 void MainWindowFrame::updateEditRangeMenu()
 {
 	vector<const Filter *> filtersRange,filtersSpectra;
-	visControl.getFiltersByType(filtersRange,FILTER_TYPE_RANGEFILE);
-	visControl.getFiltersByType(filtersSpectra,FILTER_TYPE_SPECTRUMPLOT);
+	visControl.state.treeState.getFiltersByType(filtersRange,FILTER_TYPE_RANGEFILE);
+	visControl.state.treeState.getFiltersByType(filtersSpectra,FILTER_TYPE_SPECTRUMPLOT);
 
 	//Only show the menu item if we have both ranges and plots in our
-	// fitler tree
+	// filter tree
 	bool wantEnable = filtersRange.size() && filtersSpectra.size();
 	editRangeMenuItem->Enable(wantEnable);
-	
 }
 
 void MainWindowFrame::OnTreeDeleteItem(wxTreeEvent &event)
 {
-	if(visControl.isRefreshing() )
+	if(refreshThreadActive())
 	{
+		ASSERT(false); //Shouldn't happen, but might have...
 		event.Veto();
 		return;
 	}
 	//This event is only generated programatically,
 	// we do not have to handle the direct deletion.
 
-	listLastRefresh->DeleteAllItems();
 }
 
 void MainWindowFrame::OnTreeBeginLabelEdit(wxTreeEvent &event)
 {
-	if(visControl.isRefreshing() )
+	if(refreshThreadActive() )
 	{
+		ASSERT(false);
 		event.Veto();
 		return;
 	}
@@ -3256,7 +3171,7 @@ void MainWindowFrame::OnTreeEndLabelEdit(wxTreeEvent &event)
 
 	//There is a case where the tree doesn't quite clear
 	//when there is an editor involved.
-	if(visControl.numFilters())
+	if(visControl.state.treeState.size())
 	{
 		std::string s;
 		s=stlStr(event.GetLabel());
@@ -3267,25 +3182,23 @@ void MainWindowFrame::OnTreeEndLabelEdit(wxTreeEvent &event)
 				return;
 			
 			//If the string has been changed, then we need to update	
-			if(visControl.setFilterString(filterId,s))
-			{
-				//We need to reupdate the scene, in order to re-fill the 
-				//spectra list box
-				doSceneUpdate();
-			}
+			visControl.state.treeState.setFilterString(filterId,s);
+			//We need to reupdate the scene, in order to re-fill the 
+			//spectra list box
+			doSceneUpdate();
 		}
 		else
 		{
 			event.Veto(); // Disallow blank strings.
 		}
 	}
-
 }
 
 void MainWindowFrame::OnTreeBeginDrag(wxTreeEvent &event)
 {
-	if(visControl.isRefreshing() )
+	if(refreshThreadActive() )
 	{
+		ASSERT(false); //shouldn't happen (should lock), but might
 		event.Veto();
 		return;
 	}
@@ -3331,7 +3244,7 @@ void MainWindowFrame::OnBtnFilterTreeErrs(wxCommandEvent &event)
 
 	//Grab the error strings
 	vector<FILTERTREE_ERR> res;
-	visControl.getAnalysisResults(res);
+	visControl.state.treeState.getAnalysisResults(res);
 
 	ASSERT(res.size());
 
@@ -3409,15 +3322,11 @@ void MainWindowFrame::OnTreeKeyDown(wxKeyEvent &event)
 			wxTreeItemId parent = treeFilters->GetItemParent(id);
 			wxTreeItemData *parentData=treeFilters->GetItemData(parent);
 
-			//Ask viscontrol to ensure that the parent stays persistently
-			// visible when next rebuilding the tree control
-			visControl.setWxTreeFilterViewPersistence(
-					((wxTreeUint*)parentData)->value);	
 
 			//Tree data contains unique identifier for vis control to do matching
 			wxTreeItemData *tData=treeFilters->GetItemData(id);
 			//Remove the item from the Tree 
-			visControl.removeFilterSubtree(((wxTreeUint *)tData)->value);
+			visControl.state.treeState.removeFilterSubtree(((wxTreeUint *)tData)->value);
 			//Clear property grid
 			gridFilterPropGroup->Clear();
 			if(parent !=treeFilters->GetRootItem())
@@ -3427,7 +3336,7 @@ void MainWindowFrame::OnTreeKeyDown(wxKeyEvent &event)
 				//Ensure that the parent stays visible 
 				visControl.setWxTreeFilterViewPersistence(
 						((wxTreeUint*)parentData)->value);
-				visControl.updateWxTreeCtrl(treeFilters);
+				updateWxTreeCtrl(treeFilters);
 
 				
 				//OK, so those old Id s are no longer valid,
@@ -3444,12 +3353,11 @@ void MainWindowFrame::OnTreeKeyDown(wxKeyEvent &event)
 			else
 			{
 				if(parent.IsOk())
-					visControl.updateWxTreeCtrl(treeFilters);
+					updateWxTreeCtrl(treeFilters);
 			}
 	
 			//Force a scene update, independent of if autoUpdate is enabled. 
 			doSceneUpdate();	
-		
 			break;
 		}
 		default:
@@ -3460,12 +3368,11 @@ void MainWindowFrame::OnTreeKeyDown(wxKeyEvent &event)
 
 void MainWindowFrame::OnGridFilterPropertyChange(wxPropertyGridEvent &event)
 {
-
 	//Silence error mesages
 	// we will handle validation in the backend
 	event.SetValidationFailureBehavior(0);
 	
-	if(programmaticEvent || currentlyUpdatingScene || visControl.isRefreshing())
+	if(programmaticEvent || currentlyUpdatingScene || refreshThreadActive())
 	{
 		event.Veto();
 		return;
@@ -3493,7 +3400,7 @@ void MainWindowFrame::OnGridFilterPropertyChange(wxPropertyGridEvent &event)
 
 	//Try to apply the new value
 	bool needUpdate;
-	if(!visControl.setFilterProperty(filterId,
+	if(!visControl.state.treeState.setFilterProperty(filterId,
 				key,newValue,needUpdate))
 	{
 		event.Veto();
@@ -3508,21 +3415,28 @@ void MainWindowFrame::OnGridFilterPropertyChange(wxPropertyGridEvent &event)
 		clearWxTreeImages(treeFilters);
 
 	//See wx bug #16222 - cannot modify a property grid's contents
-	// from a change event. Must work in a side-objectm then swap
+	// from a change event. Must work in a side-object then swap
 	//--
 	backFilterPropGrid= new wxPropertyGrid(filterPropertyPane,ID_GRID_FILTER_PROPERTY,
 					wxDefaultPosition,wxDefaultSize,PROPERTY_GRID_STYLE);
 	backFilterPropGrid->SetExtraStyle(PROPERTY_GRID_EXTRA_STYLE);
 
-	
+
 	visControl.updateFilterPropGrid(backFilterPropGrid,filterId,
 			stlStr(gridFilterPropGroup->SaveEditableState()));
 
+
+	int columnPos = gridFilterPropGroup->GetSplitterPosition();
+	
+
 	std::swap(backFilterPropGrid,gridFilterPropGroup);
 	do_filtergrid_prop_layout();
+	//Restore the original splitter position
+	gridFilterPropGroup->SetSplitterPosition(columnPos);
 	//--
 
 	programmaticEvent=false;
+	
 }
 
 void MainWindowFrame::OnGridFilterDClick(wxPropertyGridEvent &event)
@@ -3557,7 +3471,7 @@ void MainWindowFrame::OnGridCameraPropertyChange(wxPropertyGridEvent &event)
 		}
 		else
 		{
-			//So wx makes life har dhere. We need to do a dance to get the selection
+			//So wx makes life hard here. We need to do a dance to get the selection
 			// as a string
 			unsigned int ul;
 			ul=ll.ToLong();
@@ -3597,9 +3511,9 @@ void MainWindowFrame::OnGridCameraPropertyChange(wxPropertyGridEvent &event)
 	cameraId = l->value;
 
 	//Set property
-	visControl.setCamProperties(cameraId,key,newValue);
+	visControl.setCamProperty(cameraId,key,newValue);
 
-	//FIXME :Need to send the ne grid, not the old, due to wx bug
+	//FIXME :Need to send the new grid, not the old, due to wx bug
 	//See wx bug #16222 - cannot modify a property grid's contents
 	// from a change event. Must work in a side-objectm then swap
 	//--
@@ -3639,7 +3553,7 @@ void MainWindowFrame::OnComboCameraEnter(wxCommandEvent &event)
 	camName=stlStr(comboCamera->GetValue());
 
 	//Disallow cameras with no name
-	if (!camName.size())
+	if (camName.empty())
 		return;
 
 	//Search for the camera's position in the combo box
@@ -3653,7 +3567,7 @@ void MainWindowFrame::OnComboCameraEnter(wxCommandEvent &event)
 		//Set this camera as thew new camera
 		wxListUint *l;
 		l =(wxListUint*)  comboCamera->GetClientObject(comboCamera->GetSelection());
-		visControl.setCam(l->value);
+		visControl.setActiveCam(l->value);
 		
 		std::string s = std::string(TRANS("Restored camera: ") ) +stlStr(comboCamera->GetValue());	
 		
@@ -3666,23 +3580,24 @@ void MainWindowFrame::OnComboCameraEnter(wxCommandEvent &event)
 
 		//force redraw in 3D pane
 		panelTop->forceRedraw();
-		return ;
 	}
+	else
+	{
 
-	//Create a new camera for the scene.
-	unsigned int u=visControl.addCam(camName);
-
-	//Do not delete as this will be deleted by wx.
-	comboCamera->Append(comboCamera->GetValue(),(wxClientData *)new wxListUint(u));	
-
-	std::string s = std::string(TRANS("Stored camera: " )) +stlStr(comboCamera->GetValue());	
-	statusMessage(s.c_str(),MESSAGE_INFO);
+		//Create a new camera for the scene.
+		visControl.state.addCam(camName,true);
+		
+		std::string s = std::string(TRANS("Stored camera: " )) +
+						stlStr(comboCamera->GetValue());	
+		statusMessage(s.c_str(),MESSAGE_INFO);
 
-	visControl.setCam(u);
-	visControl.updateCameraPropGrid(gridCameraProperties,u);
-	panelTop->forceRedraw();
+		visControl.updateCameraComboBox(comboCamera);
+		visControl.updateCameraPropGrid(gridCameraProperties,
+			visControl.state.getActiveCam());
+		panelTop->forceRedraw();
 
-	setSaveStatus();
+		setSaveStatus();
+	}
 }
 
 void MainWindowFrame::OnComboCamera(wxCommandEvent &event)
@@ -3690,8 +3605,7 @@ void MainWindowFrame::OnComboCamera(wxCommandEvent &event)
 	//Set the active camera
 	wxListUint *l;
 	l =(wxListUint*)  comboCamera->GetClientObject(comboCamera->GetSelection());
-	visControl.setCam(l->value);
-
+	visControl.setActiveCam(l->value);
 
 
 	visControl.updateCameraPropGrid(gridCameraProperties,l->value);
@@ -3702,7 +3616,6 @@ void MainWindowFrame::OnComboCamera(wxCommandEvent &event)
 	panelTop->forceRedraw();
 	
 	setSaveStatus();
-	return ;
 }
 
 void MainWindowFrame::OnComboCameraSetFocus(wxFocusEvent &event)
@@ -3741,9 +3654,12 @@ void MainWindowFrame::OnComboStashSetFocus(wxFocusEvent &event)
 
 void MainWindowFrame::OnComboFilterEnter(wxCommandEvent &event)
 {
-	if(currentlyUpdatingScene || visControl.isRefreshing())
+	if(currentlyUpdatingScene || refreshThreadActive())
+	{
+		ASSERT(false); //this should not happen
 		return;
-	
+	}
+
 	OnComboFilter(event);
 }
 
@@ -3777,7 +3693,9 @@ void MainWindowFrame::OnComboFilter(wxCommandEvent &event)
 	size_t filterType;
 	filterType=filterMap[stlStr(s)];
 
+
 	ASSERT(stlStr(s) == TRANS(comboFilters_choices[filterType]));
+	Filter *f;
 	switch(comboFiltersTypeMapping[filterType])
 	{
 		case FILTER_TYPE_RANGEFILE:
@@ -3794,7 +3712,7 @@ void MainWindowFrame::OnComboFilter(wxCommandEvent &event)
 			}
 
 			//Load rangefile &  construct filter
-			Filter*f=configFile.getDefaultFilter(FILTER_TYPE_RANGEFILE);
+			f=configFile.getDefaultFilter(FILTER_TYPE_RANGEFILE);
 			std::string dataFile = stlStr(wxF.GetPath());
 			RangeFileFilter *r = (RangeFileFilter*)f;
 			r->setRangeFilename(dataFile);
@@ -3814,25 +3732,17 @@ void MainWindowFrame::OnComboFilter(wxCommandEvent &event)
 				haveErr=true;
 				break;
 			}
-			
-			visControl.addFilter(f,false,filterId);
 
-			//Rebuild tree control
-			visControl.updateWxTreeCtrl(treeFilters,f);
+
 			break;
 		}
 		default:
 		{
-			Filter *t;
 		
 			ASSERT(filterType < FILTER_TYPE_ENUM_END);
 			//Generate the appropriate filter
-			t=configFile.getDefaultFilter(comboFiltersTypeMapping[filterType]);
-			//Add the filter to viscontrol
-			visControl.addFilter(t,false,filterId);
+			f=configFile.getDefaultFilter(comboFiltersTypeMapping[filterType]);
 
-			//Rebuild tree control
-			visControl.updateWxTreeCtrl(treeFilters,t);
 		}
 	
 	}
@@ -3845,12 +3755,21 @@ void MainWindowFrame::OnComboFilter(wxCommandEvent &event)
 		return;
 	}
 
+	//Add the filter to viscontrol
+	visControl.state.treeState.addFilter(f,false,filterId);
+	//Rebuild tree control
+	updateWxTreeCtrl(treeFilters,f);
+
 
 	if(checkAutoUpdate->GetValue())
 		doSceneUpdate();
 
 	comboFilters->SetSelection(wxNOT_FOUND);
 	comboFilters->ChangeValue(TRANS(ADD_FILTER_TEXT));
+
+	//update prop grid
+	ASSERT(!backFilterPropGrid);
+	updateFilterPropertyGrid(gridFilterPropGroup,f);
 	
 }
 
@@ -3861,10 +3780,8 @@ bool MainWindowFrame::doSceneUpdate()
 
 	//Suspend the update timer, and start the progress timer
 	updateTimer->Stop();
-	progressTimer->Start(PROGRESS_TIMER_DELAY);		
 	currentlyUpdatingScene=true;
 	haveAborted=false;
-	panelTop->currentScene.setShowProgress(true);
 
 		
 	statusMessage("",MESSAGE_NONE);
@@ -3879,30 +3796,58 @@ bool MainWindowFrame::doSceneUpdate()
 
 	//Set focus on the main frame itself, so that we can catch escape key presses
 	SetFocus();
-
 	wxBusyCursor busyCursor;
-	unsigned int errCode=visControl.refreshFilterTree();
+	//reset the progress timer animation
+	visControl.scene.resetProgressAnim();
 
-	progressTimer->Stop();
-	updateTimer->Start(UPDATE_TIMER_DELAY);
+	ASSERT(!refreshControl);
+	refreshControl = new RefreshController(visControl.state.treeState);
+	refreshThread=new RefreshThread(this,refreshControl);
+	progressTimer->Start(PROGRESS_TIMER_DELAY);
+
+	refreshThread->Create();
+	refreshThread->Run();
+
+	return true;
+}
+
+void MainWindowFrame::updateWxTreeCtrl( wxTreeCtrl *t, const Filter *f)
+{
+	programmaticEvent=true;
+
+	//This routines causes, (during the call..) wx to process the tree
+	// selection code. we have to block the selection processing
+	// with the programmaticEvent var
+	visControl.updateWxTreeCtrl(t,f);
+	programmaticEvent=false;
+}
+
+
+void MainWindowFrame::finishSceneUpdate(unsigned int errCode)
+{
+	ASSERT(refreshThread);
 
 	//If there was an error, then
 	//display it	
 	if(errCode)
 	{
-		ProgressData p;
-		p=visControl.getProgress();
+		const ProgressData &p=refreshControl->curProg;
 
-		statusTimer->Start(STATUS_TIMER_DELAY,wxTIMER_ONE_SHOT);
+		statusTimer->Start(STATUS_TIMER_DELAY);
 		if(errCode)
 		{
 			std::string errString;
-			if(errCode <FILTERTREE_REFRESH_ERR_BEGIN)
+			//FIXME: This is a hack where we use the numerical value to encode the error's source. 
+			//We should not do this, but instead replace the errCode with an error object that contains both code, object and some way to extract the string 
+			if(errCode == FILTER_ERR_ABORT)
+			{
+				errString = TRANS("Refresh Aborted.");
+				MainFrame_statusbar->SetStatusText("",1);
+			}
+			else if(errCode <FILTERTREE_REFRESH_ERR_BEGIN)
 			{
 				if(p.curFilter)
 					errString = p.curFilter->getErrString(errCode);
-				else
-					errString = TRANS("Refresh Aborted.");
 			}
 			else 
 			{
@@ -3914,13 +3859,14 @@ bool MainWindowFrame::doSceneUpdate()
 
 	
 	}
-
-	//Call the progress one more time, in order to ensure that user sees "100%"
-	if(!errCode)
+	else
+	{
+		visControl.updateScene(refreshControl);
 		updateProgressStatus();
+	}
+
 	
 	currentlyUpdatingScene=false;
-	visControl.resetProgress();
 
 	//Restore the UI elements to their interactive state
 	setLockUI(false);
@@ -3928,10 +3874,8 @@ bool MainWindowFrame::doSceneUpdate()
 	panelTop->forceRedraw();
 	panelSpectra->Refresh(false);	
 
-	updateLastRefreshBox();
 	updateEditRangeMenu();
 
-	panelTop->currentScene.setShowProgress(false);
 
 	//Add (or hide) a little "Star" to inform the user there is some info available
 	if(textConsoleOut->IsEmpty() || noteDataView->GetSelection()==NOTE_CONSOLE_PAGE_OFFSET)
@@ -3956,15 +3900,59 @@ bool MainWindowFrame::doSceneUpdate()
 	//Force a paint update for the scene
 	panelTop->forceRedraw();
 
-	//Return a value dependant upon whether we successfully loaded 
-	//the data or not
-	return errCode == 0;
+}
+
+void MainWindowFrame::OnFinishRefreshThread(wxCommandEvent &event)
+{
+	ASSERT(refreshControl);
+	//The tree itself should not be refreshing once the thread has completed.
+	ASSERT(!visControl.state.treeState.isRefreshing());
+	progressTimer->Stop();
+
+	vector<std::pair<const Filter*, std::string> > consoleMessages;
+	consoleMessages=refreshControl->getConsoleMessages();
+
+	const Filter *lastFilter =0;	
+	for(size_t ui=0; ui<consoleMessages.size();ui++)
+	{
+		if(lastFilter!=consoleMessages[ui].first)
+		{
+			lastFilter=consoleMessages[ui].first;
+			textConsoleOut->AppendText("-------------\n");
+			textConsoleOut->AppendText(consoleMessages[ui].first->getUserString() + "\n");
+			textConsoleOut->AppendText("-------------\n");
+		}
+		
+		textConsoleOut->AppendText(consoleMessages[ui].second + "\n");	
+	}
+	textConsoleOut->AppendText("\n");	
+
+
+	finishSceneUpdate((unsigned int)event.GetInt());
+
+	//First wait for the refresh thread to terminate
+	refreshThread->Wait();
+	delete refreshThread;
+	refreshThread=0;
+
+	delete refreshControl;
+	refreshControl=0;
+
+	if(!event.GetInt())
+	{
+		//Set the progress string to complete, if no error
+		MainFrame_statusbar->SetStatusText("",0);
+		MainFrame_statusbar->SetStatusText(TRANS("Complete"),1);
+		MainFrame_statusbar->SetStatusText("",2);
+	}
+	//restart the update timer, to check for updates from the backend
+	updateTimer->Start(UPDATE_TIMER_DELAY);
 }
 
 void MainWindowFrame::setFilterTreeAnalysisImages()
 {
 	vector<FILTERTREE_ERR> lastErrs;
-	visControl.getAnalysisResults(lastErrs);
+	visControl.state.treeState.getAnalysisResults(lastErrs);
 
 	//Show the error button if required
 	btnFilterTreeErrs->Show(!lastErrs.empty());
@@ -4007,12 +3995,16 @@ void MainWindowFrame::setFilterTreeAnalysisImages()
 		severityIconMapping[ANALYSE_SEVERITY_WARNING] =wxART_WARNING;
 
 		for(map<const Filter*,unsigned int>::const_iterator it=severityMapping.begin();it!=severityMapping.end(); ++it)
-			iconSettings[visControl.getIdByFilter(it->first)] = severityIconMapping[it->second];
+		{
+			size_t id;
+			id=visControl.state.treeState.getIdByFilter(it->first);
+			iconSettings[id] = severityIconMapping[it->second];
+		}
 	}
 
 	//apply the filter->icon mapping
 	setWxTreeImages(treeFilters,iconSettings);
-	
+
 }
 
 void MainWindowFrame::OnStatusBarTimer(wxTimerEvent &event)
@@ -4052,6 +4044,7 @@ void MainWindowFrame::OnProgressTimer(wxTimerEvent &event)
 
 void MainWindowFrame::OnAutosaveTimer(wxTimerEvent &event)
 {
+
 	//Save a state file to the configuration dir
 	//with the title "autosave.xml"
 	//
@@ -4071,10 +4064,10 @@ void MainWindowFrame::OnAutosaveTimer(wxTimerEvent &event)
 	s=  stlStr(filePath);
 
 	//Only save if we have autosave data
-	if(visControl.hasStateData())
+	if(visControl.state.hasStateData())
 	{
 		std::map<string,string> dummyMap;
-		if(visControl.saveState(s.c_str(),dummyMap,false,false))
+		if(visControl.state.save(s.c_str(),dummyMap,false))
 			statusMessage(TRANS("Autosave complete."),MESSAGE_INFO);
 		else
 		{
@@ -4083,8 +4076,6 @@ void MainWindowFrame::OnAutosaveTimer(wxTimerEvent &event)
 				wxRemoveFile(filePath);
 		}
 	}
-
-
 }
 
 void MainWindowFrame::OnUpdateTimer(wxTimerEvent &event)
@@ -4098,12 +4089,12 @@ void MainWindowFrame::OnUpdateTimer(wxTimerEvent &event)
 	treeFilters->GetParent()->Layout();
 	#endif
 
-	if(requireFirstUpdate)
+	if(requireFirstUpdate && !refreshThreadActive())
 	{
 		//If we are using the default camera,
 		//move it to make sure that it is visible
-		if(visControl.numCams() == 1)
-			visControl.ensureSceneVisible(3);
+		if(visControl.state.getNumCams() == 1)
+			visControl.scene.ensureVisible(3);
 
 
 		doSceneUpdate();
@@ -4126,16 +4117,23 @@ void MainWindowFrame::OnUpdateTimer(wxTimerEvent &event)
 	//in the process of refreshing.
 	//Don't attempt to update if already updating, or last
 	//update aborted
-	bool visUpdates=visControl.hasUpdates();
+	bool visUpdates=visControl.state.treeState.hasUpdates();
 
 	//I can has updates?
-	if((visUpdates || plotUpdates) && !visControl.isRefreshing())
+	if((visUpdates || plotUpdates) && !refreshThreadActive())
 	{
-		//FIXME: This is a massive hack. Use proper feedback to determine
-		//the correct thing to update, rather than nuking everything
-		//from orbit
+		if(visUpdates)
+			visControl.state.treeState.applyBindingsToTree();	
+
 		if(plotUpdates)
-			visControl.clearCacheByType(FILTER_TYPE_RANGEFILE);
+		{
+			//FIXME: Hack. Rather than simply clearing the
+			//cache globally, consider actually working out
+			//which filter had the update, and refreshing that
+			//filter only. Here we assume that only Rangefiles
+			//can trigger an update
+			visControl.state.treeState.clearCacheByType(FILTER_TYPE_RANGEFILE);
+		}
 
 		doSceneUpdate();
 	}
@@ -4146,7 +4144,7 @@ void MainWindowFrame::OnUpdateTimer(wxTimerEvent &event)
 	{
 		//Use the current combobox value to determine which camera is the 
 		//current camera in the property grid
-		visControl.getCameraUpdates();
+		visControl.transferSceneCameraToState();
 			
 		int n = comboCamera->FindString(comboCamera->GetValue());
 
@@ -4255,93 +4253,107 @@ void MainWindowFrame::showStatusMessage(const char *message, unsigned int type)
 		default:
 			ASSERT(false);
 	}
-	#endif
-	
+#endif
+
 	MainFrame_statusbar->SetStatusText((message),0);
 }
 
 void MainWindowFrame::updateProgressStatus()
 {
+	//we can get some "left over" events that are queued but not processed
+	// from the main thread
+	if(!refreshThreadActive())
+		return;
 
 	std::string progressString,filterProg;
 
-
-	if(!visControl.numFilters())
+	//If we have no tree, don't update the progress
+	if(!visControl.state.treeState.size())
 		return;
 
+
+	//Request a panel refresh, so we update the opengl spinner
+	panelTop->Refresh();
+
+	//The refresh should still be present if we are using this function
 	if(haveAborted)
 	{
-		progressString=TRANS("Aborted.");
+		progressString=TRANS("Aborting....");
+		progressTimer->Stop(); //Supress any future events
+		visControl.scene.progressCircle.setMaxStep(0);
 	}
 	else
 	{
 		//Check for new progress data
-		ProgressData p;
-		p=visControl.getProgress();
+		const ProgressData &p=refreshControl->curProg;
+		ASSERT(refreshControl->curProg.filterProgress <=100);
 
 		if(p == lastProgressData
-			|| !p.maxStep)
+
+				|| !p.maxStep)
 			return;
-		lastProgressData=p;
 
+		//This shouldn't happen, but prevent >100% progress from being reported
+		unsigned int cappedProgress;
+		cappedProgress = std::min(p.filterProgress,(unsigned int)100);
+
+		//Inform progress circle in scene about current progress
+		visControl.scene.progressCircle.setCurFilter(p.totalProgress);
+		visControl.scene.progressCircle.setMaxStep(p.maxStep);
+		visControl.scene.progressCircle.setNumFilters(p.totalNumFilters);
+		visControl.scene.progressCircle.setProgress(cappedProgress);
+		visControl.scene.progressCircle.setStep(p.step);
+
+
+		lastProgressData=p;
 
 		//Update the text progress
 		{
-		ASSERT(p.totalProgress <= visControl.numFilters());
-		
-		if(p.filterProgress > 100)
-			p.filterProgress=100;
-	
-		//Create a string from the total and percentile progresses
-		std::string totalProg,totalCount,step,maxStep;
-		stream_cast(totalProg,p.totalProgress);
-		stream_cast(filterProg,p.filterProgress);
-		stream_cast(totalCount,p.totalNumFilters);
+			ASSERT(p.totalProgress <= visControl.state.treeState.size());
 
+			//Create a string from the total and percentile progresses
+			std::string totalProg,totalCount,step,maxStep;
+			stream_cast(filterProg,cappedProgress);
+			stream_cast(totalProg,p.totalProgress);
+			stream_cast(totalCount,p.totalNumFilters);
 
-		stream_cast(step,p.step);
-		stream_cast(maxStep,p.maxStep);
 
-		ASSERT(p.step <=p.maxStep);
-		
-		if(p.curFilter)
-		{
-			if(!p.maxStep)
-				progressString = totalProg+TRANS(" of ") + totalCount +
-						 " (" + p.curFilter->typeString() +")";
-			else
+			stream_cast(step,p.step);
+			stream_cast(maxStep,p.maxStep);
+
+			ASSERT(p.step <=p.maxStep);
+
+			if(p.curFilter)
 			{
-				progressString = totalProg+TRANS(" of ") + totalCount +
-						 " (" + p.curFilter->typeString() + ", "
-							+ step + "/" + maxStep + ": " + 
-						       p.stepName+")";
+				if(!p.maxStep)
+					progressString = totalProg+TRANS(" of ") + totalCount +
+						" (" + p.curFilter->typeString() +")";
+				else
+				{
+					progressString = totalProg+TRANS(" of ") + totalCount +
+						" (" + p.curFilter->typeString() + ", "
+						+ step + "/" + maxStep + ": " + 
+						p.stepName+")";
+				}
 			}
-		}
-		else
-		{
-			//If we have no filter, then we must be done if the totalProgress is
-			//equal to the total count.
-			if(totalProg == totalCount)
-				progressString = TRANS("Updated.");
 			else
-				progressString = totalProg + TRANS(" of ") + totalCount;
-		}
-
+			{
+				//If we have no filter, then we must be done if the totalProgress is
+				//equal to the total count.
+				if(totalProg == totalCount)
+					progressString = TRANS("Updated.");
+				else
+					progressString = totalProg + TRANS(" of ") + totalCount;
+			}
 
-		if( p.filterProgress != 100)
-			filterProg+=TRANS("\% Done (Esc aborts)");
-		else
-			filterProg+=TRANS("\% Done");
-		}
 
-		//Update the scene progress icon
-		if(panelTop->currentScene.getShowProgress())
-		{
-			wxPaintEvent wxP;
-			wxPostEvent(panelTop,wxP);
+			//Show the abort notice if we have hit 100%
+			if( p.filterProgress != 100 && p.filterProgress < p.totalNumFilters)
+				filterProg+=TRANS("\% Done (Esc aborts)");
+			else
+				filterProg+=TRANS("\% Done");
 		}
 
-
 	}
 
 	MainFrame_statusbar->SetBackgroundColour(wxNullColour);
@@ -4353,7 +4365,7 @@ void MainWindowFrame::updateProgressStatus()
 
 void MainWindowFrame::updatePostEffects()
 {
-	panelTop->currentScene.clearEffects();
+	visControl.scene.clearEffects();
 
 	//Do we need post-processing?
 #ifndef APPLE_EFFECTS_WORKAROUND
@@ -4411,13 +4423,13 @@ void MainWindowFrame::updatePostEffects()
 		//Send the effect to the scene
 		if(b->willDoSomething())
 		{
-			panelTop->currentScene.addEffect(b);
-			panelTop->currentScene.setEffects(true);
+			visControl.scene.addEffect(b);
+			visControl.scene.setEffects(true);
 
 
 			//Update the dx,dy and dz boxes
 			BoundCube bcTmp;
-			bcTmp=panelTop->currentScene.getBound();
+			bcTmp=visControl.scene.getBound();
 
 			b->getCroppedBounds(bcTmp);	
 
@@ -4474,7 +4486,7 @@ void MainWindowFrame::updatePostEffects()
 
 		anaglyph->setBaseShift(shift);
 		anaglyph->setFlip(checkFxStereoLensFlip->IsChecked());
-		panelTop->currentScene.addEffect(anaglyph);
+		visControl.scene.addEffect(anaglyph);
 	}
 
 	panelTop->forceRedraw();
@@ -4562,17 +4574,19 @@ void MainWindowFrame::updateFxUI(const vector<const Effect*> &effs)
 		noteFxPanelCrop->Enable();
 		noteFxPanelStereo->Enable();
 #endif
-		visControl.setEffects(true);
+	
+		visControl.scene.setEffects(true);
 	}
 	
 
 	Thaw();
 }
 
+//This routine is used by other UI processes to trigger an abort
 void MainWindowFrame::OnProgressAbort(wxCommandEvent &event)
 {
 	if(!haveAborted)
-		visControl.abort();
+		visControl.state.treeState.setAbort();
 	haveAborted=true;
 }
 
@@ -4595,14 +4609,14 @@ void MainWindowFrame::OnButtonRefresh(wxCommandEvent &event)
 	if(!gridCameraProperties || !gridFilterPropGroup)
 		return;
 
-	if(currentlyUpdatingScene || visControl.isRefreshing())
+	if(currentlyUpdatingScene || refreshThreadActive())
 		return;
 
 	//dirty hack to get keyboard state.
 	wxMouseState wxm = wxGetMouseState();
 	if(wxm.ShiftDown())
 	{
-		visControl.purgeFilterCache();
+		visControl.state.treeState.purgeFilterCache();
 	}
 	else
 	{
@@ -4610,7 +4624,6 @@ void MainWindowFrame::OnButtonRefresh(wxCommandEvent &event)
 			statusMessage(TRANS("Tip: You can shift-click to force full refresh, if required"),MESSAGE_HINT);
 	}
 	doSceneUpdate();	
-	
 }
 
 void MainWindowFrame::OnRawDataUnsplit(wxSplitterEvent &event)
@@ -4678,14 +4691,14 @@ void MainWindowFrame::OnButtonGridSave(wxCommandEvent &event)
 
 void MainWindowFrame::OnCheckAlpha(wxCommandEvent &event)
 {
-	panelTop->currentScene.setAlpha(event.IsChecked());
+	visControl.scene.setAlpha(event.IsChecked());
 
 	panelTop->forceRedraw();
 }
 
 void MainWindowFrame::OnCheckLighting(wxCommandEvent &event)
 {
-	panelTop->currentScene.setLighting(event.IsChecked());
+	visControl.scene.setLighting(event.IsChecked());
 	
 	panelTop->forceRedraw();
 }
@@ -4693,11 +4706,13 @@ void MainWindowFrame::OnCheckLighting(wxCommandEvent &event)
 void MainWindowFrame::OnCheckCacheEnable(wxCommandEvent &event)
 {
 	if(event.IsChecked())
-		visControl.setCachePercent((unsigned int)spinCachePercent->GetValue());
+	{
+		visControl.state.treeState.setCachePercent((unsigned int)spinCachePercent->GetValue());
+	}
 	else
 	{
-		visControl.setCachePercent(0);
-		visControl.purgeFilterCache();
+		visControl.state.treeState.setCachePercent(0);
+		visControl.state.treeState.purgeFilterCache();
 
 		doSceneUpdate();
 	}
@@ -4705,7 +4720,7 @@ void MainWindowFrame::OnCheckCacheEnable(wxCommandEvent &event)
 
 void MainWindowFrame::OnCheckWeakRandom(wxCommandEvent &event)
 {
-	visControl.setStrongRandom(!event.IsChecked());
+	Filter::setStrongRandom(!event.IsChecked());
 
 	doSceneUpdate();
 }
@@ -4772,7 +4787,7 @@ void MainWindowFrame::OnCacheRamUsageSpin(wxSpinEvent &event)
 {
 	ASSERT(event.GetPosition() >= 0 &&event.GetPosition()<=100);
 
-	visControl.setCachePercent(event.GetPosition());
+	visControl.state.treeState.setCachePercent(event.GetPosition());
 	
 }
 void MainWindowFrame::OnButtonRemoveCam(wxCommandEvent &event)
@@ -4792,7 +4807,8 @@ void MainWindowFrame::OnButtonRemoveCam(wxCommandEvent &event)
 	{
 		wxListUint *l;
 		l =(wxListUint*)  comboCamera->GetClientObject(n);
-		visControl.removeCam(l->value);
+
+		visControl.state.removeCam(l->value);
 		comboCamera->Delete(n);
 		
 		programmaticEvent=true;
@@ -4809,8 +4825,10 @@ void MainWindowFrame::OnSpectraListbox(wxCommandEvent &event)
 {
 	//This function gets called programatically by 
 	//doSceneUpdate. Prevent interaction.
-	if(visControl.isRefreshing())
+	if(refreshThreadActive())
 		return;
+
+
 	//Get the currently selected item
 	//Spin through the selected items
 	for(unsigned int ui=0;ui<plotList->GetCount(); ui++)
@@ -4832,12 +4850,11 @@ void MainWindowFrame::OnSpectraListbox(wxCommandEvent &event)
 
 void MainWindowFrame::OnClose(wxCloseEvent &event)
 {
-
-	if(visControl.isRefreshing())
+	if(refreshThreadActive())
 	{
 		if(!haveAborted)
 		{
-			visControl.abort();
+			refreshThread->abort();
 			haveAborted=true;
 
 			statusMessage(TRANS("Aborting..."),MESSAGE_INFO);
@@ -4862,7 +4879,7 @@ void MainWindowFrame::OnClose(wxCloseEvent &event)
 		// as we can't abort it anyway.
 		if(event.CanVeto())
 		{
-			if(visControl.stateModifyLevel() >= STATE_MODIFIED_ANCILLARY)
+			if(visControl.stateIsModified()) 
 			{
 				//Prompt for close
 				wxMessageDialog wxD(this,
@@ -4878,8 +4895,6 @@ void MainWindowFrame::OnClose(wxCloseEvent &event)
 		}
 	}
 	
-	
-
 	//Remove the autosave file if it exists, as we are shutting down neatly.
 
 	//Get self PID
@@ -4948,6 +4963,7 @@ void MainWindowFrame::OnClose(wxCloseEvent &event)
 
 void MainWindowFrame::OnCheckPostProcess(wxCommandEvent &event)
 {
+
 #ifdef APPLE_EFFECTS_WORKAROUND
 	//FIXME: I have disabled this under apple
 	ASSERT(false);
@@ -4955,7 +4971,7 @@ void MainWindowFrame::OnCheckPostProcess(wxCommandEvent &event)
 	//Disable the entire UI panel
 	noteFxPanelCrop->Enable(event.IsChecked());
 	noteFxPanelStereo->Enable(event.IsChecked());
-	visControl.setEffects(event.IsChecked());
+	visControl.scene.setEffects(event.IsChecked());
 	updatePostEffects();
 		
 	setSaveStatus();
@@ -5124,14 +5140,13 @@ void MainWindowFrame::restoreConfigDefaults()
 		stream_cast(s,configFile.getMaxPoints());
 
 		textLimitOutput->SetValue((s));
-
 		visControl.setIonDisplayLimit(configFile.getMaxPoints());
 	}
 
 	
 	if(configFile.getWantStartupOrthoCam())
 	{
-		visControl.setCamProperties(visControl.getActiveCamId(), 
+		visControl.state.setCamProperty(visControl.state.getActiveCam(), 
 					CAMERA_KEY_LOOKAT_PROJECTIONMODE,TRANS("Orthogonal"));
 	}
 }
@@ -5405,7 +5420,7 @@ void MainWindowFrame::checkReloadAutosave()
 				//Prevent the program from allowing save menu usage
 				//into autosave file
 				std::string tmpStr;
-				visControl.setFilename(tmpStr);
+				visControl.state.setFilename(tmpStr);
 
 				setSaveStatus();
 			}
@@ -5540,9 +5555,8 @@ void MainWindowFrame::checkReloadAutosave()
 
 void MainWindowFrame::setSaveStatus()
 {
-	fileSave->Enable(
-		(visControl.stateModifyLevel() >=STATE_MODIFIED_ANCILLARY)
-			&& visControl.getFilename().size());
+	fileSave->Enable( visControl.stateIsModified() && 
+			visControl.state.getFilename().size());
 }
 
 wxSize MainWindowFrame::getNiceWindowSize() const
@@ -5637,13 +5651,11 @@ void MainWindowFrame::set_properties()
     // end wxGlade
     //
 
-    PlotWrapper *p=new PlotWrapper; //plotting handler
-    panelSpectra->setPlotWrapper(p);
+    panelSpectra->setPlotWrapper(visControl.getPlotWrapper(),false);
     
     //Set the controls that the viscontrol needs to interact with
-    visControl.setScene(&panelTop->currentScene); //GL scene
+   //TODO: Require these via the constructor ?
     visControl.setRawGrid(gridRawData); //Raw data grid
-    visControl.setPlotWrapper(p);
     visControl.setPlotList(plotList);
     visControl.setConsole(textConsoleOut);
 
@@ -5694,8 +5706,6 @@ void MainWindowFrame::do_layout()
     filterPaneSizer->Add(filteringLabel, 0, 0, 0);
     filterMainCtrlSizer->Add(comboFilters, 0, wxLEFT|wxRIGHT|wxEXPAND, 4);
     filterMainCtrlSizer->Add(treeFilters, 3, wxLEFT|wxBOTTOM|wxEXPAND, 3);
-    filterMainCtrlSizer->Add(lastRefreshLabel, 0, wxTOP, 8);
-    filterMainCtrlSizer->Add(listLastRefresh, 1, wxBOTTOM|wxEXPAND, 5);
     filterTreeLeftRightSizer->Add(filterMainCtrlSizer, 3, wxEXPAND, 0);
     filterRightOfTreeSizer->Add(checkAutoUpdate, 0, 0, 0);
     filterRightOfTreeSizer->Add(10, 10, 0, 0, 0);
diff --git a/src/gui/mainFrame.h b/src/gui/mainFrame.h
index 57b73f2..df8142e 100644
--- a/src/gui/mainFrame.h
+++ b/src/gui/mainFrame.h
@@ -1,6 +1,6 @@
 /*
  * 	3Depict.h - main program header
- * 	Copyright (C) 2013 D Haley
+ * 	Copyright (C) 2015 D Haley
  *
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -42,11 +42,14 @@
 #include "backend/viscontrol.h"
 #include "backend/configFile.h"
 
+
+
 #ifndef THREEDEPICT_H 
 #define THREEDEPICT_H
 
 
 class FileDropTarget;
+class RefreshThread;
 
 enum
 {
@@ -56,6 +59,22 @@ enum
 	MESSAGE_NONE // pseudo-message to wipe all messages
 };
 
+
+//This is used to create and run a worker thread that will perform a refresh calculation
+class RefreshThread: public wxThread
+{
+	private:
+		RefreshController *refreshControl;
+		wxWindow *targetWindow;
+	public:
+		RefreshThread(wxWindow *target,RefreshController *rc); 
+		~RefreshThread();
+		//!Used internally by wxwidgets to launch thread
+		void *Entry();
+
+		void abort() {ASSERT(false);}
+};
+
 class MainWindowFrame: public wxFrame {
 public:
     // begin wxGlade: MainWindowFrame::ids
@@ -86,7 +105,9 @@ private:
     void do_filtergrid_prop_layout();
     //Force a re-layout of the camera property grid
     void do_cameragrid_prop_layout();
-   
+  
+	bool refreshThreadActive() { return refreshThread && refreshThread->IsRunning();};
+ 
    	//!Queue up a status message for display
     	void showStatusMessage(const char *message, unsigned int messageType=MESSAGE_ERROR); 
    	
@@ -97,6 +118,13 @@ private:
 	void updateProgressStatus();
 	//!Perform an update to the 3D Scene. Returns false if refresh failed
 	bool doSceneUpdate();
+	
+	//!Complete the scene update. Returns false if failed
+	void finishSceneUpdate(unsigned int errCode);
+
+	//!Wrapper for viscontrol's update function, as we need to
+	// prevent wx from firing events during tree update
+	void updateWxTreeCtrl( wxTreeCtrl *t, const Filter *f=0);
 
 	//!Update the post-processing effects in the 3D scene. 
 	void updatePostEffects(); 
@@ -107,7 +135,7 @@ private:
 	void setFilterTreeAnalysisImages(); 
 
 	//!Update the effects UI from some effects vector
-	void updateFxUI(const vector<const Effect *> &fx);
+	void updateFxUI(const std::vector<const Effect *> &fx);
 
 	void setLockUI(bool amlocking,unsigned int lockMode);
 
@@ -116,6 +144,11 @@ private:
 	//!Scene - user interaction interface "visualisation control"
 	VisController visControl;
 
+	//!Refresh control thread
+	RefreshThread *refreshThread;
+	//!Refresh control object
+	RefreshController *refreshControl;
+
 	//!Program on-disk configuration class
 	ConfigFile configFile;
 
@@ -147,7 +180,7 @@ private:
 	VersionCheckThread *verCheckThread;
 
 	//Map to convert filter drop down choices to IDs
-	map<std::string,size_t> filterMap;
+	std::map<std::string,size_t> filterMap;
    
 	//TODO: Refactor -  remove me.
 	// True if there are pending updates for the mahthgl window
@@ -155,11 +188,11 @@ private:
 
 	//List of pending messages to show in status bar
 	// first int is priority (eg MESSAGE_ERROR), string is message
-	list<pair<unsigned int, std::string > > statusQueue;
+	std::list<std::pair<unsigned int, std::string > > statusQueue;
 protected:
-    wxTimer *statusTimer;
-    wxTimer *progressTimer;
+    wxTimer *statusTimer; //One-shot timer that is used to clear the status bar
     wxTimer *updateTimer; //Periodically calls itself to check for updates from user interaction
+    wxTimer *progressTimer; //Periodically calls itself to refresh progress status
     wxTimer *autoSaveTimer; //Periodically calls itself to create an autosave state file
     wxMenuItem *checkMenuControlPane;
     wxMenuItem *checkMenuRawDataPane;
@@ -187,8 +220,6 @@ protected:
     wxStaticText* filteringLabel;
     wxComboBox* comboFilters;
     TextTreeCtrl* treeFilters;
-    wxStaticText* lastRefreshLabel;
-    wxListCtrl* listLastRefresh;
     wxCheckBox* checkAutoUpdate;
     wxButton* refreshButton;
     wxButton* btnFilterTreeExpand;
@@ -373,10 +404,10 @@ public:
     virtual void OnAutosaveTimer(wxTimerEvent &evt);
 
     virtual void OnCheckUpdatesThread(wxCommandEvent &evt);
+    virtual void OnFinishRefreshThread(wxCommandEvent &evt);
     virtual void OnIdle(wxIdleEvent &evt);
 
     virtual void SetCommandLineFiles(wxArrayString &files);
-    virtual void updateLastRefreshBox();
 
     //return type of file, based upon heuristic check
     static unsigned int guessFileType(const std::string &file);
diff --git a/src/gui/mathglPane.cpp b/src/gui/mathglPane.cpp
index 664c846..86d1a8a 100644
--- a/src/gui/mathglPane.cpp
+++ b/src/gui/mathglPane.cpp
@@ -1,6 +1,6 @@
 /*
  *	mathglPane.cpp - mathgl-wx interface panel control
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -32,6 +32,9 @@
 
 #include <mgl2/canvas_wnd.h>
 
+using std::string;
+using std::vector;
+
 //Panning speed modifier
 const float MGL_PAN_SPEED=2.0f;
 //Mathgl uses floating point loop computation, and can get stuck. Limit zoom precision
@@ -94,6 +97,28 @@ enum
 };
 
 
+void zoomBounds(float minV,float maxV,  float centre, 
+		float zoomFactor,float &newMin, float &newMax)
+{
+	ASSERT(minV < maxV);
+	ASSERT(minV< centre && maxV > centre);
+	ASSERT(zoomFactor > 0);
+	
+	//find deltas, then multiply them out
+	float lowerDelta,upperDelta;
+	lowerDelta = (centre-minV);
+	upperDelta = (maxV-centre);
+	upperDelta*=zoomFactor;
+	lowerDelta*=zoomFactor;
+	ASSERT(upperDelta > 0 && lowerDelta > 0);
+
+	//compute new bounds
+	newMin= centre - lowerDelta;
+	newMax= centre + upperDelta;
+
+	ASSERT(newMin <=newMax);
+}
+
 MathGLPane::MathGLPane(wxWindow* parent, int id) :
 wxPanel(parent, id,  wxDefaultPosition, wxDefaultSize)
 {
@@ -106,7 +131,6 @@ wxPanel(parent, id,  wxDefaultPosition, wxDefaultSize)
 	leftWindow=true;
 	thePlot=0;	
 	gr=0;
-	ownPlotPtr=false;
 	lastEditedPlot=lastEditedRegion=-1;
 	regionSelfUpdate=false;
 	plotIsLogarithmic=false;
@@ -117,8 +141,6 @@ wxPanel(parent, id,  wxDefaultPosition, wxDefaultSize)
 
 MathGLPane::~MathGLPane()
 {
-	if(thePlot && ownPlotPtr)
-		delete thePlot;
 	if(gr)
 		delete gr;
 }
@@ -177,11 +199,7 @@ unsigned int MathGLPane::getAxisMask(int x, int y) const
 
 void MathGLPane::setPlotWrapper(PlotWrapper *newPlot,bool takeOwnPtr)
 {
-	if(thePlot && ownPlotPtr)
-		delete thePlot;
-
 	thePlot=newPlot;
-	ownPlotPtr=takeOwnPtr;
 
 	Refresh();
 }
@@ -251,6 +269,11 @@ void MathGLPane::render(wxPaintEvent &event)
 
 	}
 
+#ifdef DEBUG
+	bool doTrap=getTrapfpe();
+	if(doTrap)
+		trapfpe(false);
+#endif
 	//If the plot has changed, been resized or is performing
 	// a mouse action that requires updating, we need to update it
 	//likewise if we don't have a plot, we need one.
@@ -282,7 +305,7 @@ void MathGLPane::render(wxPaintEvent &event)
 #ifdef DEBUG
 		if(strlen(gr->Message()))
 		{
-			cerr << "Mathgl reports error:" << gr->Message() << endl;
+			std::cerr << "Mathgl reports error:" << gr->Message() << std::endl;
 		}
 #endif
 		thePlot->resetChange();
@@ -296,6 +319,10 @@ void MathGLPane::render(wxPaintEvent &event)
 		free(rgbdata);
 	}
 
+#ifdef DEBUG
+	if(doTrap)
+		trapfpe(true);
+#endif
 	dc->DrawBitmap(wxBitmap(imageCacheBmp),0,0);
 	//If we are engaged in a dragging operation
 	//draw the nice little bits we need
@@ -410,9 +437,11 @@ void MathGLPane::updateMouseCursor()
 		return;
 
 	//Set cursor to normal by default
-	SetCursor(wxNullCursor);
 	if(!readyForInput())
+	{
+		SetCursor(wxNullCursor);
 		return;
+	}
 
 	//Update mouse cursor
 	//---------------
@@ -444,9 +473,11 @@ void MathGLPane::updateMouseCursor()
 			SetCursor(wxCURSOR_SIZEWE);
 				break;
 			case AXIS_POSITION_INTERIOR:
-				SetCursor(wxCURSOR_MAGNIFIER);
+				//SetCursor(wxCURSOR_MAGNIFIER);
+				SetCursor(wxNullCursor);
 				break;
 			default:
+				SetCursor(wxNullCursor);
 				;
 		}
 	}
@@ -735,23 +766,28 @@ void MathGLPane::mouseWheelMoved(wxMouseEvent& event)
 
 
 
-	//Bigger numbers mean faster
-	const float SCROLL_WHEEL_ZOOM_RATE=0.75;
+	//Bigger numbers mean faster. 
+	const float SCROLL_WHEEL_ZOOM_RATE=0.20;
+
 	float zoomRate=(float)event.GetWheelRotation()/(float)event.GetWheelDelta();
 	zoomRate=zoomRate*SCROLL_WHEEL_ZOOM_RATE;
 
 	//Convert from additive space to multiplicative
 	float zoomFactor;
-	if(zoomRate < 0.0f)
-		zoomFactor=-1.0f/zoomRate;
+	if(zoomRate > 0.0f)
+	{
+		zoomFactor=1.0/(1.0+zoomRate);
+		ASSERT(zoomFactor> 1.0f);
+	}
 	else
-		zoomFactor=zoomRate;
-
-
+	{
+		zoomFactor=(1.0-zoomRate);
+		ASSERT(zoomFactor < 1.0f);
+	}
 
-	ASSERT(zoomFactor >0.0f);
 
 
+	//retrieve the mouse position
 	mglPoint mousePos;
 	float mglX,mglY;
 	toPlotCoords(curMouse.x,curMouse.y,mglX,mglY);
@@ -771,11 +807,11 @@ void MathGLPane::mouseWheelMoved(wxMouseEvent& event)
 		//below x axis -> y zoom only
 		case AXIS_POSITION_LOW_X:
 		{
-			float newYMax,newYMin;
-			//Zoom along Y
-			newYMin= mousePos.y + (yMin-mousePos.y)*zoomFactor ;
-			newYMax= mousePos.y + (yMax-mousePos.y)*zoomFactor ;
-	
+			float newYMin,newYMax;
+			//work out existing bounds on zooming
+			zoomBounds(yMin,yMax,mousePos.y,
+				zoomFactor, newYMin,newYMax);
+			//clamp to plot
 			newYMin=std::max(yPlotMin,newYMin);
 			newYMax=std::min(yPlotMax,newYMax);
 		
@@ -786,11 +822,11 @@ void MathGLPane::mouseWheelMoved(wxMouseEvent& event)
 		//Below y axis -> x zoom only
 		case AXIS_POSITION_LOW_Y:
 		{
-			float newXMax,newXMin;
-			//Zoom along X
-			newXMin= mousePos.x + (xMin-mousePos.x)*zoomFactor ;
-			newXMax= mousePos.x + (xMax-mousePos.x)*zoomFactor ;
-	
+			float newXMin,newXMax;
+			//work out existing bounds on zooming
+			zoomBounds(xMin,xMax,mousePos.x,
+				zoomFactor, newXMin,newXMax);
+
 			newXMin=std::max(xPlotMin,newXMin);
 			newXMax=std::min(xPlotMax,newXMax);
 		
@@ -803,13 +839,12 @@ void MathGLPane::mouseWheelMoved(wxMouseEvent& event)
 		{
 			float newXMax,newXMin;
 			float newYMax,newYMin;
+			//work out existing bounds on zooming
+			zoomBounds(xMin,xMax,mousePos.x,
+				zoomFactor, newXMin,newXMax);
+			zoomBounds(yMin,yMax,mousePos.y,
+				zoomFactor, newYMin,newYMax);
 			
-			//Zoom along X
-			newXMin= mousePos.x + (xMin-mousePos.x)*zoomFactor ;
-			newXMax= mousePos.x + (xMax-mousePos.x)*zoomFactor ;
-			newYMin= mousePos.y + (yMin-mousePos.y)*zoomFactor ;
-			newYMax= mousePos.y + (yMax-mousePos.y)*zoomFactor ;
-	
 				
 			newXMin=std::max(xPlotMin,newXMin);
 			newXMax=std::min(xPlotMax,newXMax);
@@ -882,7 +917,7 @@ void MathGLPane::leftMouseReleased(wxMouseEvent& event)
 	{
 		for(size_t ui=0;ui<updateHandlers.size(); ui++)
 		{
-			pair<wxWindow*,UpdateHandler> u;
+			std::pair<wxWindow*,UpdateHandler> u;
 			u=updateHandlers[ui];
 
 			//Call the function
diff --git a/src/gui/mathglPane.h b/src/gui/mathglPane.h
index b98c22b..a812c2b 100644
--- a/src/gui/mathglPane.h
+++ b/src/gui/mathglPane.h
@@ -1,6 +1,6 @@
 /*
  *	mathglPanel.h - WxWidgets plotting panelf or interaction with mathgl
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@
 
 #include "backend/plot.h"
 
+#include <vector>
+
 // begin wxGlade: ::extracode
 // end wxGlade
 
@@ -43,7 +45,7 @@ private:
 	wxBitmap imageCacheBmp;	
 	
 	
-	vector<pair<wxWindow*,UpdateHandler> > updateHandlers;
+	std::vector<std::pair<wxWindow*,UpdateHandler> > updateHandlers;
 
 	//Current mouse position
 	wxPoint curMouse;
@@ -74,11 +76,9 @@ private:
 	bool limitInteract;
 
 	//!Pointer to the plot data holding class
+	// TODO: Make const, as this should be owned by the analysis state
 	PlotWrapper *thePlot;
 	
-	//!Should we take ownership of deleting the plot pointer
-	bool ownPlotPtr;
-
 	//!True if regions should update themselves
 	bool regionSelfUpdate;
 
@@ -160,7 +160,7 @@ public:
 	//Returns the ID of the last edited region
 	void getLastEdited(size_t &lastPlot,size_t &lastRegion) const { lastRegion=lastEditedRegion; lastPlot=lastEditedPlot;};
 	//Add a callback for the given window that will be called when the panel needs updating
-	void registerUpdateHandler(wxWindow *w, UpdateHandler handler) { updateHandlers.push_back(make_pair(w,handler));};
+	void registerUpdateHandler(wxWindow *w, UpdateHandler handler) { updateHandlers.push_back(std::make_pair(w,handler));};
 
 	//Resize event for window
 	void resized(wxSizeEvent& evt);
diff --git a/src/myAppIcon.ico b/src/myAppIcon.ico
index b2a05de..48ff344 100644
Binary files a/src/myAppIcon.ico and b/src/myAppIcon.ico differ
diff --git a/src/testing/filtertesting.cpp b/src/testing/filtertesting.cpp
index 43038e5..2b72f2a 100644
--- a/src/testing/filtertesting.cpp
+++ b/src/testing/filtertesting.cpp
@@ -1,6 +1,6 @@
 /*
  *	filtertesting.cpp - unit testing implementation for filter code
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -15,6 +15,14 @@
  *	You should have received a copy of the GNU General Public License
  *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+#include <string>
+#include <list>
+#include <set>
+#include <fstream>
+#include <iostream>
+
+
+using namespace std;
 
 //!Run each filter through its own unit test function
 bool filterTests();
@@ -57,7 +65,12 @@ bool testFilterTree(const FilterTree &f,
 	std::vector<std::pair<const Filter *, string > > consoleMessages;
 
 	ProgressData prog;
-	if(f.refreshFilterTree(outData,devices,consoleMessages,prog,dummyCallback))
+#ifdef  HAVE_CPP_1X
+	ATOMIC_BOOL wantAbort(false);
+#else
+	ATOMIC_BOOL wantAbort=false;
+#endif
+	if(f.refreshFilterTree(outData,devices,consoleMessages,prog,wantAbort))
 	{
 		f.safeDeleteFilterList(outData);
 		return false;
@@ -468,7 +481,7 @@ bool filterRefreshNoOut()
 		return true;
 	}
 
-	//Write out some useable data
+	//Write out some usable data
 	f << "1 2 3 4" << std::endl;
 	f << "2 1 3 5" << std::endl;
 	f << "3 2 1 6" << std::endl;
diff --git a/src/testing/mglTesting.cpp b/src/testing/mglTesting.cpp
index 5b54cd9..82668a1 100644
--- a/src/testing/mglTesting.cpp
+++ b/src/testing/mglTesting.cpp
@@ -1,6 +1,6 @@
 /*
  *	mglTesting.cpp - unit testing implementation for mgl code
- *	Copyright (C) 2014, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -36,13 +36,15 @@
 // a reference image (if possible)
 bool mglTest()
 {
-	WARN(false,"Disabled until upstream fix propagates");
-	return true;
-	
-		
+
+	//something is wrong with mathgl's FP handling
+	bool fpeTrapped=getTrapfpe();
+	if(fpeTrapped)
+		trapfpe(false);
+
 	unsigned int w=1024,h=768;
 	mglGraph *grS;
-	grS = new mglGraph(0,w,h);
+	grS = new mglGraph(w,h);
 
 	//Create some fake data
 	mglData someDataX,someDataY;
@@ -167,6 +169,8 @@ bool mglTest()
 	rmFile(s);
 	rmFile(t+".svg");
 
+	if(fpeTrapped)
+		trapfpe(true);
 	return true;
 }
 
diff --git a/src/testing/mglTesting.h b/src/testing/mglTesting.h
index a9da1d2..bb4be57 100644
--- a/src/testing/mglTesting.h
+++ b/src/testing/mglTesting.h
@@ -1,6 +1,6 @@
 /*
  *	mglTesting.h - unit testing implementation for mgl code
- *	Copyright (C) 2014, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/testing/testing.cpp b/src/testing/testing.cpp
index 9c711ec..7d4c982 100644
--- a/src/testing/testing.cpp
+++ b/src/testing/testing.cpp
@@ -1,6 +1,6 @@
 /*
  *	testing.cpp - unit testing implementation
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
 #include <wx/dir.h>
 
 #include <fstream>
+#include <map>
 
 #include "wx/wxcommon.h"
 
@@ -32,6 +33,9 @@
 #include "backend/configFile.h"
 #include "backend/filters/algorithms/binomial.h"
 #include "backend/filters/algorithms/K3DTree-mk2.h"
+#include "backend/filters/algorithms/K3DTree.h"
+#include "backend/filters/algorithms/mass.h"
+
 #include "backend/APT/ionhit.h"
 #include "backend/APT/APTFileIO.h"
 #include "backend/APT/abundanceParser.h"
@@ -39,6 +43,7 @@
 #include "common/stringFuncs.h"
 #include "common/xmlHelper.h"
 
+#include "gl/isoSurface.h"
 
 const char *TESTING_RESOURCE_DIRS[] = {
 		"../test/",
@@ -47,6 +52,10 @@ const char *TESTING_RESOURCE_DIRS[] = {
 
 #include "filtertesting.cpp"
 
+using std::ifstream;
+using std::cerr;
+using std::endl;
+using std::map;
 
 //!Try loading each range file in the testing folder
 bool rangeFileLoadTests();
@@ -62,6 +71,9 @@ bool locateDataTests();
 
 bool abundanceTests();
 
+//run the tests for algorithms/ 
+bool algorithmTests();
+
 bool basicFunctionTests()
 {
 	testStringFuncs();
@@ -111,19 +123,30 @@ bool basicFunctionTests()
 
 bool runUnitTests()
 {
+	//Set the abort pointer for the filter
+#ifdef HAVE_CPP_1X
+	ATOMIC_BOOL abortFlag(false);
+#else
+	ATOMIC_BOOL abortFlag=false;
+#endif
+	Filter::wantAbort=&abortFlag;
+	K3DTree::setAbortFlag(&abortFlag);
+	K3DTreeMk2::setAbortFlag(&abortFlag);
+
+	unsigned int progressVar=0;
+	K3DTree::setProgressPtr(&progressVar);
+	K3DTreeMk2::setProgressPtr(&progressVar);
 
 	cerr << "Running unit tests..." ;
 
-	if(!K3DMk2Tests())
+	if(!algorithmTests())
 		return false;
 
-
 	if(!testIonHit())
 		return false;
 
 	if(!filterTests())
 		return false;
-
 	if(!rangeFileLoadTests())
 		return false;
 
@@ -137,8 +160,6 @@ bool runUnitTests()
 	if(!runVoxelTests())
 		return false;
 
-	if(!testBinomial())
-		return false;
 
 	if(!runStateTests())
 		return false;
@@ -148,13 +169,16 @@ bool runUnitTests()
 
 	if(!testFileIO())
 		return false;
-
-	if(!mglTest())
-		return false;
+	//MGL test is disabled, due to a bug in mathgl in debian testing
+	// which causes threading segfaults. This is fixed in recent versions
+//	if(!mglTest())
+//		return false;
 
 	if(!abundanceTests())
 		return false;
 
+	if(!testIsoSurface())
+		return false;
 	cerr << " OK" << endl << endl;
 
 	return true;
@@ -166,7 +190,9 @@ bool rangeFileLoadTests()
 	//whichever is first. 
 	wxString testDir;
 	bool haveDir=false;
-	for(unsigned int ui=0;ui<THREEDEP_ARRAYSIZE(TESTING_RESOURCE_DIRS);ui++)
+	size_t n;
+	n = THREEDEP_ARRAYSIZE(TESTING_RESOURCE_DIRS);
+	for(unsigned int ui=0;ui<n;ui++)
 	{
 		testDir=(TESTING_RESOURCE_DIRS[ui]);
 		if(wxDirExists(testDir))
@@ -303,7 +329,7 @@ bool rangeFileLoadTests()
 
 
 
-	map<string,int> typeMapping;
+	map<string,unsigned int> typeMapping;
 
 	typeMapping["test1.rng"]=RANGE_FORMAT_ORNL;
 	typeMapping["test2.rng"]=RANGE_FORMAT_ORNL; 
@@ -440,7 +466,7 @@ bool locateDataTests()
 			check=true;
 		else
 		{
-			//Check only if compiled for thiarch arch
+			//Check only if compiled for a specific arch
 			if(arch == "win")
 			{
 #if defined(__WIN32__) || defined(__WIN64)
@@ -523,4 +549,19 @@ bool abundanceTests()
 	return true;
 }
 
+bool algorithmTests()
+{
+	if(!testAnderson())
+		return false;
+	if(!testBackgroundFit())
+		return false;
+
+	if(!K3DMk2Tests())
+		return false;
+	
+	if(!testBinomial())
+		return false;
+	return true;
+}
+
 #endif
diff --git a/src/testing/testing.h b/src/testing/testing.h
index 0b94768..ffb3678 100644
--- a/src/testing/testing.h
+++ b/src/testing/testing.h
@@ -1,6 +1,6 @@
 /*
  *	testing.cpp - unit testing framework
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
diff --git a/src/wx/propertyGridUpdater.cpp b/src/wx/propertyGridUpdater.cpp
index 228a54c..67b2943 100644
--- a/src/wx/propertyGridUpdater.cpp
+++ b/src/wx/propertyGridUpdater.cpp
@@ -1,6 +1,6 @@
 /*
  * propertyGridUpdater.cpp  - Update a  propertgy grid, using 3depict backend data
- * Copyright (C) 2014  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,11 @@
 //workaround for decimal separator bug
 #include <wx/numformatter.h>
 
+#include <vector>
+#include <string>
+
+using std::vector;
+using std::string;
 
 void updateFilterPropertyGrid(wxPropertyGrid *g, const Filter *f, const string &stateString)
 {
@@ -61,7 +66,7 @@ void updateFilterPropertyGrid(wxPropertyGrid *g, const Filter *f, const string &
 		g->Append(new wxPropertyCategory(string("") + title,title));
 		
 		
-		//Set the children of thies property
+		//Set the children of this property
 		for(size_t uj=0;uj<propGrouping.size();uj++)
 		{
 			FilterProperty fp;
@@ -83,6 +88,7 @@ void updateFilterPropertyGrid(wxPropertyGrid *g, const Filter *f, const string &
 							boolVal);
 					break;
 				};
+				//TODO: we need a PROPERTY_TYPE_UINT
 				case PROPERTY_TYPE_INTEGER:
 				{
 					long long iV;
@@ -145,16 +151,24 @@ void updateFilterPropertyGrid(wxPropertyGrid *g, const Filter *f, const string &
 					
 					break;
 				}
+				case PROPERTY_TYPE_DIR:
+				{
+					pgp = new wxDirProperty(fp.name,keyStr,fp.data);
+					break;
+				}
 			}
 
+			//Set the tooltip
 			pgp->SetHelpString(fp.helpText);
 
+			//add the property to the grid
 			g->Append(pgp);
 
 			switch(fp.type)
 			{
 				case PROPERTY_TYPE_BOOL:
 				{
+					//if a bool property, use a checkbox to edit
 					g->SetPropertyEditor(pgp,wxPGEditor_CheckBox);
 					break;
 				}
@@ -242,10 +256,7 @@ void updateCameraPropertyGrid(wxPropertyGrid *g, const Camera *c)
 				case PROPERTY_TYPE_COLOUR:
 				{
 					ColourRGBA rgba;
-					bool res;
-					res=rgba.parse(camProp.data);
-	
-					ASSERT(res);
+					rgba.parse(camProp.data);
 					pgp =  new wxColourProperty(camProp.name,keyStr,
 								 wxColour(rgba.r(),rgba.g(),rgba.b()) ) ;
 					break;
diff --git a/src/wx/propertyGridUpdater.h b/src/wx/propertyGridUpdater.h
index aeddbb6..f6252e8 100644
--- a/src/wx/propertyGridUpdater.h
+++ b/src/wx/propertyGridUpdater.h
@@ -1,6 +1,6 @@
 /*
  * propertyGridUpdater.h  - Update a  propertgy grid, using 3depict backend data
- * Copyright (C) 2014  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ const long PROPERTY_GRID_EXTRA_STYLE= wxPG_EX_HELP_AS_TOOLTIPS;
 // Due to a wx bug, the grid cannot contain items and be shown
 // when passed ot this function
 // statestring contains the previous grid' state (also part of bug workaround)
-void updateFilterPropertyGrid(wxPropertyGrid *g, const Filter *f, const std::string &stateString);
+void updateFilterPropertyGrid(wxPropertyGrid *g, const Filter *f, const std::string &stateString="");
 
 void updateCameraPropertyGrid(wxPropertyGrid *g, const Camera *c); 
 
diff --git a/src/wx/wxcommon.cpp b/src/wx/wxcommon.cpp
index 4a3be51..bcf22dd 100644
--- a/src/wx/wxcommon.cpp
+++ b/src/wx/wxcommon.cpp
@@ -1,6 +1,6 @@
 /*
  *	wxcommon.cpp - Comon wxwidgets functionality 
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -44,8 +44,10 @@ const char *RSS_FEED_LOCATION="http://threedepict.sourceforge.net/rss.xml";
 
 //Auto update event for posting back to main thread upon completion
 wxEventType RemoteUpdateAvailEvent = wxNewEventType(); // You get to choose the name yourself
-		
 
+//Signalling event for main frame that it is completed
+wxEventType RefreshCompleteEvent= wxNewEventType(); // You get to choose the name yourself
+		
 //Maximum amount of content in RSS header is 1MB.
 const unsigned int MAX_RSS_CONTENT_SIZE=1024*1024;
 
@@ -105,17 +107,16 @@ std::string locateDataFile(const char *name)
 
 	//Possible search paths. Must have trailing slash. will
 	//be searched in sequence.
-	const unsigned int NUM_SEARCH_DIRS=6;
 	const char *possibleDirs[] = { "./",
 					"/usr/local/share/3Depict/",
 					"/usr/share/3Depict/",
 					"/usr/share/3depict/", //Under debian, we have to use lowercase according to the debian guidelines, so handle this case.
 					"../data/",
-					"./data/",
+					"./data/"
 					};
 
-	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(possibleDirs) == NUM_SEARCH_DIRS);
-	
+	const unsigned int NUM_SEARCH_DIRS=THREEDEP_ARRAYSIZE(possibleDirs);
+
 	std::string s;
 	for(unsigned int ui=0; ui<NUM_SEARCH_DIRS; ui++)
 	{
@@ -125,7 +126,7 @@ std::string locateDataFile(const char *name)
 			return s;
 	}
 
-	//Return empty string, as we cna't find it
+	//Return empty string, as we can't find it
 	return std::string("");
 #elif defined (__APPLE__)
     CFBundleRef mainBundle = CFBundleGetMainBundle();
diff --git a/src/wx/wxcommon.h b/src/wx/wxcommon.h
index 7d45756..3477269 100644
--- a/src/wx/wxcommon.h
+++ b/src/wx/wxcommon.h
@@ -1,6 +1,6 @@
 /*
  * wxcommon.h  - Common wxwidgets header stuff
- * Copyright (C) 2014  D Haley
+ * Copyright (C) 2015  D Haley
  * 
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -129,6 +129,9 @@ std::string locateDataFile(const char *name);
 //Custom event for remote update thread posting
 extern wxEventType RemoteUpdateAvailEvent; 
 
+//Custom event for signalling that a refresh is completed
+extern wxEventType RefreshCompleteEvent; 
+
 //Return true IFF process ID and process name match running process
 bool processMatchesName(size_t processID, const std::string &procName);
 
diff --git a/src/wx/wxcomponents.cpp b/src/wx/wxcomponents.cpp
index 57de867..074c9cc 100644
--- a/src/wx/wxcomponents.cpp
+++ b/src/wx/wxcomponents.cpp
@@ -1,6 +1,6 @@
 /*
  *	wxcomponents.h - Custom wxWidgets components header
- *	Copyright (C) 2013, D Haley 
+ *	Copyright (C) 2015, D Haley 
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -26,8 +26,12 @@
 
 #include <wx/clipbrd.h>
 
+#include <stack>
+
+
 using std::ofstream;
 using std::vector;
+using std::stack;
 
 const float FONT_HEADING_SCALEFACTOR=1.25f;
 
@@ -469,9 +473,7 @@ void TextTreeCtrl::OnTreePaint(wxPaintEvent &event)
 	}
 
 	//Draw each text in turn, advancing by spacing
-
-//FIXME dc->DrawText missing under windows?
-#ifndef __WIN32__
+	
 	// start far enough back so that 
 	float startY= 0.5*(h - blockHeight);
 
@@ -481,14 +483,17 @@ void TextTreeCtrl::OnTreePaint(wxPaintEvent &event)
 		int startX;
 		startX=w/2 - textSize.GetWidth()/2; 
 
+#if !(defined(_WIN32) || defined(_WIN64) ) 
 		dc->DrawText((messageStrs[ui]),
 					startX,startY);	
-		
+#else
+		dc->DrawTextW((messageStrs[ui]),
+					startX,startY);
+#endif
 		startY+=HEIGHT_SPACING*textSize.GetHeight();
 	}
 
 	delete dc;
-#endif
 }
 
 std::string TTFFinder::findFont(const char *fontFile)
@@ -683,7 +688,7 @@ std::string TTFFinder::suggestFontName(unsigned int fontType, unsigned int index
 		"HighwayGothic.ttf",
 		"Hobo.ttf",
 		"Impact.ttf",
-		"Johnston.ttf"
+		"Johnston.ttf",
 		"NewJohnston.ttf",
 		"Kabel.ttf",
 		"LucidaGrande.ttf",
diff --git a/src/wx/wxcomponents.h b/src/wx/wxcomponents.h
index 0405ef3..9d27f94 100644
--- a/src/wx/wxcomponents.h
+++ b/src/wx/wxcomponents.h
@@ -1,6 +1,6 @@
 /*
  * 	wxcomponents.h - custom wxwidgets components
- *	Copyright (C) 2013, D. Haley
+ *	Copyright (C) 2015, D. Haley
 
  *	This program is free software: you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -70,7 +70,7 @@ protected:
 
 //!Update a wxTree control to layout according to the specified filter tree
 void upWxTreeCtrl(const FilterTree &filterTree, wxTreeCtrl *t,
-		std::map<size_t,Filter *> &filterMap,vector<const Filter *> &persistentFilters,
+		std::map<size_t,Filter *> &filterMap,std::vector<const Filter *> &persistentFilters,
 		const Filter *visibleFilt);
 
 //Subclassed wx tree ctrl to draw text in tree when empty
diff --git a/translations/3Depict_base.pot b/translations/3Depict_base.pot
index 2c9adb0..dda1c9f 100644
--- a/translations/3Depict_base.pot
+++ b/translations/3Depict_base.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-08-23 15:32+0100\n"
+"POT-Creation-Date: 2015-04-26 23:23+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -21,21 +21,21 @@ msgstr ""
 msgid "Lock"
 msgstr ""
 
-#: ../src/gl/cameras.cpp:603 ../src/backend/filters/ionClip.cpp:519
-#: ../src/backend/filters/ionClip.cpp:541
-#: ../src/backend/filters/ionClip.cpp:563
-#: ../src/backend/filters/ionClip.cpp:600
-#: ../src/backend/filters/compositionProfile.cpp:964
-#: ../src/backend/filters/compositionProfile.cpp:1002
-#: ../src/backend/filters/spatialAnalysis.cpp:774
-#: ../src/backend/filters/transform.cpp:1216
-#: ../src/backend/filters/transform.cpp:1243
-#: ../src/backend/filters/transform.cpp:1269
-#: ../src/backend/filters/annotation.cpp:563
+#: ../src/gl/cameras.cpp:603 ../src/backend/filters/ionClip.cpp:528
+#: ../src/backend/filters/ionClip.cpp:550
+#: ../src/backend/filters/ionClip.cpp:572
+#: ../src/backend/filters/ionClip.cpp:609
+#: ../src/backend/filters/compositionProfile.cpp:993
+#: ../src/backend/filters/compositionProfile.cpp:1031
+#: ../src/backend/filters/spatialAnalysis.cpp:846
+#: ../src/backend/filters/transform.cpp:1202
+#: ../src/backend/filters/transform.cpp:1229
+#: ../src/backend/filters/transform.cpp:1255
+#: ../src/backend/filters/annotation.cpp:568
 msgid "Origin"
 msgstr ""
 
-#: ../src/gl/cameras.cpp:611 ../src/backend/filters/spatialAnalysis.cpp:697
+#: ../src/gl/cameras.cpp:611 ../src/backend/filters/spatialAnalysis.cpp:769
 msgid "Target"
 msgstr ""
 
@@ -48,7 +48,7 @@ msgid "Perspective"
 msgstr ""
 
 #: ../src/gl/cameras.cpp:627 ../src/gl/cameras.cpp:730
-#: ../src/gui/mainFrame.cpp:5134
+#: ../src/gui/mainFrame.cpp:5150
 msgid "Orthogonal"
 msgstr ""
 
@@ -64,23 +64,23 @@ msgstr ""
 msgid "View size"
 msgstr ""
 
-#: ../src/wx/wxcomponents.cpp:187
+#: ../src/wx/wxcomponents.cpp:191
 msgid "Save Data..."
 msgstr ""
 
-#: ../src/wx/wxcomponents.cpp:188
+#: ../src/wx/wxcomponents.cpp:192
 msgid "Text File (*.txt)|*.txt|All Files (*)|*"
 msgstr ""
 
-#: ../src/wx/wxcomponents.cpp:200
+#: ../src/wx/wxcomponents.cpp:204
 msgid "Error saving file. Check output dir is writable."
 msgstr ""
 
-#: ../src/wx/wxcomponents.cpp:200 ../src/gui/dialogs/ExportRngDialog.cpp:170
-#: ../src/gui/mainFrame.cpp:1446 ../src/gui/mainFrame.cpp:1572
-#: ../src/gui/mainFrame.cpp:1621 ../src/gui/mainFrame.cpp:1697
-#: ../src/gui/mainFrame.cpp:2253 ../src/gui/mainFrame.cpp:2319
-#: ../src/gui/mainFrame.cpp:2411 ../src/gui/mainFrame.cpp:2526
+#: ../src/wx/wxcomponents.cpp:204 ../src/gui/dialogs/ExportRngDialog.cpp:170
+#: ../src/gui/mainFrame.cpp:1448 ../src/gui/mainFrame.cpp:1573
+#: ../src/gui/mainFrame.cpp:1622 ../src/gui/mainFrame.cpp:1698
+#: ../src/gui/mainFrame.cpp:2245 ../src/gui/mainFrame.cpp:2311
+#: ../src/gui/mainFrame.cpp:2404 ../src/gui/mainFrame.cpp:2517
 msgid "Save error"
 msgstr ""
 
@@ -227,33 +227,33 @@ msgid ""
 "rrng)|*.rrng;*.RRNG|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/glPane.cpp:637
+#: ../src/gui/glPane.cpp:628
 msgid "Use shift/ctrl-space or double tap to alter reset axis"
 msgstr ""
 
-#: ../src/gui/glPane.cpp:910
+#: ../src/gui/glPane.cpp:901
 msgid "Image progress"
 msgstr ""
 
-#: ../src/gui/glPane.cpp:911
+#: ../src/gui/glPane.cpp:902
 msgid "Rendering tiles..."
 msgstr ""
 
-#: ../src/gui/glPane.cpp:1097
+#: ../src/gui/glPane.cpp:1091
 msgid "Animation progress"
 msgstr ""
 
-#: ../src/gui/glPane.cpp:1098
+#: ../src/gui/glPane.cpp:1092
 msgid "Rendering sequence..."
 msgstr ""
 
-#: ../src/gui/glPane.cpp:1136
+#: ../src/gui/glPane.cpp:1130
 msgid "Saving Image "
 msgstr ""
 
-#: ../src/gui/glPane.cpp:1136 ../src/gui/mainFrame.cpp:4309
-#: ../src/gui/mainFrame.cpp:4313 ../src/gui/mainFrame.cpp:4326
-#: ../src/backend/filters/dataLoad.cpp:307
+#: ../src/gui/glPane.cpp:1130 ../src/gui/mainFrame.cpp:4329
+#: ../src/gui/mainFrame.cpp:4333 ../src/gui/mainFrame.cpp:4346
+#: ../src/backend/filters/dataLoad.cpp:321
 msgid " of "
 msgstr ""
 
@@ -270,14 +270,14 @@ msgid "Source Filter"
 msgstr ""
 
 #: ../src/gui/dialogs/ExportRngDialog.cpp:54
-#: ../src/backend/filters/rangeFile.cpp:665
+#: ../src/backend/filters/rangeFile.cpp:654
 msgid "Ions"
 msgstr ""
 
 #: ../src/gui/dialogs/ExportRngDialog.cpp:55
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1586
-#: ../src/backend/filters/voxelise.cpp:876
-#: ../src/backend/filters/rangeFile.cpp:732
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1587
+#: ../src/backend/filters/voxelise.cpp:834
+#: ../src/backend/filters/rangeFile.cpp:721
 msgid "Ranges"
 msgstr ""
 
@@ -288,8 +288,8 @@ msgstr ""
 #: ../src/gui/dialogs/ExportRngDialog.cpp:89
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:105
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:352
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1173
-#: ../src/backend/filters/dataLoad.cpp:566
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1177
+#: ../src/backend/filters/dataLoad.cpp:581
 msgid "Value"
 msgstr ""
 
@@ -306,7 +306,7 @@ msgid "Num Ranges"
 msgstr ""
 
 #: ../src/gui/dialogs/ExportRngDialog.cpp:116
-#: ../src/gui/dialogs/rangeEditDialog.cpp:695 ../src/backend/filter.cpp:44
+#: ../src/gui/dialogs/rangeEditDialog.cpp:696 ../src/backend/filter.cpp:50
 msgid "Ion"
 msgstr ""
 
@@ -318,7 +318,7 @@ msgstr ""
 msgid "Range end"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportRngDialog.cpp:151 ../src/gui/mainFrame.cpp:2355
+#: ../src/gui/dialogs/ExportRngDialog.cpp:151 ../src/gui/mainFrame.cpp:2348
 msgid "Save pos..."
 msgstr ""
 
@@ -326,10 +326,10 @@ msgstr ""
 msgid "ORNL format RNG (*.rng)|*.rng|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportRngDialog.cpp:167 ../src/gui/mainFrame.cpp:1446
-#: ../src/gui/mainFrame.cpp:1622 ../src/gui/mainFrame.cpp:1697
-#: ../src/gui/mainFrame.cpp:2254 ../src/gui/mainFrame.cpp:2412
-#: ../src/gui/mainFrame.cpp:2527
+#: ../src/gui/dialogs/ExportRngDialog.cpp:167 ../src/gui/mainFrame.cpp:1448
+#: ../src/gui/mainFrame.cpp:1623 ../src/gui/mainFrame.cpp:1698
+#: ../src/gui/mainFrame.cpp:2246 ../src/gui/mainFrame.cpp:2405
+#: ../src/gui/mainFrame.cpp:2518
 msgid "Unable to save. Check output destination can be written to."
 msgstr ""
 
@@ -345,92 +345,92 @@ msgstr ""
 msgid "Detailed view of selected range"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:218
+#: ../src/gui/dialogs/rangeEditDialog.cpp:221
 msgid "Show Overlays"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:243
+#: ../src/gui/dialogs/rangeEditDialog.cpp:246
 msgid "e.g. H2O"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:560
-#: ../src/gui/dialogs/rangeEditDialog.cpp:694 ../src/gui/mainFrame.cpp:5800
-#: ../src/backend/filter.cpp:45
+#: ../src/gui/dialogs/rangeEditDialog.cpp:561
+#: ../src/gui/dialogs/rangeEditDialog.cpp:695 ../src/gui/mainFrame.cpp:5801
+#: ../src/backend/filter.cpp:51
 msgid "Plot"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:561
+#: ../src/gui/dialogs/rangeEditDialog.cpp:562
 msgid "Short Name"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:562
+#: ../src/gui/dialogs/rangeEditDialog.cpp:563
 msgid "Long Name"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:563
-#: ../src/backend/filters/voxelise.cpp:1020
-#: ../src/backend/filters/compositionProfile.cpp:1100
-#: ../src/backend/filters/annotation.cpp:896
-#: ../src/backend/filters/spectrumPlot.cpp:452
+#: ../src/gui/dialogs/rangeEditDialog.cpp:564
+#: ../src/backend/filters/voxelise.cpp:975
+#: ../src/backend/filters/compositionProfile.cpp:1134
+#: ../src/backend/filters/annotation.cpp:901
+#: ../src/backend/filters/spectrumPlot.cpp:668
 msgid "Colour"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:696
-#: ../src/backend/filters/annotation.cpp:600
-#: ../src/backend/filters/annotation.cpp:641
-#: ../src/backend/filters/annotation.cpp:810
+#: ../src/gui/dialogs/rangeEditDialog.cpp:697
+#: ../src/backend/filters/annotation.cpp:605
+#: ../src/backend/filters/annotation.cpp:646
+#: ../src/backend/filters/annotation.cpp:815
 msgid "Start"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:697
-#: ../src/backend/filters/annotation.cpp:608
-#: ../src/backend/filters/annotation.cpp:650
-#: ../src/backend/filters/annotation.cpp:818
+#: ../src/gui/dialogs/rangeEditDialog.cpp:698
+#: ../src/backend/filters/annotation.cpp:613
+#: ../src/backend/filters/annotation.cpp:655
+#: ../src/backend/filters/annotation.cpp:823
 msgid "End"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1259
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1260
 msgid "Range or ion?"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1260
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1261
 msgid "Select type to add"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1541
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1542
 msgid "Range Editor"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1545
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1546
 msgid "Enable or disable all overlays"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1546
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1547
 msgid "Entered overlays, use delete to remove"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1547
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1548
 msgid "Available plots for ranging"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1548
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1549
 msgid "Enter species to display as overlay, e.g. SiO2"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1549
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1550
 msgid "Editable ranges"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1550
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1551
 msgid "Editable ions"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1585
-#: ../src/gui/dialogs/animateFilterDialog.cpp:173
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1586
+#: ../src/gui/dialogs/animateFilterDialog.cpp:177
 msgid "Plots"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1587
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1588
 msgid "Overlay"
 msgstr ""
 
@@ -446,8 +446,8 @@ msgstr ""
 msgid "Multiple autosave states were found; would you like to restore one?"
 msgstr ""
 
-#: ../src/gui/dialogs/filterErrorDialog.cpp:37 ../src/backend/filter.cpp:435
-#: ../src/backend/filter.cpp:438
+#: ../src/gui/dialogs/filterErrorDialog.cpp:37 ../src/backend/filter.cpp:456
+#: ../src/backend/filter.cpp:459
 msgid "Error"
 msgstr ""
 
@@ -460,43 +460,43 @@ msgstr ""
 msgid "Filter Errors"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:46
+#: ../src/gui/dialogs/StashDialog.cpp:49
 msgid "Stashes"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:49
+#: ../src/gui/dialogs/StashDialog.cpp:52
 msgid "Stashed Tree"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:51
+#: ../src/gui/dialogs/StashDialog.cpp:54
 msgid "Properties"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:57
+#: ../src/gui/dialogs/StashDialog.cpp:60
 msgid "Stash Name"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:58
+#: ../src/gui/dialogs/StashDialog.cpp:61
 msgid "Filter Count"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:91
+#: ../src/gui/dialogs/StashDialog.cpp:94
 msgid "Stashed Trees"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:94
+#: ../src/gui/dialogs/StashDialog.cpp:97
 msgid "Erase stashed item"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:95
+#: ../src/gui/dialogs/StashDialog.cpp:98
 msgid "Filter view for current stash"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:96
+#: ../src/gui/dialogs/StashDialog.cpp:99
 msgid "Settings for selected filter in current stash"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:97
+#: ../src/gui/dialogs/StashDialog.cpp:100
 msgid "Available stashes"
 msgstr ""
 
@@ -514,7 +514,7 @@ msgstr ""
 
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:104
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:350
-#: ../src/gui/dialogs/animateFilterDialog.cpp:192
+#: ../src/gui/dialogs/animateFilterDialog.cpp:196
 msgid "Frame"
 msgstr ""
 
@@ -583,12 +583,12 @@ msgid "Ramp"
 msgstr ""
 
 #: ../src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp:64
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1154
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1158
 msgid "Start Frame"
 msgstr ""
 
 #: ../src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp:66
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1155
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1159
 msgid "End Frame"
 msgstr ""
 
@@ -632,197 +632,197 @@ msgstr ""
 msgid "Cameca/Ametek ENV"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:152
+#: ../src/gui/dialogs/animateFilterDialog.cpp:156
 msgid "Key frames"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:153
+#: ../src/gui/dialogs/animateFilterDialog.cpp:157
 msgid "Output Data"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:154
+#: ../src/gui/dialogs/animateFilterDialog.cpp:158
 msgid "Filters and properties"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:160
+#: ../src/gui/dialogs/animateFilterDialog.cpp:164
 msgid "Dir : "
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:163
+#: ../src/gui/dialogs/animateFilterDialog.cpp:167
 msgid "Output only when refresh required"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:165
+#: ../src/gui/dialogs/animateFilterDialog.cpp:169
 msgid "Data Types:"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:166
+#: ../src/gui/dialogs/animateFilterDialog.cpp:170
 msgid "3D Images"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:167
+#: ../src/gui/dialogs/animateFilterDialog.cpp:171
 msgid "File Suffix: "
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:169
+#: ../src/gui/dialogs/animateFilterDialog.cpp:173
 msgid "Size : "
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:171
+#: ../src/gui/dialogs/animateFilterDialog.cpp:175
 msgid "..."
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:172
+#: ../src/gui/dialogs/animateFilterDialog.cpp:176
 msgid "Point data"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:174
+#: ../src/gui/dialogs/animateFilterDialog.cpp:178
 msgid "Voxel data"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:175
+#: ../src/gui/dialogs/animateFilterDialog.cpp:179
 msgid "Range files"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:176
+#: ../src/gui/dialogs/animateFilterDialog.cpp:180
 msgid "Format"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:677
+#: ../src/gui/dialogs/animateFilterDialog.cpp:681
 msgid "transition frame"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:677
-#: ../src/gui/mainFrame.cpp:1674
+#: ../src/gui/dialogs/animateFilterDialog.cpp:681
+#: ../src/gui/mainFrame.cpp:1675
 msgid "Frame count"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:749
+#: ../src/gui/dialogs/animateFilterDialog.cpp:753
 msgid "Key frame : Colour"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:802
+#: ../src/gui/dialogs/animateFilterDialog.cpp:806
 msgid "File existed, but was unable to read or interpret file contents."
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:803
+#: ../src/gui/dialogs/animateFilterDialog.cpp:807
 msgid "String load failed"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:824
+#: ../src/gui/dialogs/animateFilterDialog.cpp:828
 msgid "Keyframe : decimal"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:833
+#: ../src/gui/dialogs/animateFilterDialog.cpp:837
 msgid "Keyframe : integer"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:842
+#: ../src/gui/dialogs/animateFilterDialog.cpp:846
 msgid "Keyframe : 3D Point"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:967
+#: ../src/gui/dialogs/animateFilterDialog.cpp:971
 msgid "Select or create new folder"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1147
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1151
 msgid "Export Animation"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1148
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1152
 msgid "Select filter"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1149
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1153
 msgid "Select property"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1151
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1171
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1155
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1175
 msgid "Filter"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1152
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1172
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1156
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1176
 msgid "Property"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1153
-#: ../src/backend/filters/voxelise.cpp:903
-#: ../src/backend/filters/spatialAnalysis.cpp:906
-#: ../src/backend/filters/transform.cpp:1133
-#: ../src/backend/filters/annotation.cpp:540
-#: ../src/backend/filters/annotation.cpp:546
-#: ../src/backend/filters/ionDownsample.cpp:463
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1157
+#: ../src/backend/filters/voxelise.cpp:861
+#: ../src/backend/filters/spatialAnalysis.cpp:978
+#: ../src/backend/filters/transform.cpp:1119
+#: ../src/backend/filters/annotation.cpp:545
+#: ../src/backend/filters/annotation.cpp:551
+#: ../src/backend/filters/ionDownsample.cpp:465
 msgid "Mode"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1156
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1160
 msgid "Keyframe table"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1157
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1161
 msgid "Remove the selected keyframe from the table"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1158
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1162
 msgid "Enter where the animation frames will be exported to"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1159
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1163
 msgid "Browse to directory where the animation frames will be exported to"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1161
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1165
 msgid ""
 "Title for files, result will be saved as #-name.png, where # is image number."
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1162
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1166
 msgid "Target resolution (image size)"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1164
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1168
 msgid "Select frame for property display"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1165
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1169
 msgid "Enter frame number to change frame (eg 1/20)"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1166
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1170
 msgid "Save point data (POS files) in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1167
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1171
 msgid "Save plots (as text files) in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1168
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1172
 msgid "Save voxel data (raw files) in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1169
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1173
 msgid "Save range files  in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1174
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1178
 msgid "Animation parameters for current frame"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1175
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1179
 msgid "Abort animation"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1176
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1180
 msgid "Run Animation"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1247
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1251
 msgid "Filter view"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1248
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1252
 msgid "Frame view"
 msgstr ""
 
@@ -843,59 +843,60 @@ msgstr ""
 msgid "Resolution Selection"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:75
+#: ../src/gui/dialogs/ExportPos.cpp:62
 msgid "Export:"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:76
-#: ../src/backend/filters/boundingBox.cpp:521
+#: ../src/gui/dialogs/ExportPos.cpp:63
+#: ../src/backend/filters/boundingBox.cpp:527
 msgid "Visible"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:77
+#: ../src/gui/dialogs/ExportPos.cpp:64
 msgid "Selected Data"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:79
+#: ../src/gui/dialogs/ExportPos.cpp:66
 msgid "Available Data"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:85
+#: ../src/gui/dialogs/ExportPos.cpp:72
 msgid "Selection"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:110 ../src/gui/dialogs/ExportPos.cpp:113
+#: ../src/gui/dialogs/ExportPos.cpp:97 ../src/gui/dialogs/ExportPos.cpp:100
 msgid "Index"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:111 ../src/gui/dialogs/ExportPos.cpp:114
-#: ../src/backend/filters/compositionProfile.cpp:581
-#: ../src/backend/filters/spatialAnalysis.cpp:2199
-#: ../src/backend/filters/spatialAnalysis.cpp:2259
-#: ../src/backend/filters/spatialAnalysis.cpp:3224
-#: ../src/backend/filters/spatialAnalysis.cpp:3436
-#: ../src/backend/filters/spatialAnalysis.cpp:3495
-#: ../src/backend/filters/spectrumPlot.cpp:237
+#: ../src/gui/dialogs/ExportPos.cpp:98 ../src/gui/dialogs/ExportPos.cpp:101
+#: ../src/backend/filters/compositionProfile.cpp:604
+#: ../src/backend/filters/spatialAnalysis.cpp:2282
+#: ../src/backend/filters/spatialAnalysis.cpp:2375
+#: ../src/backend/filters/spatialAnalysis.cpp:2435
+#: ../src/backend/filters/spatialAnalysis.cpp:3388
+#: ../src/backend/filters/spatialAnalysis.cpp:3591
+#: ../src/backend/filters/spatialAnalysis.cpp:3650
+#: ../src/backend/filters/spectrumPlot.cpp:65
 msgid "Count"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:457
+#: ../src/gui/dialogs/ExportPos.cpp:446
 msgid "Export Pos Data"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:460
+#: ../src/gui/dialogs/ExportPos.cpp:449
 msgid "Tree of filters, select leaves to show ion data."
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:462
+#: ../src/gui/dialogs/ExportPos.cpp:451
 msgid "Add all data from all filters"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:463
+#: ../src/gui/dialogs/ExportPos.cpp:452
 msgid "Add all data from currently selected filter"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:464
+#: ../src/gui/dialogs/ExportPos.cpp:453
 msgid "Add selected data from currently selected filter"
 msgstr ""
 
@@ -943,7 +944,7 @@ msgstr ""
 msgid "Raw Data Panel"
 msgstr ""
 
-#: ../src/gui/dialogs/prefDialog.cpp:94 ../src/gui/mainFrame.cpp:663
+#: ../src/gui/dialogs/prefDialog.cpp:94 ../src/gui/mainFrame.cpp:676
 msgid "Plot List"
 msgstr ""
 
@@ -1027,629 +1028,613 @@ msgstr ""
 msgid "Camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:105
+#: ../src/gui/mainFrame.cpp:119
 msgid "New camera name..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:106
+#: ../src/gui/mainFrame.cpp:120
 msgid "New stash name..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:111
+#: ../src/gui/mainFrame.cpp:125
 msgid "New Filter..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:128 ../src/backend/filters/annotation.cpp:555
-#: ../src/backend/filters/annotation.cpp:659
+#: ../src/gui/mainFrame.cpp:142 ../src/backend/filters/annotation.cpp:560
+#: ../src/backend/filters/annotation.cpp:664
 #: ../src/backend/filters/annotation.h:96
 msgid "Annotation"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:129
+#: ../src/gui/mainFrame.cpp:143
 msgid "Bounding Box"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:130 ../src/backend/filters/ionClip.cpp:619
+#: ../src/gui/mainFrame.cpp:144 ../src/backend/filters/ionClip.cpp:628
 #: ../src/backend/filters/ionClip.h:66
 msgid "Clipping"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:131 ../src/backend/filters/clusterAnalysis.h:143
+#: ../src/gui/mainFrame.cpp:145 ../src/backend/filters/clusterAnalysis.h:150
 msgid "Cluster Analysis"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:132
+#: ../src/gui/mainFrame.cpp:146
 msgid "Compos. Profiles"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:133
+#: ../src/gui/mainFrame.cpp:147
 msgid "Downsampling"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:134
+#: ../src/gui/mainFrame.cpp:148
 msgid "Extern. Prog."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:135
+#: ../src/gui/mainFrame.cpp:149
 msgid "Ion Colour"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:136
+#: ../src/gui/mainFrame.cpp:150
 msgid "Ion Info"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:137
+#: ../src/gui/mainFrame.cpp:151
 msgid "Ion Transform"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:138 ../src/backend/filters/spectrumPlot.h:53
+#: ../src/gui/mainFrame.cpp:152 ../src/backend/filters/spectrumPlot.h:76
 msgid "Spectrum"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:139
+#: ../src/gui/mainFrame.cpp:153
 msgid "Range File"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:140 ../src/backend/filters/spatialAnalysis.h:193
+#: ../src/gui/mainFrame.cpp:154 ../src/backend/filters/spatialAnalysis.h:202
 msgid "Spat. Analysis"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:141 ../src/backend/filters/voxelise.h:121
+#: ../src/gui/mainFrame.cpp:155 ../src/backend/filters/voxelise.h:122
 msgid "Voxelisation"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:429
+#: ../src/gui/mainFrame.cpp:442
 msgid "OpenGL Failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:430 ../src/gui/mainFrame.cpp:432
+#: ../src/gui/mainFrame.cpp:443 ../src/gui/mainFrame.cpp:445
 msgid ""
 "Unable to initialise the openGL (3D) panel. Program cannot start. Please "
 "check your video drivers."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:452
+#: ../src/gui/mainFrame.cpp:466
 msgid "&Open...\tCtrl+O"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:452
+#: ../src/gui/mainFrame.cpp:466
 msgid "Open state file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:453
+#: ../src/gui/mainFrame.cpp:467
 msgid "&Merge...\tCtrl+Shift+O"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:453
+#: ../src/gui/mainFrame.cpp:467
 msgid "Merge other file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:457
+#: ../src/gui/mainFrame.cpp:471
 msgid "&Recent"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:458
+#: ../src/gui/mainFrame.cpp:472
 msgid "&Save\tCtrl+S"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:458
+#: ../src/gui/mainFrame.cpp:472
 msgid "Save state to file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:460
+#: ../src/gui/mainFrame.cpp:474
 msgid "Save &As...\tCtrl+Shift+S"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:460
+#: ../src/gui/mainFrame.cpp:474
 msgid "Save current state to new file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:463
+#: ../src/gui/mainFrame.cpp:477
 msgid "&Plot...\tCtrl+P"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:463
+#: ../src/gui/mainFrame.cpp:477
 msgid "Export Current Plot"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:464
+#: ../src/gui/mainFrame.cpp:478
 msgid "&Image...\tCtrl+I"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:464
+#: ../src/gui/mainFrame.cpp:478
 msgid "Export Current 3D View"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:465
+#: ../src/gui/mainFrame.cpp:479
 msgid "Ion&s...\tCtrl+N"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:465
+#: ../src/gui/mainFrame.cpp:479
 msgid "Export Ion Data"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:466
+#: ../src/gui/mainFrame.cpp:480
 msgid "Ran&ges...\tCtrl+G"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:466
+#: ../src/gui/mainFrame.cpp:480
 msgid "Export Range Data"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:467
-msgid "&Animate Filters...\tCtrl+A"
+#: ../src/gui/mainFrame.cpp:481
+msgid "&Animate Filters...\tCtrl+T"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:467
+#: ../src/gui/mainFrame.cpp:481
 msgid "Export Animated Filter"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:468
+#: ../src/gui/mainFrame.cpp:482
 msgid "Ani&mate Camera...\tCtrl+M"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:468
+#: ../src/gui/mainFrame.cpp:482
 msgid "Export Animated Camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:469
+#: ../src/gui/mainFrame.cpp:483
 msgid "Pac&kage...\tCtrl+K"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:469
+#: ../src/gui/mainFrame.cpp:483
 msgid "Export analysis package"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:471
+#: ../src/gui/mainFrame.cpp:485
 msgid "&Export"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:474
+#: ../src/gui/mainFrame.cpp:488
 msgid "&Quit\tCtrl+Q"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:474 ../src/gui/mainFrame.cpp:476
+#: ../src/gui/mainFrame.cpp:488 ../src/gui/mainFrame.cpp:490
 msgid "Exit Program"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:476
+#: ../src/gui/mainFrame.cpp:490
 msgid "E&xit"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:478
+#: ../src/gui/mainFrame.cpp:492
 msgid "&File"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:482
+#: ../src/gui/mainFrame.cpp:496
 msgid "&Background Colour...\tCtrl+B"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:482
+#: ../src/gui/mainFrame.cpp:496
 msgid "Change background colour"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:486
+#: ../src/gui/mainFrame.cpp:500
 msgid "&Control Pane\tF2"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:486 ../src/gui/mainFrame.cpp:489
+#: ../src/gui/mainFrame.cpp:500 ../src/gui/mainFrame.cpp:503
 msgid "Toggle left control pane"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:489
+#: ../src/gui/mainFrame.cpp:503
 msgid "&Control Pane\tAlt+C"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:495
+#: ../src/gui/mainFrame.cpp:509
 msgid "&Raw Data Pane\tF3"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:495 ../src/gui/mainFrame.cpp:498
+#: ../src/gui/mainFrame.cpp:509 ../src/gui/mainFrame.cpp:512
 msgid "Toggle raw data  pane (bottom)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:498
+#: ../src/gui/mainFrame.cpp:512
 msgid "&Raw Data Pane\tAlt+R"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:502
+#: ../src/gui/mainFrame.cpp:516
 msgid "&Plot List\tF4"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:502 ../src/gui/mainFrame.cpp:504
+#: ../src/gui/mainFrame.cpp:516 ../src/gui/mainFrame.cpp:518
 msgid "Toggle plot list"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:504
+#: ../src/gui/mainFrame.cpp:518
 msgid "&Plot List\tAlt+P"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:510
+#: ../src/gui/mainFrame.cpp:524
 msgid "&Legend\tCtrl+L"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:510
+#: ../src/gui/mainFrame.cpp:524
 msgid "Toggle Legend display"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:512
+#: ../src/gui/mainFrame.cpp:526
 msgid "P&lot..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:513
+#: ../src/gui/mainFrame.cpp:527
 msgid "&Axis\tCtrl+Shift+I"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:513
+#: ../src/gui/mainFrame.cpp:527
 msgid "Toggle World Axis display"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:518
+#: ../src/gui/mainFrame.cpp:532
 msgid "&Fullscreen mode\tF11"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:518 ../src/gui/mainFrame.cpp:520
+#: ../src/gui/mainFrame.cpp:532 ../src/gui/mainFrame.cpp:534
 msgid "Next fullscreen mode: with toolbars"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:520
+#: ../src/gui/mainFrame.cpp:534
 msgid "&Fullscreen mode\tCtrl+Shift+F"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:525
+#: ../src/gui/mainFrame.cpp:539
 msgid "&Undo\tCtrl+Z"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:527
+#: ../src/gui/mainFrame.cpp:541
 msgid "&Redo\tCtrl+Y"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:530
+#: ../src/gui/mainFrame.cpp:544
 msgid "&Range"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:533
+#: ../src/gui/mainFrame.cpp:547
 msgid "&Preferences"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:535
+#: ../src/gui/mainFrame.cpp:549
 msgid "&Edit"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:538
+#: ../src/gui/mainFrame.cpp:552
 msgid "&View"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:540
+#: ../src/gui/mainFrame.cpp:554
 msgid "&Help...\tCtrl+H"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:540
+#: ../src/gui/mainFrame.cpp:554
 msgid "Show help files and documentation"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:541
+#: ../src/gui/mainFrame.cpp:555
 msgid "&Contact..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:541
+#: ../src/gui/mainFrame.cpp:555
 msgid "Open contact page"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:543
+#: ../src/gui/mainFrame.cpp:557
 msgid "&About..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:543
+#: ../src/gui/mainFrame.cpp:557
 msgid "Information about this program"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:544
+#: ../src/gui/mainFrame.cpp:558
 msgid "&Help"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:546
+#: ../src/gui/mainFrame.cpp:560
 msgid "Stashed Filters"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:551
+#: ../src/gui/mainFrame.cpp:565
 msgid "New Filters"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:580
-msgid "Last Outputs"
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:582
+#: ../src/gui/mainFrame.cpp:595
 msgid "Auto Refresh"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:588
+#: ../src/gui/mainFrame.cpp:601
 msgid "Filter settings"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:591
+#: ../src/gui/mainFrame.cpp:604
 msgid "Camera Name"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:598
+#: ../src/gui/mainFrame.cpp:611
 msgid "3D Post-processing"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:600
+#: ../src/gui/mainFrame.cpp:613
 msgid "Enable Cropping"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:602 ../src/gui/mainFrame.cpp:613
+#: ../src/gui/mainFrame.cpp:615 ../src/gui/mainFrame.cpp:626
 msgid "x-y"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:603 ../src/gui/mainFrame.cpp:614
+#: ../src/gui/mainFrame.cpp:616 ../src/gui/mainFrame.cpp:627
 msgid "x-z"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:604 ../src/gui/mainFrame.cpp:615
+#: ../src/gui/mainFrame.cpp:617 ../src/gui/mainFrame.cpp:628
 msgid "y-x"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:605 ../src/gui/mainFrame.cpp:616
+#: ../src/gui/mainFrame.cpp:618 ../src/gui/mainFrame.cpp:629
 msgid "y-z"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:606 ../src/gui/mainFrame.cpp:617
+#: ../src/gui/mainFrame.cpp:619 ../src/gui/mainFrame.cpp:630
 msgid "z-x"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:607 ../src/gui/mainFrame.cpp:618
+#: ../src/gui/mainFrame.cpp:620 ../src/gui/mainFrame.cpp:631
 msgid "z-y"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:622
+#: ../src/gui/mainFrame.cpp:635
 msgid "Use camera coordinates"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:623
+#: ../src/gui/mainFrame.cpp:636
 msgid "dX"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:625
+#: ../src/gui/mainFrame.cpp:638
 msgid "dY"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:627
+#: ../src/gui/mainFrame.cpp:640
 msgid "dZ"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:629
+#: ../src/gui/mainFrame.cpp:642
 msgid "Enable Anaglyphic Stereo"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:630
+#: ../src/gui/mainFrame.cpp:643
 msgid "Flip Channels"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:631
+#: ../src/gui/mainFrame.cpp:644
 msgid "Anaglyph Mode"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:633
+#: ../src/gui/mainFrame.cpp:646
 msgid "Red-Blue"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:634
+#: ../src/gui/mainFrame.cpp:647
 msgid "Red-Green"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:635
+#: ../src/gui/mainFrame.cpp:648
 msgid "Red-Cyan"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:636
+#: ../src/gui/mainFrame.cpp:649
 msgid "Green-Magenta"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:640
+#: ../src/gui/mainFrame.cpp:653
 msgid "Baseline Separation"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:642 ../src/backend/filters/voxelise.cpp:982
-#: ../src/backend/filters/voxelise.cpp:1142
-#: ../src/backend/filters/compositionProfile.cpp:1109
-#: ../src/backend/filters/boundingBox.cpp:648
-#: ../src/backend/filters/annotation.cpp:901
-#: ../src/backend/filters/dataLoad.cpp:651
-#: ../src/backend/filters/spectrumPlot.cpp:459
+#: ../src/gui/mainFrame.cpp:655 ../src/backend/filters/voxelise.cpp:937
+#: ../src/backend/filters/voxelise.cpp:1105
+#: ../src/backend/filters/compositionProfile.cpp:1143
+#: ../src/backend/filters/boundingBox.cpp:654
+#: ../src/backend/filters/annotation.cpp:906
+#: ../src/backend/filters/dataLoad.cpp:666
+#: ../src/backend/filters/spectrumPlot.cpp:675
 msgid "Appearance"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:643
+#: ../src/gui/mainFrame.cpp:656
 msgid "Smooth && translucent objects"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:645
+#: ../src/gui/mainFrame.cpp:658
 msgid "3D lighting"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:648
+#: ../src/gui/mainFrame.cpp:661
 msgid "Performance"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:649
+#: ../src/gui/mainFrame.cpp:662
 msgid "Fast and weak randomisation."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:651
+#: ../src/gui/mainFrame.cpp:664
 msgid "Limit Output Pts"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:657
+#: ../src/gui/mainFrame.cpp:670
 msgid "Filter caching"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:659
+#: ../src/gui/mainFrame.cpp:672
 msgid "Max. Ram usage (%)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:718
-msgid "Type"
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:719
-msgid "Num"
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:734
+#: ../src/gui/mainFrame.cpp:743
 msgid "Warning: Your configuration file appears to be invalid:\n"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:735
+#: ../src/gui/mainFrame.cpp:744
 msgid "\tConfig Load: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:984
+#: ../src/gui/mainFrame.cpp:1018
 msgid "Current state has not been saved, would you like to save it now?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:985
+#: ../src/gui/mainFrame.cpp:1019
 msgid "State changed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1005
+#: ../src/gui/mainFrame.cpp:1037
 msgid "Readable files (*.xml, *.pos, *.txt,*.csv, *.ato)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1007
+#: ../src/gui/mainFrame.cpp:1039
 msgid "XML State File (*.xml)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1008
+#: ../src/gui/mainFrame.cpp:1040
 msgid "POS File (*.pos)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1009
+#: ../src/gui/mainFrame.cpp:1041
 msgid "LAWATAP ATO File (*.ato)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1010
+#: ../src/gui/mainFrame.cpp:1042
 msgid "Text File (*.txt, *.csv)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1011
+#: ../src/gui/mainFrame.cpp:1043
 msgid "All Files (*)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1024 ../src/gui/mainFrame.cpp:1080
+#: ../src/gui/mainFrame.cpp:1056 ../src/gui/mainFrame.cpp:1112
 msgid "Select Data or State File..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1068 ../src/gui/mainFrame.cpp:1407
-msgid "Loaded file."
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:1081
+#: ../src/gui/mainFrame.cpp:1113
 msgid ""
 "3Depict file (*.xml, *.pos,*.txt)|*.xml;*.pos;*.txt|POS File (*.pos)|*.pos|"
 "XML State File (*.xml)|*.xml|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1092
+#: ../src/gui/mainFrame.cpp:1124
 msgid "Merged file."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1199
+#: ../src/gui/mainFrame.cpp:1231
 msgid "Tip: You can use ⌘ (command) to merge"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1201
+#: ../src/gui/mainFrame.cpp:1233
 msgid "Tip: You can use ctrl to merge"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1241
+#: ../src/gui/mainFrame.cpp:1276
 msgid "Load error"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1242
+#: ../src/gui/mainFrame.cpp:1277
 msgid ""
 "Error loading state file.\n"
 "See console for more info."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1250
+#: ../src/gui/mainFrame.cpp:1285
 msgid ""
 "This state file contains filters that can be unsafe to run\n"
 "Do you wish to remove these before continuing?."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1251
+#: ../src/gui/mainFrame.cpp:1286
 msgid "Security warning"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1468 ../src/gui/mainFrame.cpp:1564
-#: ../src/gui/mainFrame.cpp:1997
+#: ../src/gui/mainFrame.cpp:1469 ../src/gui/mainFrame.cpp:1565
+#: ../src/gui/mainFrame.cpp:1990
 msgid "Unable to save"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1469
+#: ../src/gui/mainFrame.cpp:1470
 msgid "No plot available. Please create a plot before exporting."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1473
+#: ../src/gui/mainFrame.cpp:1474
 msgid "Save plot..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1474
+#: ../src/gui/mainFrame.cpp:1475
 msgid ""
 "By Extension (svg,png)|*.svg;*.png|Scalable Vector Graphics File (*.svg)|*."
 "svg|PNG File (*.png)|*.png|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1528
+#: ../src/gui/mainFrame.cpp:1529
 msgid "Select type for save"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1529
+#: ../src/gui/mainFrame.cpp:1530
 msgid "Choose file type"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1549 ../src/gui/mainFrame.cpp:1606
-#: ../src/gui/mainFrame.cpp:1642
+#: ../src/gui/mainFrame.cpp:1550 ../src/gui/mainFrame.cpp:1607
+#: ../src/gui/mainFrame.cpp:1643
 msgid "Choose resolution"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1565
+#: ../src/gui/mainFrame.cpp:1566
 msgid "Unknown file extension. Please use \"svg\" or \"png\""
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1576
+#: ../src/gui/mainFrame.cpp:1577
 msgid "Saved plot: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1583 ../src/gui/mainFrame.cpp:1635
+#: ../src/gui/mainFrame.cpp:1584 ../src/gui/mainFrame.cpp:1636
 msgid "Save Image..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1584 ../src/gui/mainFrame.cpp:1636
+#: ../src/gui/mainFrame.cpp:1585 ../src/gui/mainFrame.cpp:1637
 msgid "PNG File (*.png)|*.png|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1598
+#: ../src/gui/mainFrame.cpp:1599
 msgid "File already exists. Overwrite?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1599 ../src/gui/mainFrame.cpp:2385
-#: ../src/gui/mainFrame.cpp:2483 ../src/gui/mainFrame.cpp:2507
+#: ../src/gui/mainFrame.cpp:1600 ../src/gui/mainFrame.cpp:2378
+#: ../src/gui/mainFrame.cpp:2475 ../src/gui/mainFrame.cpp:2498
 msgid "Overwrite?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1626 ../src/gui/mainFrame.cpp:1702
+#: ../src/gui/mainFrame.cpp:1627 ../src/gui/mainFrame.cpp:1703
 msgid "Saved 3D View :"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1656
+#: ../src/gui/mainFrame.cpp:1657
 msgid "Program limitation"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1657
+#: ../src/gui/mainFrame.cpp:1658
 msgid ""
 "Limitation on the screenshot dimension; please ensure that both width and "
 "height exceed the initial values,\n"
@@ -1657,371 +1642,367 @@ msgid ""
 " If this bothers, please submit a bug."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1674
+#: ../src/gui/mainFrame.cpp:1675
 msgid "Number of frames"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1829
+#: ../src/gui/mainFrame.cpp:1835
 msgid "Cannot animate with no filters."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1908
+#: ../src/gui/mainFrame.cpp:1913
 msgid "Animating"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1909
+#: ../src/gui/mainFrame.cpp:1914
 msgid "Performing refresh"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1938
+#: ../src/gui/mainFrame.cpp:1940
 msgid "Filter property change failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1958
+#: ../src/gui/mainFrame.cpp:1962
 msgid "Refresh failed on frame :"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1983
-msgid "Scene generation failed"
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:1984
-msgid "Unable to generate scene for frame "
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:1998
+#: ../src/gui/mainFrame.cpp:1991
 msgid "Image save failed for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2023
+#: ../src/gui/mainFrame.cpp:2016
 msgid "Ion save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2024
+#: ../src/gui/mainFrame.cpp:2017
 msgid "Unable to save ions for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2055
+#: ../src/gui/mainFrame.cpp:2048
 msgid "Plot save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2056
+#: ../src/gui/mainFrame.cpp:2049
 msgid "Unable to save plot or frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2097
+#: ../src/gui/mainFrame.cpp:2090
 msgid "Range save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2098
+#: ../src/gui/mainFrame.cpp:2091
 msgid "Unable to save range for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2127
+#: ../src/gui/mainFrame.cpp:2120
 msgid "Voxel save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2128
+#: ../src/gui/mainFrame.cpp:2121
 msgid "Unable to save voxels for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2158
+#: ../src/gui/mainFrame.cpp:2150
 msgid "Animate failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2183 ../src/gui/mainFrame.cpp:2341
-#: ../src/gui/mainFrame.cpp:2437
+#: ../src/gui/mainFrame.cpp:2174 ../src/gui/mainFrame.cpp:2333
+#: ../src/gui/mainFrame.cpp:2430
 msgid "No filters means no data to export"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2197
+#: ../src/gui/mainFrame.cpp:2188
 msgid "Package name"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2198
+#: ../src/gui/mainFrame.cpp:2189
 msgid "Package directory name"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2200
+#: ../src/gui/mainFrame.cpp:2191
 msgid "AnalysisPackage"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2213
+#: ../src/gui/mainFrame.cpp:2204
 msgid "Package folder already exists, won't overwrite."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2214
+#: ../src/gui/mainFrame.cpp:2205
 msgid "Not available"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2239
+#: ../src/gui/mainFrame.cpp:2230
 msgid ""
 "Package folder creation failed\n"
 "check writing to this location is possible."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2240
+#: ../src/gui/mainFrame.cpp:2231
 msgid "Folder creation failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2259
+#: ../src/gui/mainFrame.cpp:2251
 msgid "Copying"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2260
+#: ../src/gui/mainFrame.cpp:2252
 msgid "Copying referenced files"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2319
+#: ../src/gui/mainFrame.cpp:2311
 msgid "Error copying file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2328
+#: ../src/gui/mainFrame.cpp:2320
 msgid "Saved package: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2351
+#: ../src/gui/mainFrame.cpp:2344
 msgid "Export"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2356
+#: ../src/gui/mainFrame.cpp:2349
 msgid "POS Data (*.pos)|*.pos|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2384 ../src/gui/mainFrame.cpp:2482
+#: ../src/gui/mainFrame.cpp:2377 ../src/gui/mainFrame.cpp:2474
 msgid "File already exists, overwrite?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2416
+#: ../src/gui/mainFrame.cpp:2409
 msgid "Saved ions: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2441
+#: ../src/gui/mainFrame.cpp:2434
 msgid "Export Ranges"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2464
+#: ../src/gui/mainFrame.cpp:2456
 msgid "Save state..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2465
+#: ../src/gui/mainFrame.cpp:2457
 msgid "XML state file (*.xml)|*.xml|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2506
+#: ../src/gui/mainFrame.cpp:2497
 msgid "Files have been referred to using relative paths. Keep relative paths?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2539
+#: ../src/gui/mainFrame.cpp:2530
 msgid "Saved state: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2616
+#: ../src/gui/mainFrame.cpp:2606
 msgid "Range editor"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2871
+#: ../src/gui/mainFrame.cpp:2863
 msgid "Manual not found locally. Launching web browser"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2880
+#: ../src/gui/mainFrame.cpp:2872
 msgid "Opening contact page in external web browser"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2892
+#: ../src/gui/mainFrame.cpp:2880
 msgid "No filter stashes to edit."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2896
+#: ../src/gui/mainFrame.cpp:2884
 msgid "Filter Stashes"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2925
+#: ../src/gui/mainFrame.cpp:2901
 msgid "Quick and dirty analysis for point data."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2935
+#: ../src/gui/mainFrame.cpp:2911
 msgid "Compiled with wx Version: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2956
+#: ../src/gui/mainFrame.cpp:2932
 msgid "Press enter to store new stash"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2962
+#: ../src/gui/mainFrame.cpp:2938
 msgid "Press enter to restore stash"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2997
+#: ../src/gui/mainFrame.cpp:2971
 msgid "Unable to create stash, selection invalid"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3005
+#: ../src/gui/mainFrame.cpp:2978
 msgid "Created new filter tree stash"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3132
+#: ../src/gui/mainFrame.cpp:3083
 msgid "Filter type not a data source - can't be at tree base"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3309
+#: ../src/gui/mainFrame.cpp:3223
 msgid "Moving - Hold ⌘ (command) to copy"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3311
+#: ../src/gui/mainFrame.cpp:3225
 msgid "Moving - Hold control to copy"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3630
+#: ../src/gui/mainFrame.cpp:3545
 msgid "Press enter to store new camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3632
+#: ../src/gui/mainFrame.cpp:3547
 msgid "Press enter to restore camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3657 ../src/gui/mainFrame.cpp:3698
+#: ../src/gui/mainFrame.cpp:3572 ../src/gui/mainFrame.cpp:3613
 msgid "Restored camera: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3677
+#: ../src/gui/mainFrame.cpp:3590
 msgid "Stored camera: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3759
+#: ../src/gui/mainFrame.cpp:3676
 msgid "Select an item from the filter tree before choosing a new filter"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3761
+#: ../src/gui/mainFrame.cpp:3678
 msgid "Load data source (file->open) before choosing a new filter"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3785
+#: ../src/gui/mainFrame.cpp:3704
 msgid "Select RNG File..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3806
+#: ../src/gui/mainFrame.cpp:3725
 msgid "Failed reading range file."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3810
+#: ../src/gui/mainFrame.cpp:3729
 msgid "Error loading file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3870 ../src/gui/mainFrame.cpp:3937
-#: ../src/gui/mainFrame.cpp:5268 ../src/gui/mainFrame.cpp:5802
+#: ../src/gui/mainFrame.cpp:3788 ../src/gui/mainFrame.cpp:3882
+#: ../src/gui/mainFrame.cpp:5284 ../src/gui/mainFrame.cpp:5803
 msgid "Cons."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3904
+#: ../src/gui/mainFrame.cpp:3844
 msgid "Refresh Aborted."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3941
+#: ../src/gui/mainFrame.cpp:3886
 msgid "*Cons."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3943
+#: ../src/gui/mainFrame.cpp:3888
 msgid "§Cons."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4037
+#: ../src/gui/mainFrame.cpp:3945
+msgid "Complete"
+msgstr ""
+
+#: ../src/gui/mainFrame.cpp:4030
 msgid "msgs"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4077
+#: ../src/gui/mainFrame.cpp:4071
 msgid "Autosave complete."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4273
-msgid "Aborted."
+#: ../src/gui/mainFrame.cpp:4281
+msgid "Aborting...."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4324
+#: ../src/gui/mainFrame.cpp:4344
 msgid "Updated."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4331
+#: ../src/gui/mainFrame.cpp:4352
 msgid "\\% Done (Esc aborts)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4333
+#: ../src/gui/mainFrame.cpp:4354
 msgid "\\% Done"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4609
+#: ../src/gui/mainFrame.cpp:4624
 msgid "Tip: You can shift-click to force full refresh, if required"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4672
+#: ../src/gui/mainFrame.cpp:4686
 msgid "No data to save"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4842
+#: ../src/gui/mainFrame.cpp:4860
 msgid "Aborting..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4848
+#: ../src/gui/mainFrame.cpp:4866
 msgid ""
 "Waiting for refresh to abort. Exiting could lead to the program "
 "backgrounding. Exit anyway? "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4849 ../src/gui/mainFrame.cpp:4869
+#: ../src/gui/mainFrame.cpp:4867 ../src/gui/mainFrame.cpp:4887
 msgid "Confirmation request"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4868
+#: ../src/gui/mainFrame.cpp:4886
 msgid "Are you sure you wish to exit 3Depict?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5296
+#: ../src/gui/mainFrame.cpp:5312
 msgid "Update Notice: New version "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5296
+#: ../src/gui/mainFrame.cpp:5312
 msgid " found online."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5300
+#: ../src/gui/mainFrame.cpp:5316
 msgid "Online Check: "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5300
+#: ../src/gui/mainFrame.cpp:5316
 msgid " is up-to-date."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5395
+#: ../src/gui/mainFrame.cpp:5406
 msgid "An auto-save state was found, would you like to restore it?."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5396
+#: ../src/gui/mainFrame.cpp:5407
 msgid "Autosave"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5403
+#: ../src/gui/mainFrame.cpp:5414
 msgid "Unable to load autosave file.."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5600
+#: ../src/gui/mainFrame.cpp:5605
 msgid "List of available filters"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5602
+#: ../src/gui/mainFrame.cpp:5607
 msgid "Tree - drag to move items, hold ⌘ for copy. Tap delete to remove items"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5604
+#: ../src/gui/mainFrame.cpp:5609
 msgid ""
 "Tree - drag to move items, hold Ctrl for copy. Tap delete to remove items."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5606
+#: ../src/gui/mainFrame.cpp:5611
 msgid ""
 "Enable/Disable automatic updates of data when filter change takes effect"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5609
+#: ../src/gui/mainFrame.cpp:5614
 msgid ""
 "Enable/Disable \"Alpha blending\" (transparency) in rendering system. "
 "Blending is used to smooth objects (avoids artefacts known as \"jaggies\") "
@@ -2029,14 +2010,14 @@ msgid ""
 "but look more blocky"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5610
+#: ../src/gui/mainFrame.cpp:5615
 msgid ""
 "Enable/Disable lighting calculations in rendering, for objects that request "
 "this. Lighting provides important depth cues for objects comprised of 3D "
 "surfaces. Disabling may allow faster rendering in complex scenes"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5611
+#: ../src/gui/mainFrame.cpp:5616
 msgid ""
 "Enable/Disable weak randomisation (Galois linear feedback shift register). "
 "Strong randomisation uses a much slower random selection method, but "
@@ -2044,14 +2025,14 @@ msgid ""
 "recommended for final analyses"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5613
+#: ../src/gui/mainFrame.cpp:5618
 msgid ""
 "Limit the number of points that can be displayed in the 3D  scene. Does not "
 "affect filter tree calculations. Disabling this can severely reduce "
 "performance, due to large numbers of points being visible at once."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5614
+#: ../src/gui/mainFrame.cpp:5619
 msgid ""
 "Enable/Disable caching of intermediate results during filter updates. "
 "Disabling caching will use less system RAM, though changes to any filter "
@@ -2059,172 +2040,174 @@ msgid ""
 "computations"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5616
+#: ../src/gui/mainFrame.cpp:5621
 msgid "Camera data information"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5620
+#: ../src/gui/mainFrame.cpp:5625
 msgid "Enable/disable visual effects on final 3D output"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5622
+#: ../src/gui/mainFrame.cpp:5627
 msgid "Enable cropping post-process effect"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5625
+#: ../src/gui/mainFrame.cpp:5630
 msgid ""
 "Colour based 3D effect enable/disable - requires appropriate colour filter "
 "3D glasses."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5626
+#: ../src/gui/mainFrame.cpp:5631
 msgid "Glasses colour mode"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5628
+#: ../src/gui/mainFrame.cpp:5633
 msgid ""
 "Level of separation between left and right images, which sets 3D depth to "
 "visual distortion tradeoff"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5632
+#: ../src/gui/mainFrame.cpp:5637
 msgid "X"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5633
+#: ../src/gui/mainFrame.cpp:5638
 msgid "Y"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5634
+#: ../src/gui/mainFrame.cpp:5639
 msgid "Save raw data to file"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5635
+#: ../src/gui/mainFrame.cpp:5640
 msgid "Copy raw data to clipboard"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5636
+#: ../src/gui/mainFrame.cpp:5641
 msgid "Manage \"stashed\" data."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5637
+#: ../src/gui/mainFrame.cpp:5642
 msgid "Program text output"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5638
+#: ../src/gui/mainFrame.cpp:5643
 msgid "Select active camera, or type to create new named camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5639
+#: ../src/gui/mainFrame.cpp:5644
 msgid "Remove the selected camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5640
+#: ../src/gui/mainFrame.cpp:5645
 msgid "Perform cropping from coordinate frame of camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5641
+#: ../src/gui/mainFrame.cpp:5646
 msgid ""
 "Set the maximum amount of RAM to use in order to speed repeat computations"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5642
+#: ../src/gui/mainFrame.cpp:5647
 msgid "Collapse the filter tree"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5643
+#: ../src/gui/mainFrame.cpp:5648
 msgid "Expand the filter tree"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5644
+#: ../src/gui/mainFrame.cpp:5649
 msgid "Process the filter tree, hold shift to purge cached filter data"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5762
+#: ../src/gui/mainFrame.cpp:5763
 msgid "Crop"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5763
+#: ../src/gui/mainFrame.cpp:5764
 msgid "Stereo"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5780
-#: ../src/backend/filters/externalProgram.cpp:577
-#: ../src/backend/filters/ionColour.cpp:312
-#: ../src/backend/filters/spectrumPlot.cpp:418
+#: ../src/gui/mainFrame.cpp:5781
+#: ../src/backend/filters/externalProgram.cpp:596
+#: ../src/backend/filters/ionColour.cpp:316
+#: ../src/backend/filters/spectrumPlot.cpp:604
 msgid "Data"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5781
+#: ../src/gui/mainFrame.cpp:5782
 msgid "Cam"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5782
+#: ../src/gui/mainFrame.cpp:5783
 msgid "Post"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5783
+#: ../src/gui/mainFrame.cpp:5784
 msgid "Tools"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5801
+#: ../src/gui/mainFrame.cpp:5802
 msgid "Raw"
 msgstr ""
 
-#: ../src/gui/mathglPane.cpp:241
+#: ../src/gui/mathglPane.cpp:259
 msgid "No plots selected."
 msgstr ""
 
-#: ../src/gui/mathglPane.cpp:1165
+#: ../src/gui/mathglPane.cpp:1198
 msgid ""
 "Unable to allocate requested memory.\n"
 " Try a lower resolution, or save as vector (SVG)."
 msgstr ""
 
-#: ../src/gui/mathglPane.cpp:1167
+#: ../src/gui/mathglPane.cpp:1200
 msgid "Plotting functions returned an error:\n"
 msgstr ""
 
-#: ../src/gui/mathglPane.cpp:1169
+#: ../src/gui/mathglPane.cpp:1202
 msgid "File readback check failed"
 msgstr ""
 
-#: ../src/gui/mathglPane.cpp:1171
+#: ../src/gui/mathglPane.cpp:1204
 msgid "Filesize during readback appears to be zero."
 msgstr ""
 
-#: ../src/3Depict.cpp:381
+#: ../src/3Depict.cpp:375
 msgid "File : "
 msgstr ""
 
-#: ../src/3Depict.cpp:381
+#: ../src/3Depict.cpp:375
 msgid " does not exist. Skipping"
 msgstr ""
 
-#: ../src/backend/configFile.cpp:186
+#: ../src/backend/configFile.cpp:187
 msgid "Config file present, but is not valid (root node test)"
 msgstr ""
 
-#: ../src/backend/configFile.cpp:227
+#: ../src/backend/configFile.cpp:228
 msgid "Unable to interpret recent file entry"
 msgstr ""
 
-#: ../src/backend/configFile.cpp:267
+#: ../src/backend/configFile.cpp:268
 msgid "Unable to determine filter type in defaults listing."
 msgstr ""
 
-#: ../src/backend/configFile.cpp:604
+#: ../src/backend/configFile.cpp:605
 msgid "Online access for non win32/apple platforms is intentionally disabled, "
 msgstr ""
 
-#: ../src/backend/configFile.cpp:605
+#: ../src/backend/configFile.cpp:606
 msgid ""
 "regardless of the settings you use here. Use your package manager to keep up-"
 "to-date"
 msgstr ""
 
-#: ../src/backend/plot.cpp:28 ../src/backend/filters/voxelise.cpp:123
-#: ../src/backend/filters/voxelise.cpp:133
+#: ../src/backend/plot.cpp:28 ../src/backend/filters/algorithms/mass.cpp:25
+#: ../src/backend/filters/voxelise.cpp:124
+#: ../src/backend/filters/voxelise.cpp:130
+#: ../src/backend/filters/spectrumPlot.cpp:76
 msgid "None"
 msgstr ""
 
@@ -2260,2557 +2243,2705 @@ msgstr ""
 msgid "Scatter"
 msgstr ""
 
-#: ../src/backend/plot.cpp:787 ../src/backend/plot.cpp:795
+#: ../src/backend/plot.cpp:739 ../src/backend/plot.cpp:747
 msgid "Multiple data types"
 msgstr ""
 
-#: ../src/backend/plot.cpp:1559
+#: ../src/backend/plot.cpp:1581
 msgid "error"
 msgstr ""
 
-#: ../src/backend/plot.cpp:1772
+#: ../src/backend/plot.cpp:1821
 msgid "Amplitude"
 msgstr ""
 
-#: ../src/backend/filtertree.cpp:1060
+#: ../src/backend/filtertree.cpp:1146
 msgid "WARNING: Skipping node "
 msgstr ""
 
-#: ../src/backend/filtertree.cpp:1060
+#: ../src/backend/filtertree.cpp:1146
 msgid " as it was not recognised"
 msgstr ""
 
-#: ../src/backend/filtertree.cpp:1098
+#: ../src/backend/filtertree.cpp:1184
 msgid "Error processing node: "
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:545
-#: ../src/backend/filters/externalProgram.cpp:559
+#: ../src/backend/filters/externalProgram.cpp:259
+msgid "Collate Input"
+msgstr ""
+
+#: ../src/backend/filters/externalProgram.cpp:348
+msgid "Execute"
+msgstr ""
+
+#: ../src/backend/filters/externalProgram.cpp:389
+msgid "Collate output"
+msgstr ""
+
+#: ../src/backend/filters/externalProgram.cpp:564
+#: ../src/backend/filters/externalProgram.cpp:578
 msgid "Command"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:548
+#: ../src/backend/filters/externalProgram.cpp:567
 msgid ""
 "Full command to send to operating system. See manual for escape sequence "
 "meanings"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:552
+#: ../src/backend/filters/externalProgram.cpp:571
 msgid "Work Dir"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:555
+#: ../src/backend/filters/externalProgram.cpp:574
 msgid "Directory to run the command in"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:562
+#: ../src/backend/filters/externalProgram.cpp:581
 msgid "Cleanup input"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:565
+#: ../src/backend/filters/externalProgram.cpp:584
 msgid "Erase input files when command completed"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:570
+#: ../src/backend/filters/externalProgram.cpp:589
 msgid "Cache"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:573
+#: ../src/backend/filters/externalProgram.cpp:592
 msgid ""
 "Assume program does not alter its output, unless inputs from 3Depict are "
 "altered"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:60
-#: ../src/backend/filters/compositionProfile.cpp:44
+#: ../src/backend/filters/algorithms/mass.cpp:26
+msgid "Flat TOF"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:33
+msgid "INsufficient bins to perform fit"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:34
+msgid "Insufficient counts to perform fit"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:35
+msgid "Insufficient data to perform fit"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:36
+msgid "Data did not appear to be random noise - cannot fit noise level"
+msgstr ""
+
+#: ../src/backend/filters/ionClip.cpp:65
+#: ../src/backend/filters/compositionProfile.cpp:53
 msgid "Sphere"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:61
+#: ../src/backend/filters/ionClip.cpp:66
 msgid "Plane"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:62
-#: ../src/backend/filters/compositionProfile.cpp:43
+#: ../src/backend/filters/ionClip.cpp:67
 msgid "Cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:63
+#: ../src/backend/filters/ionClip.cpp:68
 msgid "Aligned box"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:488
-#: ../src/backend/filters/compositionProfile.cpp:943
+#: ../src/backend/filters/ionClip.cpp:497
+#: ../src/backend/filters/compositionProfile.cpp:971
 msgid "Primitive"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:491
+#: ../src/backend/filters/ionClip.cpp:500
 msgid "Shape of clipping object"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:497
-#: ../src/backend/filters/compositionProfile.cpp:949
+#: ../src/backend/filters/ionClip.cpp:506
+#: ../src/backend/filters/compositionProfile.cpp:977
 msgid "Show Primitive"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:500
+#: ../src/backend/filters/ionClip.cpp:509
 msgid "Display the 3D interaction object"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:505
+#: ../src/backend/filters/ionClip.cpp:514
 msgid "Invert Clip"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:508
+#: ../src/backend/filters/ionClip.cpp:517
 msgid ""
 "Switch between retaining points inside (false) and outside (true) of "
 "primitive"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:522
-#: ../src/backend/filters/compositionProfile.cpp:1005
+#: ../src/backend/filters/ionClip.cpp:531
+#: ../src/backend/filters/compositionProfile.cpp:1034
 msgid "Position for centre of sphere"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:527
-#: ../src/backend/filters/ionClip.cpp:587
-#: ../src/backend/filters/compositionProfile.cpp:988
-#: ../src/backend/filters/compositionProfile.cpp:1010
-#: ../src/backend/filters/spatialAnalysis.cpp:111
-#: ../src/backend/filters/spatialAnalysis.cpp:791
+#: ../src/backend/filters/ionClip.cpp:536
+#: ../src/backend/filters/ionClip.cpp:596
+#: ../src/backend/filters/compositionProfile.cpp:1017
+#: ../src/backend/filters/compositionProfile.cpp:1039
+#: ../src/backend/filters/spatialAnalysis.cpp:121
+#: ../src/backend/filters/spatialAnalysis.cpp:863
 msgid "Radius"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:530
-#: ../src/backend/filters/compositionProfile.cpp:1013
+#: ../src/backend/filters/ionClip.cpp:539
+#: ../src/backend/filters/compositionProfile.cpp:1042
 msgid "Radius of sphere"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:544
+#: ../src/backend/filters/ionClip.cpp:553
 msgid "Position that plane passes through"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:549
+#: ../src/backend/filters/ionClip.cpp:558
 msgid "Plane Normal"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:552
+#: ../src/backend/filters/ionClip.cpp:561
 msgid "Perpendicular direction for plane"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:566
+#: ../src/backend/filters/ionClip.cpp:575
 msgid "Centre of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:571
-#: ../src/backend/filters/compositionProfile.cpp:972
-#: ../src/backend/filters/spatialAnalysis.cpp:782
-#: ../src/backend/filters/transform.cpp:1277
+#: ../src/backend/filters/ionClip.cpp:580
+#: ../src/backend/filters/compositionProfile.cpp:1001
+#: ../src/backend/filters/spatialAnalysis.cpp:854
+#: ../src/backend/filters/transform.cpp:1263
 msgid "Axis"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:574
+#: ../src/backend/filters/ionClip.cpp:583
 msgid "Positive vector for cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:579
-#: ../src/backend/filters/compositionProfile.cpp:980
+#: ../src/backend/filters/ionClip.cpp:588
+#: ../src/backend/filters/compositionProfile.cpp:1009
 msgid "Lock Axis Mag."
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:582
+#: ../src/backend/filters/ionClip.cpp:591
 msgid "Prevent changing length of cylinder during 3D interaction"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:590
-#: ../src/backend/filters/compositionProfile.cpp:991
-#: ../src/backend/filters/spatialAnalysis.cpp:794
+#: ../src/backend/filters/ionClip.cpp:599
+#: ../src/backend/filters/compositionProfile.cpp:1020
+#: ../src/backend/filters/spatialAnalysis.cpp:866
 msgid "Radius of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:603
+#: ../src/backend/filters/ionClip.cpp:612
 msgid "Centre of axis aligned box"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:608
+#: ../src/backend/filters/ionClip.cpp:617
 msgid "Corner offset"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:611
+#: ../src/backend/filters/ionClip.cpp:620
 msgid "Vector to corner of box"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:80
-#: ../src/backend/filters/clusterAnalysis.cpp:1045
+#: ../src/backend/filters/clusterAnalysis.cpp:82
+#: ../src/backend/filters/clusterAnalysis.cpp:1040
 msgid "Size Distribution"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:81
+#: ../src/backend/filters/clusterAnalysis.cpp:83
 msgid "Chemistry Distribution"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:499
+#: ../src/backend/filters/clusterAnalysis.cpp:493
 msgid "No range data. Can't cluster."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:510
+#: ../src/backend/filters/clusterAnalysis.cpp:504
 msgid ""
 "No ranges selected for cluster \"core\". Cannot continue with clustering."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:519
+#: ../src/backend/filters/clusterAnalysis.cpp:513
 msgid ""
 "No ranges selected for cluster \"bulk\". Cannot continue with clustering."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:789
+#: ../src/backend/filters/clusterAnalysis.cpp:679
+msgid "Morphology Plot"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:680
+msgid "\\lambda_1:\\lambda_2 ratio"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:681
+msgid "\\lambda_2:\\lambda_3 ratio"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:726
+msgid "No clusters had sufficient dimensionality to compute singular values"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:784
 msgid "Found :"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:791
+#: ../src/backend/filters/clusterAnalysis.cpp:786
 msgid " clusters"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:873
+#: ../src/backend/filters/clusterAnalysis.cpp:868
 msgid "Compositions (fractional, core+bulk)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:875
+#: ../src/backend/filters/clusterAnalysis.cpp:870
 msgid "Compositions (fractional, core only)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:893
+#: ../src/backend/filters/clusterAnalysis.cpp:888
 msgid "Frequencies (core+bulk)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:924
+#: ../src/backend/filters/clusterAnalysis.cpp:919
 msgid "Core Link + Erode"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:928
-#: ../src/backend/filters/clusterAnalysis.cpp:936
-#: ../src/backend/filters/spatialAnalysis.cpp:541
-#: ../src/backend/filters/spatialAnalysis.cpp:549
-#: ../src/backend/filters/transform.cpp:1140
-#: ../src/backend/filters/ionInfo.cpp:441
+#: ../src/backend/filters/clusterAnalysis.cpp:923
+#: ../src/backend/filters/clusterAnalysis.cpp:931
+#: ../src/backend/filters/spatialAnalysis.cpp:605
+#: ../src/backend/filters/spatialAnalysis.cpp:613
+#: ../src/backend/filters/transform.cpp:1126
+#: ../src/backend/filters/ionInfo.cpp:546
 msgid "Algorithm"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:932
+#: ../src/backend/filters/clusterAnalysis.cpp:927
 msgid "Cluster algorithm mode"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:943
+#: ../src/backend/filters/clusterAnalysis.cpp:938
 msgid "Core Classify"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:946
+#: ../src/backend/filters/clusterAnalysis.cpp:941
 msgid ""
 "Enable core-classifcation pre-step in clustering (Stephenson et al, 2007)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:952
+#: ../src/backend/filters/clusterAnalysis.cpp:947
 msgid "Core Classify Dist"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:955
+#: ../src/backend/filters/clusterAnalysis.cpp:950
 msgid "Restrict only atoms by distance to be cluster sources"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:960
+#: ../src/backend/filters/clusterAnalysis.cpp:955
 msgid "Classify Knn Max"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:963
+#: ../src/backend/filters/clusterAnalysis.cpp:958
 msgid ""
 "Require that the kth NN (this number) is within the classify distance, to be "
 "a cluster source"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:969
+#: ../src/backend/filters/clusterAnalysis.cpp:964
 msgid "Core Link Dist"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:972
+#: ../src/backend/filters/clusterAnalysis.cpp:967
 msgid "Distance between clusters to allow linking"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:977
+#: ../src/backend/filters/clusterAnalysis.cpp:972
 msgid "Bulk Link"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:980
-#: ../src/backend/filters/clusterAnalysis.cpp:998
+#: ../src/backend/filters/clusterAnalysis.cpp:975
+#: ../src/backend/filters/clusterAnalysis.cpp:993
 msgid "Enable  linking of non-cluster species - eg for composition analysis "
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:987
+#: ../src/backend/filters/clusterAnalysis.cpp:982
 msgid "Bulk Link (Envelope) Dist"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:990
+#: ../src/backend/filters/clusterAnalysis.cpp:985
 msgid ""
 "Distance from core points that form cluster that is used to grab surrounding "
 "bulk points"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:995
+#: ../src/backend/filters/clusterAnalysis.cpp:990
 msgid "Erosion"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1004
+#: ../src/backend/filters/clusterAnalysis.cpp:999
 msgid "Erode Dist"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1007
+#: ../src/backend/filters/clusterAnalysis.cpp:1002
 msgid ""
 "Distance from unclustered material in which bulk points are eroded from "
 "cluster"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1013
+#: ../src/backend/filters/clusterAnalysis.cpp:1008
 msgid "Clustering Params"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1018
+#: ../src/backend/filters/clusterAnalysis.cpp:1013
 msgid "Size Cropping"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1021
+#: ../src/backend/filters/clusterAnalysis.cpp:1016
 msgid "Remove clusters based upon size distribution"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1028
+#: ../src/backend/filters/clusterAnalysis.cpp:1023
 msgid "Min Size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1031
+#: ../src/backend/filters/clusterAnalysis.cpp:1026
 msgid "Remove clusters below this size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1036
+#: ../src/backend/filters/clusterAnalysis.cpp:1031
 msgid "Max Size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1039
+#: ../src/backend/filters/clusterAnalysis.cpp:1034
 msgid "Remove clusters above this size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1048
+#: ../src/backend/filters/clusterAnalysis.cpp:1043
 msgid "Show number of clusters as a function of cluster size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1054
+#: ../src/backend/filters/clusterAnalysis.cpp:1049
 msgid "Log Scale"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1057
+#: ../src/backend/filters/clusterAnalysis.cpp:1052
 msgid "Use logarithmic scale for size distribution"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1075
+#: ../src/backend/filters/clusterAnalysis.cpp:1059
+msgid "Morphology Dist."
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:1062
+msgid "Create a plot showing cluster aspect ratio"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:1068
 msgid "Cluster Id"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1078
+#: ../src/backend/filters/clusterAnalysis.cpp:1071
 msgid "Assign cluster output a unique per-cluster value (id)."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1085
+#: ../src/backend/filters/clusterAnalysis.cpp:1078
 msgid "Chemistry Dist."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1088
+#: ../src/backend/filters/clusterAnalysis.cpp:1081
 msgid "Create a plot showing chemistry for each cluster size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1095
-#: ../src/backend/filters/compositionProfile.cpp:1053
-#: ../src/backend/filters/spatialAnalysis.cpp:849
-#: ../src/backend/filters/ionInfo.cpp:412
+#: ../src/backend/filters/clusterAnalysis.cpp:1088
+#: ../src/backend/filters/compositionProfile.cpp:1087
+#: ../src/backend/filters/spatialAnalysis.cpp:921
+#: ../src/backend/filters/ionInfo.cpp:462
 msgid "Normalise"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1098
+#: ../src/backend/filters/clusterAnalysis.cpp:1091
 msgid "Convert cluster counts to composition"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1104
+#: ../src/backend/filters/clusterAnalysis.cpp:1097
 msgid "Postprocess"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1123
+#: ../src/backend/filters/clusterAnalysis.cpp:1116
 msgid "If selected, use as \"core\" ion type (can make clusters)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1128
+#: ../src/backend/filters/clusterAnalysis.cpp:1121
 msgid "Core Ranges"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1142
+#: ../src/backend/filters/clusterAnalysis.cpp:1135
 msgid ""
 "If selected, use as \"bulk\" ion type (can be included in existing clusters)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1147
+#: ../src/backend/filters/clusterAnalysis.cpp:1140
 msgid "Bulk Ranges"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1163
+#: ../src/backend/filters/clusterAnalysis.cpp:1156
 msgid "Max. Sep + Erode"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1891
+#: ../src/backend/filters/clusterAnalysis.cpp:1894
 msgid " --------------------------- Parameter selection notice ------------- "
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1892
+#: ../src/backend/filters/clusterAnalysis.cpp:1895
 msgid "You have specified a bulk distance larger than half your link distance."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1893
+#: ../src/backend/filters/clusterAnalysis.cpp:1896
 msgid ""
 "You can do this; thats OK, but the output is no longer independent of the "
 "computational process;"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1894
+#: ../src/backend/filters/clusterAnalysis.cpp:1897
 msgid ""
 "This will be a problem in the case where two or more clusters can equally "
 "lay claim to a \"bulk\" ion. "
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1895
+#: ../src/backend/filters/clusterAnalysis.cpp:1898
 msgid ""
 " If your inter-cluster distance is sufficiently large (larger than your bulk "
 "linking distance), then you can get away with this."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1896
+#: ../src/backend/filters/clusterAnalysis.cpp:1899
 msgid ""
 " In theory it is possible to \"join\" the clusters, but this has not been "
 "implemented for speed reasons."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1897
+#: ../src/backend/filters/clusterAnalysis.cpp:1900
 msgid ""
 "If you want this, please contact the author, or just use the source to add "
 "this in yourself."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1898
+#: ../src/backend/filters/clusterAnalysis.cpp:1901
 msgid "---------------------------------------------------------------------- "
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1908
-#: ../src/backend/filters/spatialAnalysis.cpp:1971
-#: ../src/backend/filters/spatialAnalysis.cpp:2314
-#: ../src/backend/filters/spatialAnalysis.cpp:2605
-#: ../src/backend/filters/spatialAnalysis.cpp:3293
-#: ../src/backend/filters/transform.cpp:991
+#: ../src/backend/filters/clusterAnalysis.cpp:1911
+#: ../src/backend/filters/spatialAnalysis.cpp:2060
+#: ../src/backend/filters/spatialAnalysis.cpp:2489
+#: ../src/backend/filters/spatialAnalysis.cpp:2775
+#: ../src/backend/filters/spatialAnalysis.cpp:3449
+#: ../src/backend/filters/transform.cpp:977
 msgid "Collate"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1927
+#: ../src/backend/filters/clusterAnalysis.cpp:1930
 msgid "Build Core"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1945
-msgid "Classify Core"
-msgstr ""
-
-#: ../src/backend/filters/clusterAnalysis.cpp:2038
-msgid "Build Bulk"
-msgstr ""
-
-#: ../src/backend/filters/clusterAnalysis.cpp:2058
+#: ../src/backend/filters/clusterAnalysis.cpp:1958
 msgid "Core"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2203
+#: ../src/backend/filters/clusterAnalysis.cpp:2097
 msgid "Bulk"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2333
+#: ../src/backend/filters/clusterAnalysis.cpp:2227
 msgid "Erode"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2407
+#: ../src/backend/filters/clusterAnalysis.cpp:2301
 msgid "Re-Collate"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2675
-#: ../src/backend/filters/clusterAnalysis.cpp:2879
+#: ../src/backend/filters/clusterAnalysis.cpp:2358
+msgid "Classify Core"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:2449
+msgid "Build Bulk"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:2694
+#: ../src/backend/filters/clusterAnalysis.cpp:2893
 msgid "Cluster Size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2676
-#: ../src/backend/filters/clusterAnalysis.cpp:2883
+#: ../src/backend/filters/clusterAnalysis.cpp:2695
+#: ../src/backend/filters/clusterAnalysis.cpp:2897
 msgid "Frequency"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2881
+#: ../src/backend/filters/clusterAnalysis.cpp:2895
 msgid "Composition"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:110
+#: ../src/backend/filters/voxelise.cpp:111
 msgid "None (Raw count)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:111
+#: ../src/backend/filters/voxelise.cpp:112
 msgid "Volume (Density)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:112
+#: ../src/backend/filters/voxelise.cpp:113
 msgid "All Ions (conc)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:113
+#: ../src/backend/filters/voxelise.cpp:114
 msgid "Ratio (Num/Denom)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:117
+#: ../src/backend/filters/voxelise.cpp:118
 msgid "Point Cloud"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:118
+#: ../src/backend/filters/voxelise.cpp:119
 msgid "Isosurface"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:119
+#: ../src/backend/filters/voxelise.cpp:120
 msgid "Axial slice"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:124
-msgid "Gaussian (2𝜎)"
-msgstr ""
-
-#: ../src/backend/filters/voxelise.cpp:128
-msgid "Zero"
+#: ../src/backend/filters/voxelise.cpp:125
+msgid "Gaussian (blur)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:129
-msgid "Bounce"
+#: ../src/backend/filters/voxelise.cpp:126
+msgid "Lapl. of Gauss. (edges)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:134
+#: ../src/backend/filters/voxelise.cpp:131
 msgid "Linear"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:601
+#: ../src/backend/filters/voxelise.cpp:564
 msgid "Voxel Limits (min,max): ("
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:751
+#: ../src/backend/filters/voxelise.cpp:709
 msgid "Fixed width"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:755
+#: ../src/backend/filters/voxelise.cpp:713
 msgid "If true, use fixed size voxels, otherwise use fixed count"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:761
+#: ../src/backend/filters/voxelise.cpp:719
 msgid "Bin width x"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:765
+#: ../src/backend/filters/voxelise.cpp:723
 msgid "Voxel size in X direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:769
+#: ../src/backend/filters/voxelise.cpp:727
 msgid "Bin width y"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:772
+#: ../src/backend/filters/voxelise.cpp:730
 msgid "Voxel size in Y direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:778
+#: ../src/backend/filters/voxelise.cpp:736
 msgid "Bin width z"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:781
+#: ../src/backend/filters/voxelise.cpp:739
 msgid "Voxel size in Z direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:788
+#: ../src/backend/filters/voxelise.cpp:746
 msgid "Num bins x"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:792
+#: ../src/backend/filters/voxelise.cpp:750
 msgid "Number of voxels to use in X direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:797
+#: ../src/backend/filters/voxelise.cpp:755
 msgid "Num bins y"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:800
+#: ../src/backend/filters/voxelise.cpp:758
 msgid "Number of voxels to use in Y direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:806
+#: ../src/backend/filters/voxelise.cpp:764
 msgid "Num bins z"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:808
+#: ../src/backend/filters/voxelise.cpp:766
 msgid "Number of voxels to use in Z direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:838
+#: ../src/backend/filters/voxelise.cpp:796
 msgid "Normalise by"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:841
+#: ../src/backend/filters/voxelise.cpp:799
 msgid "Method to use to normalise scalar value in each voxel"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:844
+#: ../src/backend/filters/voxelise.cpp:802
 msgid "Computation"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:851
+#: ../src/backend/filters/voxelise.cpp:809
 msgid "Numerator"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:854
+#: ../src/backend/filters/voxelise.cpp:812
 msgid "Parmeter \"a\" used in fraction (a/b) to get voxel value"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:871
+#: ../src/backend/filters/voxelise.cpp:829
 msgid "Enable this ion for numerator"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:883
+#: ../src/backend/filters/voxelise.cpp:841
 msgid "Denominator"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:886
+#: ../src/backend/filters/voxelise.cpp:844
 msgid "Parameter \"b\" used in fraction (a/b) to get voxel value"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:899
+#: ../src/backend/filters/voxelise.cpp:857
 msgid "Enable this ion for denominator contribution"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:920
-#: ../src/backend/filters/voxelise.cpp:955
+#: ../src/backend/filters/voxelise.cpp:879
+#: ../src/backend/filters/voxelise.cpp:910
 msgid "Filtering"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:924
+#: ../src/backend/filters/voxelise.cpp:883
 msgid "Smoothing method to use on voxels"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:927
+#: ../src/backend/filters/voxelise.cpp:886
 msgid "Processing"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:933
-msgid "Kernel Bins"
+#: ../src/backend/filters/voxelise.cpp:892
+msgid "Standard Dev"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:937
-msgid "Number of bins in convolution kernel"
+#: ../src/backend/filters/voxelise.cpp:896
+msgid "Filtering Scale"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:948
-msgid "Exterior values"
+#: ../src/backend/filters/voxelise.cpp:902
+msgid "Kernel Size"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:951
-msgid "Method to use to treat boundaries of voxel data for convolution"
+#: ../src/backend/filters/voxelise.cpp:906
+msgid ""
+"Filter radius, in multiples of std. dev. Larger -> slower, more accurate"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:971
+#: ../src/backend/filters/voxelise.cpp:926
 msgid "Representation"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:974
+#: ../src/backend/filters/voxelise.cpp:929
 msgid "3D display method"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:985
+#: ../src/backend/filters/voxelise.cpp:940
 msgid "Spot size"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:988
+#: ../src/backend/filters/voxelise.cpp:943
 msgid "Size of the spots to use for display"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:993
-#: ../src/backend/filters/voxelise.cpp:1028
+#: ../src/backend/filters/voxelise.cpp:948
+#: ../src/backend/filters/voxelise.cpp:983
+#: ../src/backend/filters/voxelise.cpp:1055
 msgid "Transparency"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:996
+#: ../src/backend/filters/voxelise.cpp:951
 msgid "How \"see through\" each point is (0 - opaque, 1 - invisible)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1005
+#: ../src/backend/filters/voxelise.cpp:960
 msgid "Surf. param."
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1008
+#: ../src/backend/filters/voxelise.cpp:963
 msgid "Isovalue"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1011
+#: ../src/backend/filters/voxelise.cpp:966
 msgid "Scalar value to show as isosurface"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1016
-#: ../src/backend/filters/voxelise.cpp:1081
-#: ../src/backend/filters/spatialAnalysis.cpp:2019
-#: ../src/backend/filters/spatialAnalysis.cpp:2073
+#: ../src/backend/filters/voxelise.cpp:971
+#: ../src/backend/filters/voxelise.cpp:1036
+#: ../src/backend/filters/spatialAnalysis.cpp:2106
+#: ../src/backend/filters/spatialAnalysis.cpp:2159
 msgid "Surface"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1023
+#: ../src/backend/filters/voxelise.cpp:978
 msgid "Colour of isosurface"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1031
+#: ../src/backend/filters/voxelise.cpp:986
+#: ../src/backend/filters/voxelise.cpp:1058
 msgid "How \"see through\" each facet is (0 - opaque, 1 - invisible)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1042
+#: ../src/backend/filters/voxelise.cpp:997
 msgid "Slice param."
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1050
+#: ../src/backend/filters/voxelise.cpp:1005
 msgid "Slice Axis"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1053
+#: ../src/backend/filters/voxelise.cpp:1008
 msgid "Normal for the planar slice"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1060
+#: ../src/backend/filters/voxelise.cpp:1015
 msgid "Slice Coord"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1063
+#: ../src/backend/filters/voxelise.cpp:1018
 msgid "Fractional coordinate that slice plane passes through"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1068
+#: ../src/backend/filters/voxelise.cpp:1023
 msgid "Interp. Mode"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1076
+#: ../src/backend/filters/voxelise.cpp:1031
 msgid "Interpolation mode for direction normal to slice"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1092
+#: ../src/backend/filters/voxelise.cpp:1047
 msgid "Colour mode"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1095
-#: ../src/backend/filters/ionColour.cpp:265
+#: ../src/backend/filters/voxelise.cpp:1050
+#: ../src/backend/filters/ionColour.cpp:269
 msgid "Colour scheme used to assign points colours by value"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1100
-#: ../src/backend/filters/ionColour.cpp:277
+#: ../src/backend/filters/voxelise.cpp:1063
+#: ../src/backend/filters/ionColour.cpp:281
 msgid "Show Bar"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1107
+#: ../src/backend/filters/voxelise.cpp:1070
 msgid "Auto Bounds"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1108
+#: ../src/backend/filters/voxelise.cpp:1071
 msgid "Auto-compute min/max values in map"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1118
-#: ../src/backend/filters/ionColour.cpp:298
+#: ../src/backend/filters/voxelise.cpp:1081
+#: ../src/backend/filters/ionColour.cpp:302
 msgid "Map start"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1119
-#: ../src/backend/filters/ionColour.cpp:299
+#: ../src/backend/filters/voxelise.cpp:1082
+#: ../src/backend/filters/ionColour.cpp:303
 msgid "Assign points with this value to the first colour in map"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1126
-#: ../src/backend/filters/ionColour.cpp:306
+#: ../src/backend/filters/voxelise.cpp:1089
+#: ../src/backend/filters/ionColour.cpp:310
 msgid "Map end"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1127
-#: ../src/backend/filters/ionColour.cpp:307
+#: ../src/backend/filters/voxelise.cpp:1090
+#: ../src/backend/filters/ionColour.cpp:311
 msgid "Assign points with this value to the last colour in map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:261
+#: ../src/backend/filters/ionColour.cpp:265
 msgid "Colour Map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:269
+#: ../src/backend/filters/ionColour.cpp:273
 msgid "Reverse map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:270
+#: ../src/backend/filters/ionColour.cpp:274
 msgid "Reverse the colour scale"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:283
+#: ../src/backend/filters/ionColour.cpp:287
 msgid "Opacity"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:290
+#: ../src/backend/filters/ionColour.cpp:294
 msgid "Num Colours"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:292
+#: ../src/backend/filters/ionColour.cpp:296
 msgid "Number of unique colours to use in colour map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:413
+#: ../src/backend/filters/ionColour.cpp:417 ../src/backend/filter.cpp:172
 msgid "Aborted"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:568
+#: ../src/backend/filters/compositionProfile.cpp:51
+msgid "Cylinder (axial)"
+msgstr ""
+
+#: ../src/backend/filters/compositionProfile.cpp:52
+msgid "Cylinder (radial)"
+msgstr ""
+
+#: ../src/backend/filters/compositionProfile.cpp:591
 msgid "Distance"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:576
+#: ../src/backend/filters/compositionProfile.cpp:599
 msgid "Fraction"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:578
+#: ../src/backend/filters/compositionProfile.cpp:601
 msgid "Density (\\frac{\\#}{len^3})"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:605
+#: ../src/backend/filters/compositionProfile.cpp:628
 msgid "Freq. Profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:657
+#: ../src/backend/filters/compositionProfile.cpp:680
 msgid "No data remained in profile - cannot display result"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:937
+#: ../src/backend/filters/compositionProfile.cpp:965
 msgid "Primitive type"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:941
+#: ../src/backend/filters/compositionProfile.cpp:969
 msgid "Basic shape to use for profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:953
+#: ../src/backend/filters/compositionProfile.cpp:981
 msgid "Display the 3D composition profile interaction object"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:967
-#: ../src/backend/filters/spatialAnalysis.cpp:777
+#: ../src/backend/filters/compositionProfile.cpp:996
+#: ../src/backend/filters/spatialAnalysis.cpp:849
 msgid "Position for centre of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:975
+#: ../src/backend/filters/compositionProfile.cpp:1004
 msgid "Vector between ends of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:983
+#: ../src/backend/filters/compositionProfile.cpp:1012
 msgid "Prevent length of cylinder changing during interaction"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1023
+#: ../src/backend/filters/compositionProfile.cpp:1055
 msgid "Fixed Bin Num"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1026
+#: ../src/backend/filters/compositionProfile.cpp:1058
 msgid ""
 "If true, use a fixed number of bins for profile, otherwise use fixed step "
 "size"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1032
-#: ../src/backend/filters/spatialAnalysis.cpp:612
-#: ../src/backend/filters/spatialAnalysis.cpp:754
+#: ../src/backend/filters/compositionProfile.cpp:1065
+#: ../src/backend/filters/spatialAnalysis.cpp:690
+#: ../src/backend/filters/spatialAnalysis.cpp:826
 msgid "Num Bins"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1037
+#: ../src/backend/filters/compositionProfile.cpp:1070
 msgid "Number of bins to use for profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1042
-#: ../src/backend/filters/spectrumPlot.cpp:386
+#: ../src/backend/filters/compositionProfile.cpp:1076
+#: ../src/backend/filters/spectrumPlot.cpp:572
 msgid "Bin width"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1048
+#: ../src/backend/filters/compositionProfile.cpp:1082
 msgid "Size of each bin in profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1057
+#: ../src/backend/filters/compositionProfile.cpp:1091
 msgid "Convert bin counts into relative frequencies in each bin"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1061
+#: ../src/backend/filters/compositionProfile.cpp:1095
 msgid "Min. events"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1065
+#: ../src/backend/filters/compositionProfile.cpp:1099
 msgid "Drop data that does not have this many events"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1068
+#: ../src/backend/filters/compositionProfile.cpp:1102
 msgid "Settings"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1090
-#: ../src/backend/filters/spectrumPlot.cpp:445
+#: ../src/backend/filters/compositionProfile.cpp:1124
+#: ../src/backend/filters/spectrumPlot.cpp:661
 msgid "Plot Type"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1093
+#: ../src/backend/filters/compositionProfile.cpp:1127
 msgid "Visual style for plot"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1103
+#: ../src/backend/filters/compositionProfile.cpp:1137
 msgid "Colour of plot"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1119
+#: ../src/backend/filters/compositionProfile.cpp:1153
 msgid "Err. Estimator"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1122
+#: ../src/backend/filters/compositionProfile.cpp:1156
 msgid "Method of estimating error associated with each bin"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1129
+#: ../src/backend/filters/compositionProfile.cpp:1163
 msgid "Avg. Window"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1132
+#: ../src/backend/filters/compositionProfile.cpp:1166
 msgid "Number of bins to include in moving average filter"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1136
+#: ../src/backend/filters/compositionProfile.cpp:1170
 msgid "Error analysis"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:101
+#: ../src/backend/filters/spatialAnalysis.cpp:111
 msgid "Local Density"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:102
+#: ../src/backend/filters/spatialAnalysis.cpp:112
 msgid "Density Filtering"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:103
+#: ../src/backend/filters/spatialAnalysis.cpp:113
 msgid "Radial Distribution"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:104
+#: ../src/backend/filters/spatialAnalysis.cpp:114
 msgid "Axial Distribution"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:105
+#: ../src/backend/filters/spatialAnalysis.cpp:115
 msgid "Binomial Distribution"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:106
+#: ../src/backend/filters/spatialAnalysis.cpp:116
 msgid "Point Em/Replacement"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:110
+#: ../src/backend/filters/spatialAnalysis.cpp:120
 msgid "Neighbour Count"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:544
+#: ../src/backend/filters/spatialAnalysis.cpp:608
 msgid "Spatial analysis algorithm to use"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:567
+#: ../src/backend/filters/spatialAnalysis.cpp:631
 msgid "Stop Mode"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:570
+#: ../src/backend/filters/spatialAnalysis.cpp:634
 msgid "Method to use to terminate algorithm when examining each point"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:577
+#: ../src/backend/filters/spatialAnalysis.cpp:641
 msgid "NN Max"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:580
+#: ../src/backend/filters/spatialAnalysis.cpp:644
 msgid "Maximum number of neighbours to examine"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:586
+#: ../src/backend/filters/spatialAnalysis.cpp:651
 msgid "Normalise bins"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:589
+#: ../src/backend/filters/spatialAnalysis.cpp:654
 msgid ""
 "Normalise counts by binwidth. Needed when comparing NN histograms against "
 "one another"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:596
+#: ../src/backend/filters/spatialAnalysis.cpp:660
+msgid "Show Random"
+msgstr ""
+
+#: ../src/backend/filters/spatialAnalysis.cpp:663
+msgid "Show a fitted (density matched) theoretical distribution"
+msgstr ""
+
+#: ../src/backend/filters/spatialAnalysis.cpp:674
 msgid "Dist Max"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:599
+#: ../src/backend/filters/spatialAnalysis.cpp:677
 msgid "Maximum distance from each point for search"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:615
-#: ../src/backend/filters/spatialAnalysis.cpp:757
+#: ../src/backend/filters/spatialAnalysis.cpp:693
+#: ../src/backend/filters/spatialAnalysis.cpp:829
 msgid "Number of bins for output 1D RDF plot"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:621
+#: ../src/backend/filters/spatialAnalysis.cpp:699
 msgid "Surface Remove"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:624
+#: ../src/backend/filters/spatialAnalysis.cpp:702
 msgid ""
 "Exclude surface as part of source to minimise bias in RDF (at cost of "
 "increased noise)"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:631
+#: ../src/backend/filters/spatialAnalysis.cpp:709
 msgid "Remove Dist"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:634
+#: ../src/backend/filters/spatialAnalysis.cpp:712
 msgid "Minimum distance to remove from surface"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:642
-#: ../src/backend/filters/spatialAnalysis.cpp:762
+#: ../src/backend/filters/spatialAnalysis.cpp:720
+#: ../src/backend/filters/spatialAnalysis.cpp:834
 msgid "Plot colour "
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:645
-#: ../src/backend/filters/spatialAnalysis.cpp:765
+#: ../src/backend/filters/spatialAnalysis.cpp:723
+#: ../src/backend/filters/spatialAnalysis.cpp:837
 msgid "Colour of output plot"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:649
-#: ../src/backend/filters/spatialAnalysis.cpp:743
-#: ../src/backend/filters/spatialAnalysis.cpp:748
-#: ../src/backend/filters/spatialAnalysis.cpp:797
-#: ../src/backend/filters/spatialAnalysis.cpp:836
+#: ../src/backend/filters/spatialAnalysis.cpp:727
+#: ../src/backend/filters/spatialAnalysis.cpp:815
+#: ../src/backend/filters/spatialAnalysis.cpp:820
+#: ../src/backend/filters/spatialAnalysis.cpp:869
+#: ../src/backend/filters/spatialAnalysis.cpp:908
 msgid "Alg. Params."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:665
+#: ../src/backend/filters/spatialAnalysis.cpp:740
 msgid "Source"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:668
+#: ../src/backend/filters/spatialAnalysis.cpp:743
 msgid "Ions to use for initiating RDF search"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:681
+#: ../src/backend/filters/spatialAnalysis.cpp:756
 msgid "Enable/disable ion as source"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:687
+#: ../src/backend/filters/spatialAnalysis.cpp:762
 msgid "Source Ion"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:700
+#: ../src/backend/filters/spatialAnalysis.cpp:772
 msgid "Enable/disable all ions as target"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:712
+#: ../src/backend/filters/spatialAnalysis.cpp:784
 msgid "Enable/disable this ion as target"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:717
+#: ../src/backend/filters/spatialAnalysis.cpp:789
 msgid "Target Ion"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:727
+#: ../src/backend/filters/spatialAnalysis.cpp:799
 msgid "Cutoff"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:730
+#: ../src/backend/filters/spatialAnalysis.cpp:802
 msgid "Remove points with local density above/below this value"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:736
+#: ../src/backend/filters/spatialAnalysis.cpp:808
 msgid "Retain Upper"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:739
+#: ../src/backend/filters/spatialAnalysis.cpp:811
 msgid "Retain either points with density above (enabled) or below cutoff"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:785
+#: ../src/backend/filters/spatialAnalysis.cpp:857
 msgid "Vector between centre and end of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:804
-#: ../src/backend/filters/spatialAnalysis.cpp:3432
-#: ../src/backend/filters/spatialAnalysis.cpp:3491
+#: ../src/backend/filters/spatialAnalysis.cpp:876
+#: ../src/backend/filters/spatialAnalysis.cpp:3587
+#: ../src/backend/filters/spatialAnalysis.cpp:3646
 msgid "Block size"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:807
+#: ../src/backend/filters/spatialAnalysis.cpp:879
 msgid "Number of ions to use per block"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:814
+#: ../src/backend/filters/spatialAnalysis.cpp:886
 msgid "Max Block Aspect"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:817
+#: ../src/backend/filters/spatialAnalysis.cpp:889
 msgid ""
 "Maximum allowable block aspect ratio. Blocks above this aspect are "
 "discarded. Setting too high decreases correlation strength. Too low causes "
 "loss of statistical power."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:828
+#: ../src/backend/filters/spatialAnalysis.cpp:900
 msgid "Extrusion Direction"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:831
+#: ../src/backend/filters/spatialAnalysis.cpp:903
 msgid "Direction in which blocks are extended during construction."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:840
+#: ../src/backend/filters/spatialAnalysis.cpp:912
 msgid "Plot Counts"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:843
+#: ../src/backend/filters/spatialAnalysis.cpp:915
 msgid "Show the counts in the binomial histogram"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:852
+#: ../src/backend/filters/spatialAnalysis.cpp:924
 msgid ""
 "Normalise the counts in the binomial histogram to a probability density "
 "function"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:866
+#: ../src/backend/filters/spatialAnalysis.cpp:938
 msgid "Display Grid"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:875
+#: ../src/backend/filters/spatialAnalysis.cpp:947
 msgid "View Options"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:881
+#: ../src/backend/filters/spatialAnalysis.cpp:953
 msgid "Data File"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:885
+#: ../src/backend/filters/spatialAnalysis.cpp:957
 msgid "Pos file of points to subtract/replace/etc"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:890
+#: ../src/backend/filters/spatialAnalysis.cpp:962
 msgid "Match Tol."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:893
+#: ../src/backend/filters/spatialAnalysis.cpp:965
 msgid "Tolerance to allow for matching"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:909
+#: ../src/backend/filters/spatialAnalysis.cpp:981
 msgid "Replacment condition"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:915
+#: ../src/backend/filters/spatialAnalysis.cpp:987
 msgid "Replace value"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:918
+#: ../src/backend/filters/spatialAnalysis.cpp:990
 msgid "Use value data from file when replacing ions"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:923
+#: ../src/backend/filters/spatialAnalysis.cpp:995
 msgid "Replacement"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2005
-#: ../src/backend/filters/spatialAnalysis.cpp:2059
-#: ../src/backend/filters/spatialAnalysis.cpp:2320
-#: ../src/backend/filters/spatialAnalysis.cpp:2611
-#: ../src/backend/filters/spatialAnalysis.cpp:3136
+#: ../src/backend/filters/spatialAnalysis.cpp:2092
+#: ../src/backend/filters/spatialAnalysis.cpp:2145
+#: ../src/backend/filters/spatialAnalysis.cpp:2495
+#: ../src/backend/filters/spatialAnalysis.cpp:2781
+#: ../src/backend/filters/spatialAnalysis.cpp:3300
 msgid "Build"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2113
-#: ../src/backend/filters/spatialAnalysis.cpp:2347
-#: ../src/backend/filters/spatialAnalysis.cpp:2638
+#: ../src/backend/filters/spatialAnalysis.cpp:2199
+#: ../src/backend/filters/spatialAnalysis.cpp:2518
+#: ../src/backend/filters/spatialAnalysis.cpp:2805
 msgid "Analyse"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2195
-#: ../src/backend/filters/spatialAnalysis.cpp:2258
+#: ../src/backend/filters/spatialAnalysis.cpp:2278
+#: ../src/backend/filters/spatialAnalysis.cpp:2368
+#: ../src/backend/filters/spatialAnalysis.cpp:2434
 msgid "Radial Distance"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2197
+#: ../src/backend/filters/spatialAnalysis.cpp:2280
+#: ../src/backend/filters/spatialAnalysis.cpp:2373
 msgid "Count/Distance"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2202
+#: ../src/backend/filters/spatialAnalysis.cpp:2285
+#: ../src/backend/filters/spatialAnalysis.cpp:2378
 msgid "NN Freq."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2249
+#: ../src/backend/filters/spatialAnalysis.cpp:2425
 msgid "Warning, "
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2250
+#: ../src/backend/filters/spatialAnalysis.cpp:2426
 msgid ""
 " points were unable to find neighbour points that exceeded the search "
 "radius, and thus terminated prematurely"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2260
+#: ../src/backend/filters/spatialAnalysis.cpp:2436
 msgid " RDF"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2543
-#: ../src/backend/filters/spatialAnalysis.cpp:2844
+#: ../src/backend/filters/spatialAnalysis.cpp:2714
+#: ../src/backend/filters/spatialAnalysis.cpp:3011
 msgid "Number Density (\\#/Vol^3)"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2565
-#: ../src/backend/filters/spatialAnalysis.cpp:2864
+#: ../src/backend/filters/spatialAnalysis.cpp:2736
+#: ../src/backend/filters/spatialAnalysis.cpp:3031
 msgid "Warning,"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2566
-#: ../src/backend/filters/spatialAnalysis.cpp:2865
+#: ../src/backend/filters/spatialAnalysis.cpp:2737
+#: ../src/backend/filters/spatialAnalysis.cpp:3032
 msgid " points were un-analysable. These have been dropped"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2588
-#: ../src/backend/filters/spatialAnalysis.cpp:2887
+#: ../src/backend/filters/spatialAnalysis.cpp:2759
+#: ../src/backend/filters/spatialAnalysis.cpp:3054
 msgid "And so on..."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2970
+#: ../src/backend/filters/spatialAnalysis.cpp:3136
 msgid "Extract"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3045
+#: ../src/backend/filters/spatialAnalysis.cpp:3209
 msgid "Reduce"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3155
+#: ../src/backend/filters/spatialAnalysis.cpp:3318
 msgid "Compute"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3200
+#: ../src/backend/filters/spatialAnalysis.cpp:3364
 msgid "Insufficient points to complete analysis"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3223
+#: ../src/backend/filters/spatialAnalysis.cpp:3387
 msgid "Axial Distance"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3225
+#: ../src/backend/filters/spatialAnalysis.cpp:3389
 msgid " 1D Dist. Func."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3302
+#: ../src/backend/filters/spatialAnalysis.cpp:3458
 msgid "Binomial"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3434
-#: ../src/backend/filters/spatialAnalysis.cpp:3493
+#: ../src/backend/filters/spatialAnalysis.cpp:3589
+#: ../src/backend/filters/spatialAnalysis.cpp:3648
 msgid "Rel. Frequency"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:78
+#: ../src/backend/filters/transform.cpp:81
 msgid "Translate"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:79
+#: ../src/backend/filters/transform.cpp:82
 msgid "Scale (isotropic)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:80
+#: ../src/backend/filters/transform.cpp:83
 msgid "Scale (anisotropic)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:81
+#: ../src/backend/filters/transform.cpp:84
 msgid "Rotate"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:82
+#: ../src/backend/filters/transform.cpp:85
 msgid "Value Shuffle"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:83
+#: ../src/backend/filters/transform.cpp:86
 msgid "Spatial Noise"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:84
+#: ../src/backend/filters/transform.cpp:87
 msgid "Translate Value"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:88
+#: ../src/backend/filters/transform.cpp:91
 msgid "Specify"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:89
+#: ../src/backend/filters/transform.cpp:92
 msgid "Boundbox Centre"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:90
+#: ../src/backend/filters/transform.cpp:93
 msgid "Mass Centre"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1010
+#: ../src/backend/filters/transform.cpp:996
 msgid "Mass-to-Charge (amu/e)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1064
+#: ../src/backend/filters/transform.cpp:1050
 msgid "Shuffle"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1088
+#: ../src/backend/filters/transform.cpp:1074
 msgid "Splice"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1136
+#: ../src/backend/filters/transform.cpp:1122
 msgid "Algorithm to use to transform point data"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1153
+#: ../src/backend/filters/transform.cpp:1139
 msgid "Origin mode"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1156
+#: ../src/backend/filters/transform.cpp:1142
 msgid "Select how transform origin is computed"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1161
+#: ../src/backend/filters/transform.cpp:1147
 msgid "Show marker"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1165
+#: ../src/backend/filters/transform.cpp:1151
 msgid "Display an interactive object to set transform origin"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1167
+#: ../src/backend/filters/transform.cpp:1153
 msgid "Display a small marker to denote transform origin"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1183
+#: ../src/backend/filters/transform.cpp:1169
 msgid "Translation"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1186
+#: ../src/backend/filters/transform.cpp:1172
 msgid "Translation vector for transform"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1198
+#: ../src/backend/filters/transform.cpp:1184
 msgid "Offset"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1202
+#: ../src/backend/filters/transform.cpp:1188
 msgid "Scalar to use to offset each point's associated value"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1219
-#: ../src/backend/filters/transform.cpp:1246
+#: ../src/backend/filters/transform.cpp:1205
+#: ../src/backend/filters/transform.cpp:1232
 msgid "Origin of scale trasnform"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1226
-#: ../src/backend/filters/transform.cpp:1253
+#: ../src/backend/filters/transform.cpp:1212
+#: ../src/backend/filters/transform.cpp:1239
 msgid "Scale Fact."
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1229
-#: ../src/backend/filters/transform.cpp:1256
+#: ../src/backend/filters/transform.cpp:1215
+#: ../src/backend/filters/transform.cpp:1242
 msgid "Enlargement factor for scaling around origin"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1272
+#: ../src/backend/filters/transform.cpp:1258
 msgid "Origin of rotation"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1280
+#: ../src/backend/filters/transform.cpp:1266
 msgid "Axis around which to revolve"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1285
+#: ../src/backend/filters/transform.cpp:1271
 msgid "Angle (deg)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1288
+#: ../src/backend/filters/transform.cpp:1274
 msgid "Angle to perform rotation (ACW, as viewed from axis towards origin)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1305
+#: ../src/backend/filters/transform.cpp:1291
 msgid "Noise Type"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1308
+#: ../src/backend/filters/transform.cpp:1294
 msgid "Method to use to degrade point data"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1315
+#: ../src/backend/filters/transform.cpp:1301
 msgid "Noise level"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1317
+#: ../src/backend/filters/transform.cpp:1303
 msgid "Standard dev."
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1325
+#: ../src/backend/filters/transform.cpp:1311
 msgid "Amplitude of noise"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1337
+#: ../src/backend/filters/transform.cpp:1323
 msgid "Transform Params"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1675
+#: ../src/backend/filters/transform.cpp:1660
 msgid "White"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1677
+#: ../src/backend/filters/transform.cpp:1662
 msgid "Gaussian"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:55
+#: ../src/backend/filters/boundingBox.cpp:60
 msgid "Box only"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:56
+#: ../src/backend/filters/boundingBox.cpp:61
 msgid "Tick"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:57
+#: ../src/backend/filters/boundingBox.cpp:62
 msgid "Dimension"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:525
+#: ../src/backend/filters/boundingBox.cpp:531
 msgid "If true, show box, otherwise hide box"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:538
+#: ../src/backend/filters/boundingBox.cpp:544
 msgid "Style"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:541
+#: ../src/backend/filters/boundingBox.cpp:547
 msgid "Box display mode"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:544
+#: ../src/backend/filters/boundingBox.cpp:550
 msgid "Display mode"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:553
+#: ../src/backend/filters/boundingBox.cpp:559
 msgid "Fixed Tick Num"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:557
+#: ../src/backend/filters/boundingBox.cpp:563
 msgid ""
 "If true, evenly use specified number of ticks. Otherwise, use distance to "
 "determine tick count"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:565
+#: ../src/backend/filters/boundingBox.cpp:571
 msgid "Num X"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:568
+#: ../src/backend/filters/boundingBox.cpp:574
 msgid "Tick count in X direction"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:573
+#: ../src/backend/filters/boundingBox.cpp:579
 msgid "Num Y"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:576
+#: ../src/backend/filters/boundingBox.cpp:582
 msgid "Tick count in Y direction"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:581
+#: ../src/backend/filters/boundingBox.cpp:587
 msgid "Num Z"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:584
+#: ../src/backend/filters/boundingBox.cpp:590
 msgid "Tick count in Z direction"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:590
+#: ../src/backend/filters/boundingBox.cpp:596
 msgid "Spacing X"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:594
+#: ../src/backend/filters/boundingBox.cpp:600
 msgid "Distance between ticks on X axis"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:598
+#: ../src/backend/filters/boundingBox.cpp:604
 msgid "Spacing Y"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:602
+#: ../src/backend/filters/boundingBox.cpp:608
 msgid "Distance between ticks on Y axis"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:606
+#: ../src/backend/filters/boundingBox.cpp:612
 msgid "Spacing Z"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:610
+#: ../src/backend/filters/boundingBox.cpp:616
 msgid "Distance between ticks on Z axis"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:613
+#: ../src/backend/filters/boundingBox.cpp:619
 msgid "Tick marks"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:620
+#: ../src/backend/filters/boundingBox.cpp:626
 msgid "Box Colour"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:624
+#: ../src/backend/filters/boundingBox.cpp:630
 msgid "Colour of the bounding box"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:629
+#: ../src/backend/filters/boundingBox.cpp:635
 msgid "Line thickness"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:633
+#: ../src/backend/filters/boundingBox.cpp:639
 msgid "Thickness of the lines used to draw the box"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:641
-#: ../src/backend/filters/annotation.cpp:843
+#: ../src/backend/filters/boundingBox.cpp:647
+#: ../src/backend/filters/annotation.cpp:848
 msgid "Font Size"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:644
+#: ../src/backend/filters/boundingBox.cpp:650
 msgid "Relative size for text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:68
+#: ../src/backend/filters/annotation.cpp:73
 msgid "Arrow"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:69
+#: ../src/backend/filters/annotation.cpp:74
 msgid "Text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:70
+#: ../src/backend/filters/annotation.cpp:75
 msgid "Arrow+Text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:71
+#: ../src/backend/filters/annotation.cpp:76
 msgid "Angle"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:72
+#: ../src/backend/filters/annotation.cpp:77
 msgid "Ruler"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:519
+#: ../src/backend/filters/annotation.cpp:524
 msgid "Enable"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:522
+#: ../src/backend/filters/annotation.cpp:527
 msgid "Enable/disable annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:543
+#: ../src/backend/filters/annotation.cpp:548
 msgid "Type or style of annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:559
-#: ../src/backend/filters/annotation.cpp:663
+#: ../src/backend/filters/annotation.cpp:564
+#: ../src/backend/filters/annotation.cpp:668
 msgid "Text of annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:567
+#: ../src/backend/filters/annotation.cpp:572
 msgid "Position of annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:571
-#: ../src/backend/filters/annotation.cpp:678
-#: ../src/backend/filters/annotation.cpp:737
-#: ../src/backend/filters/annotation.cpp:826
+#: ../src/backend/filters/annotation.cpp:576
+#: ../src/backend/filters/annotation.cpp:683
+#: ../src/backend/filters/annotation.cpp:742
+#: ../src/backend/filters/annotation.cpp:831
 msgid "Up dir"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:575
-#: ../src/backend/filters/annotation.cpp:830
+#: ../src/backend/filters/annotation.cpp:580
+#: ../src/backend/filters/annotation.cpp:835
 msgid "Vector for up direction of annotation text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:579
-#: ../src/backend/filters/annotation.cpp:685
-#: ../src/backend/filters/annotation.cpp:729
-#: ../src/backend/filters/annotation.cpp:834
+#: ../src/backend/filters/annotation.cpp:584
+#: ../src/backend/filters/annotation.cpp:690
+#: ../src/backend/filters/annotation.cpp:734
+#: ../src/backend/filters/annotation.cpp:839
 msgid "Across dir"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:583
-#: ../src/backend/filters/annotation.cpp:838
+#: ../src/backend/filters/annotation.cpp:588
+#: ../src/backend/filters/annotation.cpp:843
 msgid "Reading direction for annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:588
-#: ../src/backend/filters/annotation.cpp:670
-#: ../src/backend/filters/annotation.cpp:764
+#: ../src/backend/filters/annotation.cpp:593
+#: ../src/backend/filters/annotation.cpp:675
+#: ../src/backend/filters/annotation.cpp:769
 msgid "Text size"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:592
-#: ../src/backend/filters/annotation.cpp:674
-#: ../src/backend/filters/annotation.cpp:846
+#: ../src/backend/filters/annotation.cpp:597
+#: ../src/backend/filters/annotation.cpp:679
+#: ../src/backend/filters/annotation.cpp:851
 msgid "Relative size of annotation text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:604
-#: ../src/backend/filters/annotation.cpp:645
+#: ../src/backend/filters/annotation.cpp:609
+#: ../src/backend/filters/annotation.cpp:650
 msgid "3D position for tail of arrow"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:612
-#: ../src/backend/filters/annotation.cpp:654
+#: ../src/backend/filters/annotation.cpp:617
+#: ../src/backend/filters/annotation.cpp:659
 msgid "3D Position to which arrow points"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:615
-#: ../src/backend/filters/annotation.cpp:725
+#: ../src/backend/filters/annotation.cpp:620
+#: ../src/backend/filters/annotation.cpp:730
 msgid "Positioning"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:620
-#: ../src/backend/filters/annotation.cpp:692
+#: ../src/backend/filters/annotation.cpp:625
+#: ../src/backend/filters/annotation.cpp:697
 msgid "Tip radius"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:624
+#: ../src/backend/filters/annotation.cpp:629
 msgid "Size of the arrow head"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:628
+#: ../src/backend/filters/annotation.cpp:633
 msgid "Line size"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:632
+#: ../src/backend/filters/annotation.cpp:637
 msgid "Thickness of line used to draw arrow stem"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:666
+#: ../src/backend/filters/annotation.cpp:671
 msgid "Options"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:702
+#: ../src/backend/filters/annotation.cpp:707
 msgid "Position A"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:706
+#: ../src/backend/filters/annotation.cpp:711
 msgid "Location of first non-central vertex"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:710
+#: ../src/backend/filters/annotation.cpp:715
 msgid "Origin "
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:714
+#: ../src/backend/filters/annotation.cpp:719
 msgid "Location of central vertex"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:718
+#: ../src/backend/filters/annotation.cpp:723
 msgid "Position B"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:722
+#: ../src/backend/filters/annotation.cpp:727
 msgid "Location of second non-central vertex"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:733
+#: ../src/backend/filters/annotation.cpp:738
 msgid "Reading direction for angle text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:738
+#: ../src/backend/filters/annotation.cpp:743
 msgid "Vector for up direction of angle text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:746
+#: ../src/backend/filters/annotation.cpp:751
 msgid "Reflexive"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:749
+#: ../src/backend/filters/annotation.cpp:754
 msgid "Measure interor (enabled) or exterior angle (disabled)"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:754
+#: ../src/backend/filters/annotation.cpp:759
 msgid "Show Angle"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:758
+#: ../src/backend/filters/annotation.cpp:763
 msgid "Display angle text (when enabled)"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:768
+#: ../src/backend/filters/annotation.cpp:773
 msgid "Size of angle text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:786
+#: ../src/backend/filters/annotation.cpp:791
 msgid "Digit format"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:790
+#: ../src/backend/filters/annotation.cpp:795
 msgid ""
 "Format of angle text; # for numeral position, '.' for separator, eg ##.## "
 "gives 12.34"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:796
-#: ../src/backend/filters/annotation.cpp:881
+#: ../src/backend/filters/annotation.cpp:801
+#: ../src/backend/filters/annotation.cpp:886
 msgid "Sphere size"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:800
-#: ../src/backend/filters/annotation.cpp:885
+#: ../src/backend/filters/annotation.cpp:805
+#: ../src/backend/filters/annotation.cpp:890
 msgid "Marker sphere size for manipulating tool"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:814
+#: ../src/backend/filters/annotation.cpp:819
 msgid "Ruler beginning 3D location"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:822
+#: ../src/backend/filters/annotation.cpp:827
 msgid "Ruler finish 3D location"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:852
+#: ../src/backend/filters/annotation.cpp:857
 msgid "Fixed ticks"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:855
+#: ../src/backend/filters/annotation.cpp:860
 msgid ""
 "Use fixed (enabled) number of text markers, or one every fixed distance "
 "(disabled)"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:862
+#: ../src/backend/filters/annotation.cpp:867
 msgid "Num Ticks"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:865
+#: ../src/backend/filters/annotation.cpp:870
 msgid "Number of tick marks along ruler"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:872
+#: ../src/backend/filters/annotation.cpp:877
 msgid "Tick Spacing"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:875
+#: ../src/backend/filters/annotation.cpp:880
 msgid "Distance between tick marks along ruler"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:899
+#: ../src/backend/filters/annotation.cpp:904
 msgid "Colour for ruler and ticks"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:445
+#: ../src/backend/filters/ionDownsample.cpp:191
+msgid "Sampling"
+msgstr ""
+
+#: ../src/backend/filters/ionDownsample.cpp:447
 msgid "By Count"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:448
+#: ../src/backend/filters/ionDownsample.cpp:450
 msgid "Sample up to a fixed number of ions"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:454
+#: ../src/backend/filters/ionDownsample.cpp:456
 msgid "Per Species"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:458
+#: ../src/backend/filters/ionDownsample.cpp:460
 msgid "Use species specific (from ranging) sampling values"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:487
+#: ../src/backend/filters/ionDownsample.cpp:489
 msgid "Sampling value for species"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:495
-#: ../src/backend/filters/ionDownsample.cpp:519
+#: ../src/backend/filters/ionDownsample.cpp:497
+#: ../src/backend/filters/ionDownsample.cpp:521
 msgid "Sampling rates"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:503
+#: ../src/backend/filters/ionDownsample.cpp:505
 msgid "Output Count"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:506
+#: ../src/backend/filters/ionDownsample.cpp:508
 msgid "Sample up to this value of points"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:511
+#: ../src/backend/filters/ionDownsample.cpp:513
 msgid "Out Fraction"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:515
+#: ../src/backend/filters/ionDownsample.cpp:517
 msgid "Sample this fraction of points"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:30
+#: ../src/backend/filters/ionInfo.cpp:37
 msgid "Rectilinear"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:31
+#: ../src/backend/filters/ionInfo.cpp:38
 msgid "Convex hull"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:193
+#: ../src/backend/filters/ionInfo.cpp:200
 msgid "No ions"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:231
+#: ../src/backend/filters/ionInfo.cpp:226
+#: ../src/backend/filters/spectrumPlot.cpp:432
+msgid ""
+"Background fit failed - input data was considered ill formed (gauss-test)"
+msgstr ""
+
+#: ../src/backend/filters/ionInfo.cpp:227
+msgid "Following data has not been corrected"
+msgstr ""
+
+#: ../src/backend/filters/ionInfo.cpp:279
 msgid "--Counts--"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:241
+#: ../src/backend/filters/ionInfo.cpp:289
 msgid "Total Ranged\t"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:246
+#: ../src/backend/filters/ionInfo.cpp:294
 msgid "Total (incl. unranged)\t"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:259
+#: ../src/backend/filters/ionInfo.cpp:307
 msgid "n/a"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:269
+#: ../src/backend/filters/ionInfo.cpp:317
 msgid "Unranged"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:282
+#: ../src/backend/filters/ionInfo.cpp:330
 msgid "Number of points : "
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:311
+#: ../src/backend/filters/ionInfo.cpp:359
 msgid "Rectilinear Bounds : "
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:316
+#: ../src/backend/filters/ionInfo.cpp:364
 msgid "Volume (len^3): "
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:332
+#: ../src/backend/filters/ionInfo.cpp:381
 msgid "Convex Volume (len^3): "
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:334
+#: ../src/backend/filters/ionInfo.cpp:384
 msgid "Unable to compute volume"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:363
+#: ../src/backend/filters/ionInfo.cpp:413
 msgid "Ranged Density (pts/vol):"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:368
+#: ../src/backend/filters/ionInfo.cpp:418
 msgid "Total Density (pts/vol):"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:396
+#: ../src/backend/filters/ionInfo.cpp:445
 msgid "Compositions"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:397
+#: ../src/backend/filters/ionInfo.cpp:446
 msgid "Display compositional data for points in console"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:401
+#: ../src/backend/filters/ionInfo.cpp:450
 msgid "Counts"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:402
+#: ../src/backend/filters/ionInfo.cpp:451
 msgid "Display count data for points in console"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:416
-msgid "Normalise count data"
+#: ../src/backend/filters/ionInfo.cpp:458
+msgid "Ion data"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:421
-msgid "Ion data"
+#: ../src/backend/filters/ionInfo.cpp:466
+msgid "Normalise count data"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:426
+#: ../src/backend/filters/ionInfo.cpp:530
 msgid "Volume"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:429
+#: ../src/backend/filters/ionInfo.cpp:533
 msgid "Compute volume for point data"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:444
+#: ../src/backend/filters/ionInfo.cpp:549
 msgid "Select volume counting technique"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:457
+#: ../src/backend/filters/ionInfo.cpp:562
 msgid "Volume data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:56
+#: ../src/backend/filters/dataLoad.cpp:61
 msgid "Auto"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:57
+#: ../src/backend/filters/dataLoad.cpp:62
 msgid "Little"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:58
+#: ../src/backend/filters/dataLoad.cpp:63
 msgid "Big"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:61
+#: ../src/backend/filters/dataLoad.cpp:66
 msgid "POS Data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:62
+#: ../src/backend/filters/dataLoad.cpp:67
 msgid "Text Data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:63
+#: ../src/backend/filters/dataLoad.cpp:68
 msgid "ATO Data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:238
+#: ../src/backend/filters/dataLoad.cpp:248
 msgid " does not exist"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:276
-#: ../src/backend/filters/dataLoad.cpp:289
-#: ../src/backend/filters/dataLoad.cpp:332
-#: ../src/backend/filters/dataLoad.cpp:343
-#: ../src/backend/filters/dataLoad.cpp:404
+#: ../src/backend/filters/dataLoad.cpp:275
+msgid "Reading File"
+msgstr ""
+
+#: ../src/backend/filters/dataLoad.cpp:290
+#: ../src/backend/filters/dataLoad.cpp:303
+#: ../src/backend/filters/dataLoad.cpp:347
+#: ../src/backend/filters/dataLoad.cpp:358
+#: ../src/backend/filters/dataLoad.cpp:418
 msgid "Error loading file: "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:306
+#: ../src/backend/filters/dataLoad.cpp:320
 msgid "Sampling is active, loaded "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:307
+#: ../src/backend/filters/dataLoad.cpp:321
 msgid " available."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:315
+#: ../src/backend/filters/dataLoad.cpp:329
 msgid "Loaded entire dataset, "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:315
-#: ../src/backend/filters/dataLoad.cpp:414
+#: ../src/backend/filters/dataLoad.cpp:329
+#: ../src/backend/filters/dataLoad.cpp:428
 msgid " points."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:358
+#: ../src/backend/filters/dataLoad.cpp:373
 msgid ""
 "Data file contained incorrect number of columns -- should be 3 or 4, was "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:413
+#: ../src/backend/filters/dataLoad.cpp:427
 msgid "Loaded dataset, "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:445
+#: ../src/backend/filters/dataLoad.cpp:460
 msgid ""
 "Warning:One or more bounds of the loaded data approaches the limits of "
 "numerical stability for the internal data type(magnitude too large). "
 "Consider rescaling data before loading"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:469
-#: ../src/backend/filters/dataLoad.cpp:490
-#: ../src/backend/filters/rangeFile.cpp:569
-#: ../src/backend/filters/rangeFile.cpp:589
+#: ../src/backend/filters/dataLoad.cpp:484
+#: ../src/backend/filters/dataLoad.cpp:505
+#: ../src/backend/filters/rangeFile.cpp:558
+#: ../src/backend/filters/rangeFile.cpp:578
 msgid "File"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:470
+#: ../src/backend/filters/dataLoad.cpp:485
 msgid "File from which to load data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:473
+#: ../src/backend/filters/dataLoad.cpp:488
 msgid ""
 "Readable files (*.xml, *.pos, *.txt,*.csv, *.ato)|*.xml;*.pos;*.txt;*.csv;*."
 "ato|All Files|*"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:483
+#: ../src/backend/filters/dataLoad.cpp:498
 msgid "File type"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:485
+#: ../src/backend/filters/dataLoad.cpp:500
 msgid "Type of file to be loaded"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:500
+#: ../src/backend/filters/dataLoad.cpp:515
 msgid "Entries per point"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:501
+#: ../src/backend/filters/dataLoad.cpp:516
 msgid "Number of decimal values in file per 3D point (normally 4)"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:520
+#: ../src/backend/filters/dataLoad.cpp:535
 msgid "File \"Endianness\""
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:521
+#: ../src/backend/filters/dataLoad.cpp:536
 msgid "On-disk data storage format. If file won't load, just try each"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:546
+#: ../src/backend/filters/dataLoad.cpp:561
 msgid "Relative offset of each entry in file for point's X position"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:554
+#: ../src/backend/filters/dataLoad.cpp:569
 msgid "Relative offset of each entry in file for point's Y position"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:562
+#: ../src/backend/filters/dataLoad.cpp:577
 msgid "Relative offset of each entry in file for point's Z position"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:570
+#: ../src/backend/filters/dataLoad.cpp:585
 msgid ""
 "Relative offset of each entry in file to use for scalar value of 3D point"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:573
+#: ../src/backend/filters/dataLoad.cpp:588
 msgid "Value Label"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:577
+#: ../src/backend/filters/dataLoad.cpp:592
 msgid "Name for the scalar value associated with each point"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:580
+#: ../src/backend/filters/dataLoad.cpp:595
 msgid "Format params."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:586
+#: ../src/backend/filters/dataLoad.cpp:601
 msgid "Enabled"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:590
+#: ../src/backend/filters/dataLoad.cpp:605
 msgid "Load this file?"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:601
+#: ../src/backend/filters/dataLoad.cpp:616
 msgid "Sample data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:604
+#: ../src/backend/filters/dataLoad.cpp:619
 msgid ""
 "Perform random selection on file contents, instead of loading entire file"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:611
+#: ../src/backend/filters/dataLoad.cpp:626
 msgid "Load Limit (MB)"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:614
+#: ../src/backend/filters/dataLoad.cpp:629
 msgid "Limit for size of data to load"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:621
+#: ../src/backend/filters/dataLoad.cpp:636
 msgid "Monitor"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:625
+#: ../src/backend/filters/dataLoad.cpp:640
 msgid ""
 "Watch file timestamp to track changes to file contents from other programs"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:629
+#: ../src/backend/filters/dataLoad.cpp:644
 msgid "Load params."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:636
+#: ../src/backend/filters/dataLoad.cpp:651
 msgid "Default colour "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:639
+#: ../src/backend/filters/dataLoad.cpp:654
 msgid "Default colour for points, if not overridden by other filters"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:644
+#: ../src/backend/filters/dataLoad.cpp:659
 msgid "Draw Size"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:647
+#: ../src/backend/filters/dataLoad.cpp:662
 msgid "Default size for points, if not overridden by other filters"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:122
+#: ../src/backend/filters/spectrumPlot.cpp:77
+msgid "Maximum"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:78
+msgid "Max in limit"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:79
+msgid "Probability"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:170
 msgid "Extrema"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:171
+#: ../src/backend/filters/spectrumPlot.cpp:219
 msgid "count"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:256
+#: ../src/backend/filters/spectrumPlot.cpp:304
 msgid "Mixed data"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:390
+#: ../src/backend/filters/spectrumPlot.cpp:441
+msgid "Background:"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:496
+msgid "Relative "
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:499
+msgid "Probability Density"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:576
 msgid "Step size for spectrum"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:395
+#: ../src/backend/filters/spectrumPlot.cpp:581
 msgid "Auto Min/max"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:399
+#: ../src/backend/filters/spectrumPlot.cpp:585
 msgid "Automatically compute spectrum upper and lower bound"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:404
+#: ../src/backend/filters/spectrumPlot.cpp:590
 msgid "Min"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:407
+#: ../src/backend/filters/spectrumPlot.cpp:593
 msgid "Starting position for spectrum"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:412
+#: ../src/backend/filters/spectrumPlot.cpp:598
 msgid "Max"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:415
+#: ../src/backend/filters/spectrumPlot.cpp:601
 msgid "Ending position for spectrum"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:423
+#: ../src/backend/filters/spectrumPlot.cpp:609
 msgid "Logarithmic"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:426
+#: ../src/backend/filters/spectrumPlot.cpp:612
 msgid "Convert the plot to logarithmic mode"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:448
+#: ../src/backend/filters/spectrumPlot.cpp:624
+msgid "Normalisation"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:627
+msgid "Rescale the plot height, to make inter-spectrum comparisons easier"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:634
+msgid "Lower Bound"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:638
+msgid "Do not use data below this x-value for normalisation"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:642
+msgid "Upper Bound"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:646
+msgid "Do not use data above this x-value for normalisation"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:664
 msgid "Visual style of plot"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:455
+#: ../src/backend/filters/spectrumPlot.cpp:671
 msgid "Colour of plotted spectrum"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:151
+#: ../src/backend/filters/rangeFile.cpp:153
 msgid "Pre-Allocate"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:282 ../src/backend/filter.cpp:48
+#: ../src/backend/filters/rangeFile.cpp:288 ../src/backend/filter.cpp:54
 msgid "Range"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:572
+#: ../src/backend/filters/rangeFile.cpp:561
 msgid "File to use for range data"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:582
+#: ../src/backend/filters/rangeFile.cpp:571
 msgid "Drop unranged"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:584
+#: ../src/backend/filters/rangeFile.cpp:573
 msgid "Remove unranged points when generating output"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:594
+#: ../src/backend/filters/rangeFile.cpp:583
 msgid "Legend"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:596
+#: ../src/backend/filters/rangeFile.cpp:585
 msgid "Display colour legend for enabled ions"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:600
+#: ../src/backend/filters/rangeFile.cpp:589
 msgid "View"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:616
+#: ../src/backend/filters/rangeFile.cpp:605
 msgid "All Ions"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:617
+#: ../src/backend/filters/rangeFile.cpp:606
 msgid "Enable/disable all ions at once"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:625
+#: ../src/backend/filters/rangeFile.cpp:614
 msgid "Species"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:632
+#: ../src/backend/filters/rangeFile.cpp:621
 msgid "IonID "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:633
+#: ../src/backend/filters/rangeFile.cpp:622
 msgid "Enable/disable specified ion"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:643
+#: ../src/backend/filters/rangeFile.cpp:632
 msgid "Active Ion "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:645
+#: ../src/backend/filters/rangeFile.cpp:634
 msgid "If true, ion is used in output"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:655
+#: ../src/backend/filters/rangeFile.cpp:644
 msgid "Colour "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:659
+#: ../src/backend/filters/rangeFile.cpp:648
 msgid "Colour used to represent ion"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:682
+#: ../src/backend/filters/rangeFile.cpp:671
 msgid "All Ranges"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:683
+#: ../src/backend/filters/rangeFile.cpp:672
 msgid "Enable/disable all ranges"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:698
+#: ../src/backend/filters/rangeFile.cpp:687
 msgid "Active Rng "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:701
+#: ../src/backend/filters/rangeFile.cpp:690
 msgid ""
 "Enable/disable specified range (ion must also be enabled to activiate range)"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:705
+#: ../src/backend/filters/rangeFile.cpp:694
 msgid "Ion "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:708
+#: ../src/backend/filters/rangeFile.cpp:697
 msgid "Name of ion associate to this range"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:717
+#: ../src/backend/filters/rangeFile.cpp:706
 msgid "Start rng "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:720
+#: ../src/backend/filters/rangeFile.cpp:709
 msgid "Start value for range"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:725
+#: ../src/backend/filters/rangeFile.cpp:714
 msgid "End rng "
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:728
+#: ../src/backend/filters/rangeFile.cpp:717
 msgid "Stopping value for range`"
 msgstr ""
 
-#: ../src/backend/state.cpp:137
+#: ../src/backend/state.cpp:151
 msgid ""
 "This file is a \"state\" file for the 3Depict program, and stores "
 "information about a particular analysis session. This file should be a valid "
 "\"XML\" file"
 msgstr ""
 
-#: ../src/backend/state.cpp:290
+#: ../src/backend/state.cpp:318
 msgid "Failed to allocate parser"
 msgstr ""
 
-#: ../src/backend/state.cpp:325
+#: ../src/backend/state.cpp:353
 msgid ""
 "Unable to retrieve root node in input state file... Is this really a non-"
 "empty XML file?"
 msgstr ""
 
-#: ../src/backend/state.cpp:332
+#: ../src/backend/state.cpp:360
 msgid "Base state node missing. Is this really a state XML file??"
 msgstr ""
 
-#: ../src/backend/state.cpp:361
+#: ../src/backend/state.cpp:389
 msgid "State was created by a newer version of this program.. "
 msgstr ""
 
-#: ../src/backend/state.cpp:362
+#: ../src/backend/state.cpp:390
 msgid "file reading will continue, but may fail."
 msgstr ""
 
-#: ../src/backend/state.cpp:367
+#: ../src/backend/state.cpp:395
 msgid ""
 "Warning, unparseable version number in state file. File reading will "
 "continue, but may fail"
 msgstr ""
 
-#: ../src/backend/state.cpp:374
+#: ../src/backend/state.cpp:402
 msgid "Unable to find the \"writer\" node"
 msgstr ""
 
-#: ../src/backend/state.cpp:384
+#: ../src/backend/state.cpp:412
 msgid "Unable to find the \"backcolour\" node."
 msgstr ""
 
-#: ../src/backend/state.cpp:391
+#: ../src/backend/state.cpp:419
 msgid "\"backcolour\" node missing \"r\" value."
 msgstr ""
 
-#: ../src/backend/state.cpp:396
+#: ../src/backend/state.cpp:424
 msgid "Unable to interpret \"backColour\" node's \"r\" value."
 msgstr ""
 
-#: ../src/backend/state.cpp:404
+#: ../src/backend/state.cpp:432
 msgid "\"backcolour\" node missing \"g\" value."
 msgstr ""
 
-#: ../src/backend/state.cpp:410
+#: ../src/backend/state.cpp:438
 msgid "Unable to interpret \"backColour\" node's \"g\" value."
 msgstr ""
 
-#: ../src/backend/state.cpp:418
+#: ../src/backend/state.cpp:446
 msgid "\"backcolour\" node missing \"b\" value."
 msgstr ""
 
-#: ../src/backend/state.cpp:424
+#: ../src/backend/state.cpp:452
 msgid "Unable to interpret \"backColour\" node's \"b\" value."
 msgstr ""
 
-#: ../src/backend/state.cpp:431
+#: ../src/backend/state.cpp:459
 msgid "\"backcolour\"s rgb values must be in range [0,1]"
 msgstr ""
 
-#: ../src/backend/state.cpp:459
+#: ../src/backend/state.cpp:487
 msgid "Unable to find or interpret \"showaxis\" node"
 msgstr ""
 
-#: ../src/backend/state.cpp:503
+#: ../src/backend/state.cpp:531
 msgid "Unable to locate \"filtertree\" node."
 msgstr ""
 
-#: ../src/backend/state.cpp:519
+#: ../src/backend/state.cpp:547
 msgid "Cameras section missing \"active\" node."
 msgstr ""
 
-#: ../src/backend/state.cpp:527
+#: ../src/backend/state.cpp:555
 msgid "Unable to find property \"value\"  for \"cameras->active\" node."
 msgstr ""
 
-#: ../src/backend/state.cpp:533
+#: ../src/backend/state.cpp:561
 msgid "Unable to interpret property \"value\"  for \"cameras->active\" node."
 msgstr ""
 
-#: ../src/backend/state.cpp:552
+#: ../src/backend/state.cpp:580
 msgid "Failed to interpret camera state for camera : "
 msgstr ""
 
-#: ../src/backend/state.cpp:560
+#: ../src/backend/state.cpp:588
 msgid "Unable to interpret the camera type for camera : "
 msgstr ""
 
-#: ../src/backend/state.cpp:596
+#: ../src/backend/state.cpp:632
 msgid "Unable to locate stash name for stash "
 msgstr ""
 
-#: ../src/backend/state.cpp:603
+#: ../src/backend/state.cpp:639
 msgid "Empty stash name for stash "
 msgstr ""
 
-#: ../src/backend/state.cpp:612
+#: ../src/backend/state.cpp:648
 msgid "No filter tree for stash:"
 msgstr ""
 
-#: ../src/backend/state.cpp:618
+#: ../src/backend/state.cpp:654
 msgid "For stash "
 msgstr ""
 
-#: ../src/backend/state.cpp:650
+#: ../src/backend/state.cpp:686
 msgid "Unrecognised effect :"
 msgstr ""
 
-#: ../src/backend/state.cpp:660
+#: ../src/backend/state.cpp:696
 msgid "Duplicate effect found"
 msgstr ""
 
-#: ../src/backend/state.cpp:660
+#: ../src/backend/state.cpp:696
 msgid " cannot use."
 msgstr ""
 
-#: ../src/backend/state.cpp:670
+#: ../src/backend/state.cpp:706
 msgid "Error reading effect : "
 msgstr ""
 
-#: ../src/backend/state.cpp:866
+#: ../src/backend/state.cpp:898
 msgid "-merge"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:199
+#: ../src/backend/filtertreeAnalyse.cpp:223
 msgid ""
 "Parent filter has no output, but filter requires input -- there is no point "
 "in placing a child filter here."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:200
+#: ../src/backend/filtertreeAnalyse.cpp:224
 msgid "Leaf-only filter with child"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:210
+#: ../src/backend/filtertreeAnalyse.cpp:234
 msgid ""
 "Parent filters' output will be blocked by child, without use. Parent results "
 "will be dropped."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:211
-#: ../src/backend/filtertreeAnalyse.cpp:225
+#: ../src/backend/filtertreeAnalyse.cpp:235
+#: ../src/backend/filtertreeAnalyse.cpp:249
 msgid "Bad parent->child pair"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:224
+#: ../src/backend/filtertreeAnalyse.cpp:248
 msgid ""
 "First filter does not output anything useable by child filter. Child filter "
 "not useful."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:304
+#: ../src/backend/filtertreeAnalyse.cpp:328
 msgid "Spatial results possibly altered"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:305
+#: ../src/backend/filtertreeAnalyse.cpp:329
 msgid ""
 "Filters and settings selected that could alter reported results that depend "
 "upon density. Check to see if spatial sampling may be happening in the "
 "filter tree - this warning is provisional only."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:373
+#: ../src/backend/filtertreeAnalyse.cpp:397
 msgid "Filter needs parent \""
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:374
+#: ../src/backend/filtertreeAnalyse.cpp:398
 msgid ""
 "\" but does not have one. Filter may not function correctly until this "
 "parent is given."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:375
+#: ../src/backend/filtertreeAnalyse.cpp:399
 msgid "Filter missing needed parent"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:555
+#: ../src/backend/filtertreeAnalyse.cpp:430
+msgid "Bad range filter settings"
+msgstr ""
+
+#: ../src/backend/filtertreeAnalyse.cpp:431
+msgid ""
+"Rangefile set to drop unranged data, however a child filter requires it."
+msgstr ""
+
+#: ../src/backend/filtertreeAnalyse.cpp:613
 msgid "Composition results possibly altered"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:556
+#: ../src/backend/filtertreeAnalyse.cpp:614
 msgid ""
 "Filters and settings selected that could bias reported composition. Check to "
 "see if species biasing may occcur in the filter tree - this warning is "
 "provisional only."
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:43 ../src/backend/APT/APTFileIO.cpp:78
-#: ../src/backend/APT/APTFileIO.cpp:102
+#: ../src/backend/APT/APTFileIO.cpp:44 ../src/backend/APT/APTFileIO.cpp:79
+#: ../src/backend/APT/APTFileIO.cpp:103
 msgid "Error opening file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:44
+#: ../src/backend/APT/APTFileIO.cpp:45
 msgid "Only found header, no data"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:45
+#: ../src/backend/APT/APTFileIO.cpp:46
 msgid "Unable to reopen file after first scan"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:46
+#: ../src/backend/APT/APTFileIO.cpp:47
 msgid "Error whilst reading file contents"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:47 ../src/backend/APT/APTFileIO.cpp:48
+#: ../src/backend/APT/APTFileIO.cpp:48 ../src/backend/APT/APTFileIO.cpp:49
 msgid "Unexpected file format"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:49
+#: ../src/backend/APT/APTFileIO.cpp:50
 msgid "Insufficient memory to continue"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:53
+#: ../src/backend/APT/APTFileIO.cpp:54
 msgid "Memory allocation failure on POS load"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:54
+#: ../src/backend/APT/APTFileIO.cpp:55
 msgid "Error opening pos file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:55
+#: ../src/backend/APT/APTFileIO.cpp:56
 msgid "Pos file empty"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:56
+#: ../src/backend/APT/APTFileIO.cpp:57
 msgid "Pos file size appears to have non-integer number of entries"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:57
+#: ../src/backend/APT/APTFileIO.cpp:58
 msgid "Error reading from pos file (after open)"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:58
+#: ../src/backend/APT/APTFileIO.cpp:59
 msgid "Error - Found NaN in pos file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:59
+#: ../src/backend/APT/APTFileIO.cpp:60
 msgid "Error - Found Inf in pos file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:60
+#: ../src/backend/APT/APTFileIO.cpp:61
 msgid "Pos load aborted by interrupt."
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:79
+#: ../src/backend/APT/APTFileIO.cpp:80
 msgid "No numerical data found"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:80
+#: ../src/backend/APT/APTFileIO.cpp:81
 msgid "Error re-opening file, after first scan"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:81
+#: ../src/backend/APT/APTFileIO.cpp:82
 msgid "Unable to read file contents after open"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:82
+#: ../src/backend/APT/APTFileIO.cpp:83
 msgid "Error interpreting field in file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:83
+#: ../src/backend/APT/APTFileIO.cpp:84
 msgid "Incorrect number of fields in file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:84 ../src/backend/APT/APTFileIO.cpp:106
+#: ../src/backend/APT/APTFileIO.cpp:85 ../src/backend/APT/APTFileIO.cpp:107
 msgid "Unable to allocate memory to store data"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:103
+#: ../src/backend/APT/APTFileIO.cpp:104
 msgid "File is empty"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:104
+#: ../src/backend/APT/APTFileIO.cpp:105
 msgid "Filesize does not match expected format"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:105
+#: ../src/backend/APT/APTFileIO.cpp:106
 msgid "File version number not <4, as expected"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:107
+#: ../src/backend/APT/APTFileIO.cpp:108
 msgid "Unable to detect endian-ness in file"
 msgstr ""
 
@@ -4910,7 +5041,7 @@ msgstr ""
 msgid "Range file is exceedingly large. Refusing to open"
 msgstr ""
 
-#: ../src/backend/APT/APTRanges.cpp:1403
+#: ../src/backend/APT/APTRanges.cpp:1404
 msgid ""
 "Range headings do not match order of the ions listed in the name "
 "specifications. The name specification ordering will be used when reading "
@@ -4919,15 +5050,15 @@ msgid ""
 "Check range-species associations actually match what you expect."
 msgstr ""
 
-#: ../src/backend/filter.cpp:46
+#: ../src/backend/filter.cpp:52
 msgid "2D Plot"
 msgstr ""
 
-#: ../src/backend/filter.cpp:47
+#: ../src/backend/filter.cpp:53
 msgid "Draw"
 msgstr ""
 
-#: ../src/backend/filter.cpp:49
+#: ../src/backend/filter.cpp:55
 msgid "Voxel"
 msgstr ""
 
@@ -4951,7 +5082,7 @@ msgstr ""
 msgid "Ion Sampler"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.h:91
+#: ../src/backend/filters/ionInfo.h:103
 msgid "Ion info"
 msgstr ""
 
@@ -4963,11 +5094,11 @@ msgstr ""
 msgid "Ext. Program"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.h:93
+#: ../src/backend/filters/rangeFile.h:96
 msgid "Ranging"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.h:117
+#: ../src/backend/filters/compositionProfile.h:120
 msgid "Comp. Prof."
 msgstr ""
 
diff --git a/translations/3Depict_de_DE.mo b/translations/3Depict_de_DE.mo
index 36291f1..c31b1ce 100644
Binary files a/translations/3Depict_de_DE.mo and b/translations/3Depict_de_DE.mo differ
diff --git a/translations/3Depict_de_DE.po b/translations/3Depict_de_DE.po
index 8cee9e4..ca22634 100644
--- a/translations/3Depict_de_DE.po
+++ b/translations/3Depict_de_DE.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 3Depict\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-08-23 15:32+0100\n"
+"POT-Creation-Date: 2015-04-26 23:23+0100\n"
 "PO-Revision-Date: 2012-07-09 08:21+0000\n"
 "Last-Translator: epix1234 <erich_s at gmx.de>\n"
 "Language-Team: German (Germany) (http://www.transifex.com/projects/p/3depict/"
@@ -25,21 +25,21 @@ msgstr ""
 msgid "Lock"
 msgstr "Sperren"
 
-#: ../src/gl/cameras.cpp:603 ../src/backend/filters/ionClip.cpp:519
-#: ../src/backend/filters/ionClip.cpp:541
-#: ../src/backend/filters/ionClip.cpp:563
-#: ../src/backend/filters/ionClip.cpp:600
-#: ../src/backend/filters/compositionProfile.cpp:964
-#: ../src/backend/filters/compositionProfile.cpp:1002
-#: ../src/backend/filters/spatialAnalysis.cpp:774
-#: ../src/backend/filters/transform.cpp:1216
-#: ../src/backend/filters/transform.cpp:1243
-#: ../src/backend/filters/transform.cpp:1269
-#: ../src/backend/filters/annotation.cpp:563
+#: ../src/gl/cameras.cpp:603 ../src/backend/filters/ionClip.cpp:528
+#: ../src/backend/filters/ionClip.cpp:550
+#: ../src/backend/filters/ionClip.cpp:572
+#: ../src/backend/filters/ionClip.cpp:609
+#: ../src/backend/filters/compositionProfile.cpp:993
+#: ../src/backend/filters/compositionProfile.cpp:1031
+#: ../src/backend/filters/spatialAnalysis.cpp:846
+#: ../src/backend/filters/transform.cpp:1202
+#: ../src/backend/filters/transform.cpp:1229
+#: ../src/backend/filters/transform.cpp:1255
+#: ../src/backend/filters/annotation.cpp:568
 msgid "Origin"
 msgstr "Ursprung"
 
-#: ../src/gl/cameras.cpp:611 ../src/backend/filters/spatialAnalysis.cpp:697
+#: ../src/gl/cameras.cpp:611 ../src/backend/filters/spatialAnalysis.cpp:769
 msgid "Target"
 msgstr "Ziel"
 
@@ -52,7 +52,7 @@ msgid "Perspective"
 msgstr "Perspektivisch"
 
 #: ../src/gl/cameras.cpp:627 ../src/gl/cameras.cpp:730
-#: ../src/gui/mainFrame.cpp:5134
+#: ../src/gui/mainFrame.cpp:5150
 msgid "Orthogonal"
 msgstr "Orthogonal"
 
@@ -68,25 +68,25 @@ msgstr "Bildausschnitt"
 msgid "View size"
 msgstr "Anzeigegröße"
 
-#: ../src/wx/wxcomponents.cpp:187
+#: ../src/wx/wxcomponents.cpp:191
 msgid "Save Data..."
 msgstr "Datei speichern..."
 
-#: ../src/wx/wxcomponents.cpp:188
+#: ../src/wx/wxcomponents.cpp:192
 msgid "Text File (*.txt)|*.txt|All Files (*)|*"
 msgstr "Text Datei (*.txt)|*.txt|Alle Dateien (*)|*"
 
-#: ../src/wx/wxcomponents.cpp:200
+#: ../src/wx/wxcomponents.cpp:204
 msgid "Error saving file. Check output dir is writable."
 msgstr ""
 "Fehler beim Schreiben der Datei. Stellen Sie sicher, dass das "
 "Zielverzeichnis nicht schreibgeschüzt ist."
 
-#: ../src/wx/wxcomponents.cpp:200 ../src/gui/dialogs/ExportRngDialog.cpp:170
-#: ../src/gui/mainFrame.cpp:1446 ../src/gui/mainFrame.cpp:1572
-#: ../src/gui/mainFrame.cpp:1621 ../src/gui/mainFrame.cpp:1697
-#: ../src/gui/mainFrame.cpp:2253 ../src/gui/mainFrame.cpp:2319
-#: ../src/gui/mainFrame.cpp:2411 ../src/gui/mainFrame.cpp:2526
+#: ../src/wx/wxcomponents.cpp:204 ../src/gui/dialogs/ExportRngDialog.cpp:170
+#: ../src/gui/mainFrame.cpp:1448 ../src/gui/mainFrame.cpp:1573
+#: ../src/gui/mainFrame.cpp:1622 ../src/gui/mainFrame.cpp:1698
+#: ../src/gui/mainFrame.cpp:2245 ../src/gui/mainFrame.cpp:2311
+#: ../src/gui/mainFrame.cpp:2404 ../src/gui/mainFrame.cpp:2517
 msgid "Save error"
 msgstr "Fehler speichern"
 
@@ -233,35 +233,35 @@ msgid ""
 "rrng)|*.rrng;*.RRNG|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/glPane.cpp:637
+#: ../src/gui/glPane.cpp:628
 msgid "Use shift/ctrl-space or double tap to alter reset axis"
 msgstr ""
 "Verwenden Sie Shift / ⌘-Leertaste oder doppeltippen, um Achsen "
 "zurückzusetzen oder zu verändern"
 
-#: ../src/gui/glPane.cpp:910
+#: ../src/gui/glPane.cpp:901
 msgid "Image progress"
 msgstr "Bild Fortschritt"
 
-#: ../src/gui/glPane.cpp:911
+#: ../src/gui/glPane.cpp:902
 msgid "Rendering tiles..."
 msgstr "Rendering tiles..."
 
-#: ../src/gui/glPane.cpp:1097
+#: ../src/gui/glPane.cpp:1091
 msgid "Animation progress"
 msgstr "Animation-Fortschritt"
 
-#: ../src/gui/glPane.cpp:1098
+#: ../src/gui/glPane.cpp:1092
 msgid "Rendering sequence..."
 msgstr "Renderreihenfolge..."
 
-#: ../src/gui/glPane.cpp:1136
+#: ../src/gui/glPane.cpp:1130
 msgid "Saving Image "
 msgstr "Speichere Bild "
 
-#: ../src/gui/glPane.cpp:1136 ../src/gui/mainFrame.cpp:4309
-#: ../src/gui/mainFrame.cpp:4313 ../src/gui/mainFrame.cpp:4326
-#: ../src/backend/filters/dataLoad.cpp:307
+#: ../src/gui/glPane.cpp:1130 ../src/gui/mainFrame.cpp:4329
+#: ../src/gui/mainFrame.cpp:4333 ../src/gui/mainFrame.cpp:4346
+#: ../src/backend/filters/dataLoad.cpp:321
 msgid " of "
 msgstr " von "
 
@@ -278,14 +278,14 @@ msgid "Source Filter"
 msgstr "Source Filter"
 
 #: ../src/gui/dialogs/ExportRngDialog.cpp:54
-#: ../src/backend/filters/rangeFile.cpp:665
+#: ../src/backend/filters/rangeFile.cpp:654
 msgid "Ions"
 msgstr "Ionen"
 
 #: ../src/gui/dialogs/ExportRngDialog.cpp:55
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1586
-#: ../src/backend/filters/voxelise.cpp:876
-#: ../src/backend/filters/rangeFile.cpp:732
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1587
+#: ../src/backend/filters/voxelise.cpp:834
+#: ../src/backend/filters/rangeFile.cpp:721
 msgid "Ranges"
 msgstr "Ranges"
 
@@ -296,8 +296,8 @@ msgstr "Param."
 #: ../src/gui/dialogs/ExportRngDialog.cpp:89
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:105
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:352
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1173
-#: ../src/backend/filters/dataLoad.cpp:566
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1177
+#: ../src/backend/filters/dataLoad.cpp:581
 msgid "Value"
 msgstr "Wert"
 
@@ -314,7 +314,7 @@ msgid "Num Ranges"
 msgstr "Num Ranges"
 
 #: ../src/gui/dialogs/ExportRngDialog.cpp:116
-#: ../src/gui/dialogs/rangeEditDialog.cpp:695 ../src/backend/filter.cpp:44
+#: ../src/gui/dialogs/rangeEditDialog.cpp:696 ../src/backend/filter.cpp:50
 msgid "Ion"
 msgstr "Ion"
 
@@ -326,7 +326,7 @@ msgstr "Range Anfang"
 msgid "Range end"
 msgstr "Range Ende"
 
-#: ../src/gui/dialogs/ExportRngDialog.cpp:151 ../src/gui/mainFrame.cpp:2355
+#: ../src/gui/dialogs/ExportRngDialog.cpp:151 ../src/gui/mainFrame.cpp:2348
 msgid "Save pos..."
 msgstr "pos speichern..."
 
@@ -334,10 +334,10 @@ msgstr "pos speichern..."
 msgid "ORNL format RNG (*.rng)|*.rng|All Files (*)|*"
 msgstr "ORNL Format RNG (*.rng)|*.rng|Alle Dateien (*)|*"
 
-#: ../src/gui/dialogs/ExportRngDialog.cpp:167 ../src/gui/mainFrame.cpp:1446
-#: ../src/gui/mainFrame.cpp:1622 ../src/gui/mainFrame.cpp:1697
-#: ../src/gui/mainFrame.cpp:2254 ../src/gui/mainFrame.cpp:2412
-#: ../src/gui/mainFrame.cpp:2527
+#: ../src/gui/dialogs/ExportRngDialog.cpp:167 ../src/gui/mainFrame.cpp:1448
+#: ../src/gui/mainFrame.cpp:1623 ../src/gui/mainFrame.cpp:1698
+#: ../src/gui/mainFrame.cpp:2246 ../src/gui/mainFrame.cpp:2405
+#: ../src/gui/mainFrame.cpp:2518
 msgid "Unable to save. Check output destination can be written to."
 msgstr ""
 "Speichern nicht möglich. Bitte überprüfen Sie ob der Ausgabepfad "
@@ -355,92 +355,92 @@ msgstr "Liste der Rangedateien im Filterbaum"
 msgid "Detailed view of selected range"
 msgstr "Detailierte Ansicht des ausgewählten Range"
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:218
+#: ../src/gui/dialogs/rangeEditDialog.cpp:221
 msgid "Show Overlays"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:243
+#: ../src/gui/dialogs/rangeEditDialog.cpp:246
 msgid "e.g. H2O"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:560
-#: ../src/gui/dialogs/rangeEditDialog.cpp:694 ../src/gui/mainFrame.cpp:5800
-#: ../src/backend/filter.cpp:45
+#: ../src/gui/dialogs/rangeEditDialog.cpp:561
+#: ../src/gui/dialogs/rangeEditDialog.cpp:695 ../src/gui/mainFrame.cpp:5801
+#: ../src/backend/filter.cpp:51
 msgid "Plot"
 msgstr "Plot"
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:561
+#: ../src/gui/dialogs/rangeEditDialog.cpp:562
 msgid "Short Name"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:562
+#: ../src/gui/dialogs/rangeEditDialog.cpp:563
 msgid "Long Name"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:563
-#: ../src/backend/filters/voxelise.cpp:1020
-#: ../src/backend/filters/compositionProfile.cpp:1100
-#: ../src/backend/filters/annotation.cpp:896
-#: ../src/backend/filters/spectrumPlot.cpp:452
+#: ../src/gui/dialogs/rangeEditDialog.cpp:564
+#: ../src/backend/filters/voxelise.cpp:975
+#: ../src/backend/filters/compositionProfile.cpp:1134
+#: ../src/backend/filters/annotation.cpp:901
+#: ../src/backend/filters/spectrumPlot.cpp:668
 msgid "Colour"
 msgstr "Farbe"
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:696
-#: ../src/backend/filters/annotation.cpp:600
-#: ../src/backend/filters/annotation.cpp:641
-#: ../src/backend/filters/annotation.cpp:810
+#: ../src/gui/dialogs/rangeEditDialog.cpp:697
+#: ../src/backend/filters/annotation.cpp:605
+#: ../src/backend/filters/annotation.cpp:646
+#: ../src/backend/filters/annotation.cpp:815
 msgid "Start"
 msgstr "Anfang"
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:697
-#: ../src/backend/filters/annotation.cpp:608
-#: ../src/backend/filters/annotation.cpp:650
-#: ../src/backend/filters/annotation.cpp:818
+#: ../src/gui/dialogs/rangeEditDialog.cpp:698
+#: ../src/backend/filters/annotation.cpp:613
+#: ../src/backend/filters/annotation.cpp:655
+#: ../src/backend/filters/annotation.cpp:823
 msgid "End"
 msgstr "Ende"
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1259
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1260
 msgid "Range or ion?"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1260
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1261
 msgid "Select type to add"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1541
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1542
 msgid "Range Editor"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1545
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1546
 msgid "Enable or disable all overlays"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1546
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1547
 msgid "Entered overlays, use delete to remove"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1547
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1548
 msgid "Available plots for ranging"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1548
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1549
 msgid "Enter species to display as overlay, e.g. SiO2"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1549
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1550
 msgid "Editable ranges"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1550
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1551
 msgid "Editable ions"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1585
-#: ../src/gui/dialogs/animateFilterDialog.cpp:173
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1586
+#: ../src/gui/dialogs/animateFilterDialog.cpp:177
 msgid "Plots"
 msgstr ""
 
-#: ../src/gui/dialogs/rangeEditDialog.cpp:1587
+#: ../src/gui/dialogs/rangeEditDialog.cpp:1588
 msgid "Overlay"
 msgstr ""
 
@@ -456,8 +456,8 @@ msgstr ""
 msgid "Multiple autosave states were found; would you like to restore one?"
 msgstr ""
 
-#: ../src/gui/dialogs/filterErrorDialog.cpp:37 ../src/backend/filter.cpp:435
-#: ../src/backend/filter.cpp:438
+#: ../src/gui/dialogs/filterErrorDialog.cpp:37 ../src/backend/filter.cpp:456
+#: ../src/backend/filter.cpp:459
 msgid "Error"
 msgstr ""
 
@@ -470,43 +470,43 @@ msgstr ""
 msgid "Filter Errors"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:46
+#: ../src/gui/dialogs/StashDialog.cpp:49
 msgid "Stashes"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:49
+#: ../src/gui/dialogs/StashDialog.cpp:52
 msgid "Stashed Tree"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:51
+#: ../src/gui/dialogs/StashDialog.cpp:54
 msgid "Properties"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:57
+#: ../src/gui/dialogs/StashDialog.cpp:60
 msgid "Stash Name"
 msgstr "Stash Name"
 
-#: ../src/gui/dialogs/StashDialog.cpp:58
+#: ../src/gui/dialogs/StashDialog.cpp:61
 msgid "Filter Count"
 msgstr "Filter Count"
 
-#: ../src/gui/dialogs/StashDialog.cpp:91
+#: ../src/gui/dialogs/StashDialog.cpp:94
 msgid "Stashed Trees"
 msgstr "Stashed Trees"
 
-#: ../src/gui/dialogs/StashDialog.cpp:94
+#: ../src/gui/dialogs/StashDialog.cpp:97
 msgid "Erase stashed item"
 msgstr ""
 
-#: ../src/gui/dialogs/StashDialog.cpp:95
+#: ../src/gui/dialogs/StashDialog.cpp:98
 msgid "Filter view for current stash"
 msgstr "Filteransicht für den aktuellen Stash"
 
-#: ../src/gui/dialogs/StashDialog.cpp:96
+#: ../src/gui/dialogs/StashDialog.cpp:99
 msgid "Settings for selected filter in current stash"
 msgstr "Einstellungen für den ausgewählten Stash"
 
-#: ../src/gui/dialogs/StashDialog.cpp:97
+#: ../src/gui/dialogs/StashDialog.cpp:100
 msgid "Available stashes"
 msgstr "Verfügbare Stash"
 
@@ -524,7 +524,7 @@ msgstr ""
 
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:104
 #: ../src/gui/dialogs/animateSubDialogs/stringKeyFrameDialog.cpp:350
-#: ../src/gui/dialogs/animateFilterDialog.cpp:192
+#: ../src/gui/dialogs/animateFilterDialog.cpp:196
 msgid "Frame"
 msgstr ""
 
@@ -593,12 +593,12 @@ msgid "Ramp"
 msgstr ""
 
 #: ../src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp:64
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1154
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1158
 msgid "Start Frame"
 msgstr ""
 
 #: ../src/gui/dialogs/animateSubDialogs/colourKeyFrameDialog.cpp:66
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1155
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1159
 msgid "End Frame"
 msgstr ""
 
@@ -642,197 +642,197 @@ msgstr ""
 msgid "Cameca/Ametek ENV"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:152
+#: ../src/gui/dialogs/animateFilterDialog.cpp:156
 msgid "Key frames"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:153
+#: ../src/gui/dialogs/animateFilterDialog.cpp:157
 msgid "Output Data"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:154
+#: ../src/gui/dialogs/animateFilterDialog.cpp:158
 msgid "Filters and properties"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:160
+#: ../src/gui/dialogs/animateFilterDialog.cpp:164
 msgid "Dir : "
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:163
+#: ../src/gui/dialogs/animateFilterDialog.cpp:167
 msgid "Output only when refresh required"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:165
+#: ../src/gui/dialogs/animateFilterDialog.cpp:169
 msgid "Data Types:"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:166
+#: ../src/gui/dialogs/animateFilterDialog.cpp:170
 msgid "3D Images"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:167
+#: ../src/gui/dialogs/animateFilterDialog.cpp:171
 msgid "File Suffix: "
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:169
+#: ../src/gui/dialogs/animateFilterDialog.cpp:173
 msgid "Size : "
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:171
+#: ../src/gui/dialogs/animateFilterDialog.cpp:175
 msgid "..."
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:172
+#: ../src/gui/dialogs/animateFilterDialog.cpp:176
 msgid "Point data"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:174
+#: ../src/gui/dialogs/animateFilterDialog.cpp:178
 msgid "Voxel data"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:175
+#: ../src/gui/dialogs/animateFilterDialog.cpp:179
 msgid "Range files"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:176
+#: ../src/gui/dialogs/animateFilterDialog.cpp:180
 msgid "Format"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:677
+#: ../src/gui/dialogs/animateFilterDialog.cpp:681
 msgid "transition frame"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:677
-#: ../src/gui/mainFrame.cpp:1674
+#: ../src/gui/dialogs/animateFilterDialog.cpp:681
+#: ../src/gui/mainFrame.cpp:1675
 msgid "Frame count"
 msgstr "Bildanzahl"
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:749
+#: ../src/gui/dialogs/animateFilterDialog.cpp:753
 msgid "Key frame : Colour"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:802
+#: ../src/gui/dialogs/animateFilterDialog.cpp:806
 msgid "File existed, but was unable to read or interpret file contents."
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:803
+#: ../src/gui/dialogs/animateFilterDialog.cpp:807
 msgid "String load failed"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:824
+#: ../src/gui/dialogs/animateFilterDialog.cpp:828
 msgid "Keyframe : decimal"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:833
+#: ../src/gui/dialogs/animateFilterDialog.cpp:837
 msgid "Keyframe : integer"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:842
+#: ../src/gui/dialogs/animateFilterDialog.cpp:846
 msgid "Keyframe : 3D Point"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:967
+#: ../src/gui/dialogs/animateFilterDialog.cpp:971
 msgid "Select or create new folder"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1147
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1151
 msgid "Export Animation"
 msgstr "Animation exportieren"
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1148
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1152
 msgid "Select filter"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1149
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1153
 msgid "Select property"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1151
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1171
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1155
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1175
 msgid "Filter"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1152
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1172
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1156
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1176
 msgid "Property"
 msgstr "Eigenschaft"
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1153
-#: ../src/backend/filters/voxelise.cpp:903
-#: ../src/backend/filters/spatialAnalysis.cpp:906
-#: ../src/backend/filters/transform.cpp:1133
-#: ../src/backend/filters/annotation.cpp:540
-#: ../src/backend/filters/annotation.cpp:546
-#: ../src/backend/filters/ionDownsample.cpp:463
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1157
+#: ../src/backend/filters/voxelise.cpp:861
+#: ../src/backend/filters/spatialAnalysis.cpp:978
+#: ../src/backend/filters/transform.cpp:1119
+#: ../src/backend/filters/annotation.cpp:545
+#: ../src/backend/filters/annotation.cpp:551
+#: ../src/backend/filters/ionDownsample.cpp:465
 msgid "Mode"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1156
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1160
 msgid "Keyframe table"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1157
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1161
 msgid "Remove the selected keyframe from the table"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1158
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1162
 msgid "Enter where the animation frames will be exported to"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1159
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1163
 msgid "Browse to directory where the animation frames will be exported to"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1161
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1165
 msgid ""
 "Title for files, result will be saved as #-name.png, where # is image number."
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1162
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1166
 msgid "Target resolution (image size)"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1164
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1168
 msgid "Select frame for property display"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1165
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1169
 msgid "Enter frame number to change frame (eg 1/20)"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1166
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1170
 msgid "Save point data (POS files) in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1167
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1171
 msgid "Save plots (as text files) in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1168
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1172
 msgid "Save voxel data (raw files) in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1169
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1173
 msgid "Save range files  in output folder?"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1174
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1178
 msgid "Animation parameters for current frame"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1175
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1179
 msgid "Abort animation"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1176
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1180
 msgid "Run Animation"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1247
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1251
 msgid "Filter view"
 msgstr ""
 
-#: ../src/gui/dialogs/animateFilterDialog.cpp:1248
+#: ../src/gui/dialogs/animateFilterDialog.cpp:1252
 msgid "Frame view"
 msgstr ""
 
@@ -853,59 +853,60 @@ msgstr "Zurücksetzen"
 msgid "Resolution Selection"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:75
+#: ../src/gui/dialogs/ExportPos.cpp:62
 msgid "Export:"
 msgstr "Exportieren:"
 
-#: ../src/gui/dialogs/ExportPos.cpp:76
-#: ../src/backend/filters/boundingBox.cpp:521
+#: ../src/gui/dialogs/ExportPos.cpp:63
+#: ../src/backend/filters/boundingBox.cpp:527
 msgid "Visible"
 msgstr "Sichtbar"
 
-#: ../src/gui/dialogs/ExportPos.cpp:77
+#: ../src/gui/dialogs/ExportPos.cpp:64
 msgid "Selected Data"
 msgstr "Daten auswählen"
 
-#: ../src/gui/dialogs/ExportPos.cpp:79
+#: ../src/gui/dialogs/ExportPos.cpp:66
 msgid "Available Data"
 msgstr "Verfügbare Daten"
 
-#: ../src/gui/dialogs/ExportPos.cpp:85
+#: ../src/gui/dialogs/ExportPos.cpp:72
 msgid "Selection"
 msgstr "Auswahl"
 
-#: ../src/gui/dialogs/ExportPos.cpp:110 ../src/gui/dialogs/ExportPos.cpp:113
+#: ../src/gui/dialogs/ExportPos.cpp:97 ../src/gui/dialogs/ExportPos.cpp:100
 msgid "Index"
 msgstr "Index"
 
-#: ../src/gui/dialogs/ExportPos.cpp:111 ../src/gui/dialogs/ExportPos.cpp:114
-#: ../src/backend/filters/compositionProfile.cpp:581
-#: ../src/backend/filters/spatialAnalysis.cpp:2199
-#: ../src/backend/filters/spatialAnalysis.cpp:2259
-#: ../src/backend/filters/spatialAnalysis.cpp:3224
-#: ../src/backend/filters/spatialAnalysis.cpp:3436
-#: ../src/backend/filters/spatialAnalysis.cpp:3495
-#: ../src/backend/filters/spectrumPlot.cpp:237
+#: ../src/gui/dialogs/ExportPos.cpp:98 ../src/gui/dialogs/ExportPos.cpp:101
+#: ../src/backend/filters/compositionProfile.cpp:604
+#: ../src/backend/filters/spatialAnalysis.cpp:2282
+#: ../src/backend/filters/spatialAnalysis.cpp:2375
+#: ../src/backend/filters/spatialAnalysis.cpp:2435
+#: ../src/backend/filters/spatialAnalysis.cpp:3388
+#: ../src/backend/filters/spatialAnalysis.cpp:3591
+#: ../src/backend/filters/spatialAnalysis.cpp:3650
+#: ../src/backend/filters/spectrumPlot.cpp:65
 msgid "Count"
 msgstr "Anzahl"
 
-#: ../src/gui/dialogs/ExportPos.cpp:457
+#: ../src/gui/dialogs/ExportPos.cpp:446
 msgid "Export Pos Data"
 msgstr "POS Daten exportieren"
 
-#: ../src/gui/dialogs/ExportPos.cpp:460
+#: ../src/gui/dialogs/ExportPos.cpp:449
 msgid "Tree of filters, select leaves to show ion data."
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:462
+#: ../src/gui/dialogs/ExportPos.cpp:451
 msgid "Add all data from all filters"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:463
+#: ../src/gui/dialogs/ExportPos.cpp:452
 msgid "Add all data from currently selected filter"
 msgstr ""
 
-#: ../src/gui/dialogs/ExportPos.cpp:464
+#: ../src/gui/dialogs/ExportPos.cpp:453
 msgid "Add selected data from currently selected filter"
 msgstr ""
 
@@ -953,7 +954,7 @@ msgstr "Kontrollfenster"
 msgid "Raw Data Panel"
 msgstr "Rohdatenfenster"
 
-#: ../src/gui/dialogs/prefDialog.cpp:94 ../src/gui/mainFrame.cpp:663
+#: ../src/gui/dialogs/prefDialog.cpp:94 ../src/gui/mainFrame.cpp:676
 msgid "Plot List"
 msgstr "Plotliste"
 
@@ -1039,82 +1040,82 @@ msgstr ""
 msgid "Camera"
 msgstr "Kamera"
 
-#: ../src/gui/mainFrame.cpp:105
+#: ../src/gui/mainFrame.cpp:119
 msgid "New camera name..."
 msgstr "Neuer Kameraname..."
 
-#: ../src/gui/mainFrame.cpp:106
+#: ../src/gui/mainFrame.cpp:120
 msgid "New stash name..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:111
+#: ../src/gui/mainFrame.cpp:125
 msgid "New Filter..."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:128 ../src/backend/filters/annotation.cpp:555
-#: ../src/backend/filters/annotation.cpp:659
+#: ../src/gui/mainFrame.cpp:142 ../src/backend/filters/annotation.cpp:560
+#: ../src/backend/filters/annotation.cpp:664
 #: ../src/backend/filters/annotation.h:96
 msgid "Annotation"
 msgstr "Kommentar"
 
-#: ../src/gui/mainFrame.cpp:129
+#: ../src/gui/mainFrame.cpp:143
 msgid "Bounding Box"
 msgstr "Begrenzungs-Box"
 
-#: ../src/gui/mainFrame.cpp:130 ../src/backend/filters/ionClip.cpp:619
+#: ../src/gui/mainFrame.cpp:144 ../src/backend/filters/ionClip.cpp:628
 #: ../src/backend/filters/ionClip.h:66
 msgid "Clipping"
 msgstr "Zuschneiden"
 
-#: ../src/gui/mainFrame.cpp:131 ../src/backend/filters/clusterAnalysis.h:143
+#: ../src/gui/mainFrame.cpp:145 ../src/backend/filters/clusterAnalysis.h:150
 msgid "Cluster Analysis"
 msgstr "Clusteranalyse"
 
-#: ../src/gui/mainFrame.cpp:132
+#: ../src/gui/mainFrame.cpp:146
 msgid "Compos. Profiles"
 msgstr "Konz.Profil"
 
-#: ../src/gui/mainFrame.cpp:133
+#: ../src/gui/mainFrame.cpp:147
 msgid "Downsampling"
 msgstr "Datenreduktion"
 
-#: ../src/gui/mainFrame.cpp:134
+#: ../src/gui/mainFrame.cpp:148
 msgid "Extern. Prog."
 msgstr "Ext. Progr."
 
-#: ../src/gui/mainFrame.cpp:135
+#: ../src/gui/mainFrame.cpp:149
 msgid "Ion Colour"
 msgstr "Ionenfarbe"
 
-#: ../src/gui/mainFrame.cpp:136
+#: ../src/gui/mainFrame.cpp:150
 msgid "Ion Info"
 msgstr "Ion Info"
 
-#: ../src/gui/mainFrame.cpp:137
+#: ../src/gui/mainFrame.cpp:151
 msgid "Ion Transform"
 msgstr "Ionentransform."
 
-#: ../src/gui/mainFrame.cpp:138 ../src/backend/filters/spectrumPlot.h:53
+#: ../src/gui/mainFrame.cpp:152 ../src/backend/filters/spectrumPlot.h:76
 msgid "Spectrum"
 msgstr "Spektrum"
 
-#: ../src/gui/mainFrame.cpp:139
+#: ../src/gui/mainFrame.cpp:153
 msgid "Range File"
 msgstr "Rangedatei"
 
-#: ../src/gui/mainFrame.cpp:140 ../src/backend/filters/spatialAnalysis.h:193
+#: ../src/gui/mainFrame.cpp:154 ../src/backend/filters/spatialAnalysis.h:202
 msgid "Spat. Analysis"
 msgstr "Räumliche Analyse"
 
-#: ../src/gui/mainFrame.cpp:141 ../src/backend/filters/voxelise.h:121
+#: ../src/gui/mainFrame.cpp:155 ../src/backend/filters/voxelise.h:122
 msgid "Voxelisation"
 msgstr "Voxelisation"
 
-#: ../src/gui/mainFrame.cpp:429
+#: ../src/gui/mainFrame.cpp:442
 msgid "OpenGL Failed"
 msgstr "OpenGL fehlgeschlagen"
 
-#: ../src/gui/mainFrame.cpp:430 ../src/gui/mainFrame.cpp:432
+#: ../src/gui/mainFrame.cpp:443 ../src/gui/mainFrame.cpp:445
 msgid ""
 "Unable to initialise the openGL (3D) panel. Program cannot start. Please "
 "check your video drivers."
@@ -1122,447 +1123,431 @@ msgstr ""
 "Kann das OpenGL (3D)-Panel nicht initialisieren. Das Programm kann nicht "
 "gestartet werden. Bitte überprüfen Sie Ihren Video-Treiber."
 
-#: ../src/gui/mainFrame.cpp:452
+#: ../src/gui/mainFrame.cpp:466
 msgid "&Open...\tCtrl+O"
 msgstr "&Öffnen...\tCtrl+O"
 
-#: ../src/gui/mainFrame.cpp:452
+#: ../src/gui/mainFrame.cpp:466
 msgid "Open state file"
 msgstr "Statusdatei öffnen"
 
-#: ../src/gui/mainFrame.cpp:453
+#: ../src/gui/mainFrame.cpp:467
 msgid "&Merge...\tCtrl+Shift+O"
 msgstr "&Zusammenführen...\tCtrl+Shift+O"
 
-#: ../src/gui/mainFrame.cpp:453
+#: ../src/gui/mainFrame.cpp:467
 msgid "Merge other file"
 msgstr "Merge other file"
 
-#: ../src/gui/mainFrame.cpp:457
+#: ../src/gui/mainFrame.cpp:471
 msgid "&Recent"
 msgstr "&Letzte"
 
-#: ../src/gui/mainFrame.cpp:458
+#: ../src/gui/mainFrame.cpp:472
 msgid "&Save\tCtrl+S"
 msgstr "&Speichern\tCtrl+S"
 
-#: ../src/gui/mainFrame.cpp:458
+#: ../src/gui/mainFrame.cpp:472
 msgid "Save state to file"
 msgstr "Status in Datei speichern"
 
-#: ../src/gui/mainFrame.cpp:460
+#: ../src/gui/mainFrame.cpp:474
 msgid "Save &As...\tCtrl+Shift+S"
 msgstr "Speichern &als...\tCtrl+Shift+S"
 
-#: ../src/gui/mainFrame.cpp:460
+#: ../src/gui/mainFrame.cpp:474
 msgid "Save current state to new file"
 msgstr "Aktuellen Status als neue Datei speichern"
 
-#: ../src/gui/mainFrame.cpp:463
+#: ../src/gui/mainFrame.cpp:477
 msgid "&Plot...\tCtrl+P"
 msgstr "&Plot...\tCtrl+P"
 
-#: ../src/gui/mainFrame.cpp:463
+#: ../src/gui/mainFrame.cpp:477
 msgid "Export Current Plot"
 msgstr "Aktuellen Plot exportieren"
 
-#: ../src/gui/mainFrame.cpp:464
+#: ../src/gui/mainFrame.cpp:478
 msgid "&Image...\tCtrl+I"
 msgstr "&Bild...\tCtrl+I"
 
-#: ../src/gui/mainFrame.cpp:464
+#: ../src/gui/mainFrame.cpp:478
 msgid "Export Current 3D View"
 msgstr "Aktuelle 3D Ansicht exportieren"
 
-#: ../src/gui/mainFrame.cpp:465
+#: ../src/gui/mainFrame.cpp:479
 msgid "Ion&s...\tCtrl+N"
 msgstr "Ion&en...\tCtrl+N"
 
-#: ../src/gui/mainFrame.cpp:465
+#: ../src/gui/mainFrame.cpp:479
 msgid "Export Ion Data"
 msgstr "Ionendaten exportieren"
 
-#: ../src/gui/mainFrame.cpp:466
+#: ../src/gui/mainFrame.cpp:480
 msgid "Ran&ges...\tCtrl+G"
 msgstr "Ran&ges...\tCtrl+G"
 
-#: ../src/gui/mainFrame.cpp:466
+#: ../src/gui/mainFrame.cpp:480
 msgid "Export Range Data"
 msgstr "Rangedaten exportieren"
 
-#: ../src/gui/mainFrame.cpp:467
-msgid "&Animate Filters...\tCtrl+A"
+#: ../src/gui/mainFrame.cpp:481
+msgid "&Animate Filters...\tCtrl+T"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:467
+#: ../src/gui/mainFrame.cpp:481
 msgid "Export Animated Filter"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:468
+#: ../src/gui/mainFrame.cpp:482
 msgid "Ani&mate Camera...\tCtrl+M"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:468
+#: ../src/gui/mainFrame.cpp:482
 msgid "Export Animated Camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:469
+#: ../src/gui/mainFrame.cpp:483
 msgid "Pac&kage...\tCtrl+K"
 msgstr "Pa&ket...\tCtrl+K"
 
-#: ../src/gui/mainFrame.cpp:469
+#: ../src/gui/mainFrame.cpp:483
 msgid "Export analysis package"
 msgstr "Analysepaket exportieren"
 
-#: ../src/gui/mainFrame.cpp:471
+#: ../src/gui/mainFrame.cpp:485
 msgid "&Export"
 msgstr "&Exportieren"
 
-#: ../src/gui/mainFrame.cpp:474
+#: ../src/gui/mainFrame.cpp:488
 msgid "&Quit\tCtrl+Q"
 msgstr "&Beenden\tCtrl+Q"
 
-#: ../src/gui/mainFrame.cpp:474 ../src/gui/mainFrame.cpp:476
+#: ../src/gui/mainFrame.cpp:488 ../src/gui/mainFrame.cpp:490
 msgid "Exit Program"
 msgstr "Programm beenden"
 
-#: ../src/gui/mainFrame.cpp:476
+#: ../src/gui/mainFrame.cpp:490
 msgid "E&xit"
 msgstr "E&xit"
 
-#: ../src/gui/mainFrame.cpp:478
+#: ../src/gui/mainFrame.cpp:492
 msgid "&File"
 msgstr "&Datei"
 
-#: ../src/gui/mainFrame.cpp:482
+#: ../src/gui/mainFrame.cpp:496
 msgid "&Background Colour...\tCtrl+B"
 msgstr "&Hintergrundfarbe...\tCtrl+B"
 
-#: ../src/gui/mainFrame.cpp:482
+#: ../src/gui/mainFrame.cpp:496
 msgid "Change background colour"
 msgstr "Hintergrundfarbe ändern"
 
-#: ../src/gui/mainFrame.cpp:486
+#: ../src/gui/mainFrame.cpp:500
 msgid "&Control Pane\tF2"
 msgstr "&Kontrollfenster\tF2"
 
-#: ../src/gui/mainFrame.cpp:486 ../src/gui/mainFrame.cpp:489
+#: ../src/gui/mainFrame.cpp:500 ../src/gui/mainFrame.cpp:503
 msgid "Toggle left control pane"
 msgstr "Linkes Kontrollfenster ein/aus schalten"
 
-#: ../src/gui/mainFrame.cpp:489
+#: ../src/gui/mainFrame.cpp:503
 msgid "&Control Pane\tAlt+C"
 msgstr "&Kontrollfenster\tAlt+C"
 
-#: ../src/gui/mainFrame.cpp:495
+#: ../src/gui/mainFrame.cpp:509
 msgid "&Raw Data Pane\tF3"
 msgstr "&Rohdatenfenster\tF3"
 
-#: ../src/gui/mainFrame.cpp:495 ../src/gui/mainFrame.cpp:498
+#: ../src/gui/mainFrame.cpp:509 ../src/gui/mainFrame.cpp:512
 msgid "Toggle raw data  pane (bottom)"
 msgstr "Rohdatenfenster (unten)"
 
-#: ../src/gui/mainFrame.cpp:498
+#: ../src/gui/mainFrame.cpp:512
 msgid "&Raw Data Pane\tAlt+R"
 msgstr "&Rohdatenfenster\tAlt+R"
 
-#: ../src/gui/mainFrame.cpp:502
+#: ../src/gui/mainFrame.cpp:516
 msgid "&Plot List\tF4"
 msgstr "&Plot Liste\tF4"
 
-#: ../src/gui/mainFrame.cpp:502 ../src/gui/mainFrame.cpp:504
+#: ../src/gui/mainFrame.cpp:516 ../src/gui/mainFrame.cpp:518
 msgid "Toggle plot list"
 msgstr "Plotliste ein/aus schalten"
 
-#: ../src/gui/mainFrame.cpp:504
+#: ../src/gui/mainFrame.cpp:518
 msgid "&Plot List\tAlt+P"
 msgstr "&Plot Liste\tAlt+P"
 
-#: ../src/gui/mainFrame.cpp:510
+#: ../src/gui/mainFrame.cpp:524
 msgid "&Legend\tCtrl+L"
 msgstr "&Legende\tCtrl+L"
 
-#: ../src/gui/mainFrame.cpp:510
+#: ../src/gui/mainFrame.cpp:524
 msgid "Toggle Legend display"
 msgstr "Legende anzeigen ein/aus"
 
-#: ../src/gui/mainFrame.cpp:512
+#: ../src/gui/mainFrame.cpp:526
 msgid "P&lot..."
 msgstr "P&lot..."
 
-#: ../src/gui/mainFrame.cpp:513
+#: ../src/gui/mainFrame.cpp:527
 msgid "&Axis\tCtrl+Shift+I"
 msgstr "&Achsen\tCtrl+Shift+I"
 
-#: ../src/gui/mainFrame.cpp:513
+#: ../src/gui/mainFrame.cpp:527
 msgid "Toggle World Axis display"
 msgstr "Hauptachsen ein/aus schalten"
 
-#: ../src/gui/mainFrame.cpp:518
+#: ../src/gui/mainFrame.cpp:532
 msgid "&Fullscreen mode\tF11"
 msgstr "&Vollbildmodus\tF11"
 
-#: ../src/gui/mainFrame.cpp:518 ../src/gui/mainFrame.cpp:520
+#: ../src/gui/mainFrame.cpp:532 ../src/gui/mainFrame.cpp:534
 msgid "Next fullscreen mode: with toolbars"
 msgstr "Nächster Vollbildmodus: ohne Werkzeugleisten"
 
-#: ../src/gui/mainFrame.cpp:520
+#: ../src/gui/mainFrame.cpp:534
 msgid "&Fullscreen mode\tCtrl+Shift+F"
 msgstr "&Vollbildmodus\tCtrl+Shift+F"
 
-#: ../src/gui/mainFrame.cpp:525
+#: ../src/gui/mainFrame.cpp:539
 msgid "&Undo\tCtrl+Z"
 msgstr "&Zurück\tCtrl+Z"
 
-#: ../src/gui/mainFrame.cpp:527
+#: ../src/gui/mainFrame.cpp:541
 msgid "&Redo\tCtrl+Y"
 msgstr "&Wiederholen\tCtrl+Y"
 
-#: ../src/gui/mainFrame.cpp:530
+#: ../src/gui/mainFrame.cpp:544
 msgid "&Range"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:533
+#: ../src/gui/mainFrame.cpp:547
 msgid "&Preferences"
 msgstr "&Voreinstellungen"
 
-#: ../src/gui/mainFrame.cpp:535
+#: ../src/gui/mainFrame.cpp:549
 msgid "&Edit"
 msgstr "&Bearbeiten"
 
-#: ../src/gui/mainFrame.cpp:538
+#: ../src/gui/mainFrame.cpp:552
 msgid "&View"
 msgstr "&Ansicht"
 
-#: ../src/gui/mainFrame.cpp:540
+#: ../src/gui/mainFrame.cpp:554
 msgid "&Help...\tCtrl+H"
 msgstr "&Hilfe...\tCtrl+H"
 
-#: ../src/gui/mainFrame.cpp:540
+#: ../src/gui/mainFrame.cpp:554
 msgid "Show help files and documentation"
 msgstr "Hilfedateien und Dokumentation anzeigen"
 
-#: ../src/gui/mainFrame.cpp:541
+#: ../src/gui/mainFrame.cpp:555
 msgid "&Contact..."
 msgstr "&Kontakt..."
 
-#: ../src/gui/mainFrame.cpp:541
+#: ../src/gui/mainFrame.cpp:555
 msgid "Open contact page"
 msgstr "Kontaktseite öffnen"
 
-#: ../src/gui/mainFrame.cpp:543
+#: ../src/gui/mainFrame.cpp:557
 msgid "&About..."
 msgstr "Über 3Depict..."
 
-#: ../src/gui/mainFrame.cpp:543
+#: ../src/gui/mainFrame.cpp:557
 msgid "Information about this program"
 msgstr "Informationen zu diesem Programm"
 
-#: ../src/gui/mainFrame.cpp:544
+#: ../src/gui/mainFrame.cpp:558
 msgid "&Help"
 msgstr "&Hilfe"
 
-#: ../src/gui/mainFrame.cpp:546
+#: ../src/gui/mainFrame.cpp:560
 msgid "Stashed Filters"
 msgstr "Zwischengelagerte Filter"
 
-#: ../src/gui/mainFrame.cpp:551
+#: ../src/gui/mainFrame.cpp:565
 msgid "New Filters"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:580
-msgid "Last Outputs"
-msgstr "Letzte Ausgabe"
-
-#: ../src/gui/mainFrame.cpp:582
+#: ../src/gui/mainFrame.cpp:595
 msgid "Auto Refresh"
 msgstr ""
 "Autom.\n"
 "aktualisieren"
 
-#: ../src/gui/mainFrame.cpp:588
+#: ../src/gui/mainFrame.cpp:601
 msgid "Filter settings"
 msgstr "Filtereinstellungen"
 
-#: ../src/gui/mainFrame.cpp:591
+#: ../src/gui/mainFrame.cpp:604
 msgid "Camera Name"
 msgstr "Kameraname"
 
-#: ../src/gui/mainFrame.cpp:598
+#: ../src/gui/mainFrame.cpp:611
 msgid "3D Post-processing"
 msgstr "3D Nachbearbeitung"
 
-#: ../src/gui/mainFrame.cpp:600
+#: ../src/gui/mainFrame.cpp:613
 msgid "Enable Cropping"
 msgstr "Zuschneiden aktivieren"
 
-#: ../src/gui/mainFrame.cpp:602 ../src/gui/mainFrame.cpp:613
+#: ../src/gui/mainFrame.cpp:615 ../src/gui/mainFrame.cpp:626
 msgid "x-y"
 msgstr "x-y"
 
-#: ../src/gui/mainFrame.cpp:603 ../src/gui/mainFrame.cpp:614
+#: ../src/gui/mainFrame.cpp:616 ../src/gui/mainFrame.cpp:627
 msgid "x-z"
 msgstr "x-z"
 
-#: ../src/gui/mainFrame.cpp:604 ../src/gui/mainFrame.cpp:615
+#: ../src/gui/mainFrame.cpp:617 ../src/gui/mainFrame.cpp:628
 msgid "y-x"
 msgstr "y-x"
 
-#: ../src/gui/mainFrame.cpp:605 ../src/gui/mainFrame.cpp:616
+#: ../src/gui/mainFrame.cpp:618 ../src/gui/mainFrame.cpp:629
 msgid "y-z"
 msgstr "y-z"
 
-#: ../src/gui/mainFrame.cpp:606 ../src/gui/mainFrame.cpp:617
+#: ../src/gui/mainFrame.cpp:619 ../src/gui/mainFrame.cpp:630
 msgid "z-x"
 msgstr "z-x"
 
-#: ../src/gui/mainFrame.cpp:607 ../src/gui/mainFrame.cpp:618
+#: ../src/gui/mainFrame.cpp:620 ../src/gui/mainFrame.cpp:631
 msgid "z-y"
 msgstr "z-y"
 
-#: ../src/gui/mainFrame.cpp:622
+#: ../src/gui/mainFrame.cpp:635
 msgid "Use camera coordinates"
 msgstr "Verwende Kamerakoordinaten"
 
-#: ../src/gui/mainFrame.cpp:623
+#: ../src/gui/mainFrame.cpp:636
 msgid "dX"
 msgstr "dX"
 
-#: ../src/gui/mainFrame.cpp:625
+#: ../src/gui/mainFrame.cpp:638
 msgid "dY"
 msgstr "dY"
 
-#: ../src/gui/mainFrame.cpp:627
+#: ../src/gui/mainFrame.cpp:640
 msgid "dZ"
 msgstr "dZ"
 
-#: ../src/gui/mainFrame.cpp:629
+#: ../src/gui/mainFrame.cpp:642
 msgid "Enable Anaglyphic Stereo"
 msgstr "Anaglyphic Stereo aktivieren"
 
-#: ../src/gui/mainFrame.cpp:630
+#: ../src/gui/mainFrame.cpp:643
 msgid "Flip Channels"
 msgstr "Kanäle tauschen"
 
-#: ../src/gui/mainFrame.cpp:631
+#: ../src/gui/mainFrame.cpp:644
 msgid "Anaglyph Mode"
 msgstr "Anaglyphmodus"
 
-#: ../src/gui/mainFrame.cpp:633
+#: ../src/gui/mainFrame.cpp:646
 msgid "Red-Blue"
 msgstr "Rot-Blau"
 
-#: ../src/gui/mainFrame.cpp:634
+#: ../src/gui/mainFrame.cpp:647
 msgid "Red-Green"
 msgstr "Rot-Grün"
 
-#: ../src/gui/mainFrame.cpp:635
+#: ../src/gui/mainFrame.cpp:648
 msgid "Red-Cyan"
 msgstr "Rot-Zyan"
 
-#: ../src/gui/mainFrame.cpp:636
+#: ../src/gui/mainFrame.cpp:649
 msgid "Green-Magenta"
 msgstr "Grün-Magenta"
 
-#: ../src/gui/mainFrame.cpp:640
+#: ../src/gui/mainFrame.cpp:653
 msgid "Baseline Separation"
 msgstr "Basislinienabstand"
 
-#: ../src/gui/mainFrame.cpp:642 ../src/backend/filters/voxelise.cpp:982
-#: ../src/backend/filters/voxelise.cpp:1142
-#: ../src/backend/filters/compositionProfile.cpp:1109
-#: ../src/backend/filters/boundingBox.cpp:648
-#: ../src/backend/filters/annotation.cpp:901
-#: ../src/backend/filters/dataLoad.cpp:651
-#: ../src/backend/filters/spectrumPlot.cpp:459
+#: ../src/gui/mainFrame.cpp:655 ../src/backend/filters/voxelise.cpp:937
+#: ../src/backend/filters/voxelise.cpp:1105
+#: ../src/backend/filters/compositionProfile.cpp:1143
+#: ../src/backend/filters/boundingBox.cpp:654
+#: ../src/backend/filters/annotation.cpp:906
+#: ../src/backend/filters/dataLoad.cpp:666
+#: ../src/backend/filters/spectrumPlot.cpp:675
 msgid "Appearance"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:643
+#: ../src/gui/mainFrame.cpp:656
 msgid "Smooth && translucent objects"
 msgstr "Glatte && durchsichtige Objekte"
 
-#: ../src/gui/mainFrame.cpp:645
+#: ../src/gui/mainFrame.cpp:658
 msgid "3D lighting"
 msgstr "3D Beleuchtung"
 
-#: ../src/gui/mainFrame.cpp:648
+#: ../src/gui/mainFrame.cpp:661
 msgid "Performance"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:649
+#: ../src/gui/mainFrame.cpp:662
 msgid "Fast and weak randomisation."
 msgstr "Schnelle aber schwache Randomisierung"
 
-#: ../src/gui/mainFrame.cpp:651
+#: ../src/gui/mainFrame.cpp:664
 msgid "Limit Output Pts"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:657
+#: ../src/gui/mainFrame.cpp:670
 msgid "Filter caching"
 msgstr "Filter zwischenspeichern"
 
-#: ../src/gui/mainFrame.cpp:659
+#: ../src/gui/mainFrame.cpp:672
 msgid "Max. Ram usage (%)"
 msgstr "Max. RAM-Nutzung (%)"
 
-#: ../src/gui/mainFrame.cpp:718
-msgid "Type"
-msgstr "Type"
-
-#: ../src/gui/mainFrame.cpp:719
-msgid "Num"
-msgstr "Num"
-
-#: ../src/gui/mainFrame.cpp:734
+#: ../src/gui/mainFrame.cpp:743
 msgid "Warning: Your configuration file appears to be invalid:\n"
 msgstr "Warnung: Ihre Konfigurationsdatei scheint ungültig zu sein.\n"
 
-#: ../src/gui/mainFrame.cpp:735
+#: ../src/gui/mainFrame.cpp:744
 msgid "\tConfig Load: "
 msgstr "\tConfig Load: "
 
-#: ../src/gui/mainFrame.cpp:984
+#: ../src/gui/mainFrame.cpp:1018
 msgid "Current state has not been saved, would you like to save it now?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:985
+#: ../src/gui/mainFrame.cpp:1019
 msgid "State changed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1005
+#: ../src/gui/mainFrame.cpp:1037
 msgid "Readable files (*.xml, *.pos, *.txt,*.csv, *.ato)"
 msgstr "Lesbare Dateien (*.xml, *.pos, *.txt,*.csv,*.ato)"
 
-#: ../src/gui/mainFrame.cpp:1007
+#: ../src/gui/mainFrame.cpp:1039
 msgid "XML State File (*.xml)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1008
+#: ../src/gui/mainFrame.cpp:1040
 msgid "POS File (*.pos)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1009
+#: ../src/gui/mainFrame.cpp:1041
 msgid "LAWATAP ATO File (*.ato)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1010
+#: ../src/gui/mainFrame.cpp:1042
 msgid "Text File (*.txt, *.csv)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1011
+#: ../src/gui/mainFrame.cpp:1043
 msgid "All Files (*)"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1024 ../src/gui/mainFrame.cpp:1080
+#: ../src/gui/mainFrame.cpp:1056 ../src/gui/mainFrame.cpp:1112
 msgid "Select Data or State File..."
 msgstr "Daten oder Statusdatei auswählen..."
 
-#: ../src/gui/mainFrame.cpp:1068 ../src/gui/mainFrame.cpp:1407
-msgid "Loaded file."
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:1081
+#: ../src/gui/mainFrame.cpp:1113
 msgid ""
 "3Depict file (*.xml, *.pos,*.txt)|*.xml;*.pos;*.txt|POS File (*.pos)|*.pos|"
 "XML State File (*.xml)|*.xml|All Files (*)|*"
@@ -1570,23 +1555,23 @@ msgstr ""
 "3Depictdateien (*.xml, *.pos,*.txt)|*.xml;*.pos;*.txt|POS Datei (*.pos)|*."
 "pos|XML Status Datei (*.xml)|*.xml|All Files (*)|*"
 
-#: ../src/gui/mainFrame.cpp:1092
+#: ../src/gui/mainFrame.cpp:1124
 msgid "Merged file."
 msgstr "Datei zusammengeführt."
 
-#: ../src/gui/mainFrame.cpp:1199
+#: ../src/gui/mainFrame.cpp:1231
 msgid "Tip: You can use ⌘ (command) to merge"
 msgstr "Tip: Sie können ⌘ (command) zum Zusammenführen verwenden"
 
-#: ../src/gui/mainFrame.cpp:1201
+#: ../src/gui/mainFrame.cpp:1233
 msgid "Tip: You can use ctrl to merge"
 msgstr "Tip: Sie können strg zum Zusammen führen verwenden"
 
-#: ../src/gui/mainFrame.cpp:1241
+#: ../src/gui/mainFrame.cpp:1276
 msgid "Load error"
 msgstr "Fehler beim Laden"
 
-#: ../src/gui/mainFrame.cpp:1242
+#: ../src/gui/mainFrame.cpp:1277
 msgid ""
 "Error loading state file.\n"
 "See console for more info."
@@ -1594,7 +1579,7 @@ msgstr ""
 "Fehler beim Laden der Statusdatei.\n"
 "Konsole für mehr Informationen."
 
-#: ../src/gui/mainFrame.cpp:1250
+#: ../src/gui/mainFrame.cpp:1285
 msgid ""
 "This state file contains filters that can be unsafe to run\n"
 "Do you wish to remove these before continuing?."
@@ -1602,24 +1587,24 @@ msgstr ""
 "Diese Statusdatei enthält Filter deren Anwendung möglicherweise unsicher "
 "ist. Wollen Sie diese entfernen."
 
-#: ../src/gui/mainFrame.cpp:1251
+#: ../src/gui/mainFrame.cpp:1286
 msgid "Security warning"
 msgstr "Sicherheitswarnung"
 
-#: ../src/gui/mainFrame.cpp:1468 ../src/gui/mainFrame.cpp:1564
-#: ../src/gui/mainFrame.cpp:1997
+#: ../src/gui/mainFrame.cpp:1469 ../src/gui/mainFrame.cpp:1565
+#: ../src/gui/mainFrame.cpp:1990
 msgid "Unable to save"
 msgstr "Speichern nicht möglich"
 
-#: ../src/gui/mainFrame.cpp:1469
+#: ../src/gui/mainFrame.cpp:1470
 msgid "No plot available. Please create a plot before exporting."
 msgstr "Kein Plot vefügbar. Plot muss vor dem Exportieren erzeugt werden."
 
-#: ../src/gui/mainFrame.cpp:1473
+#: ../src/gui/mainFrame.cpp:1474
 msgid "Save plot..."
 msgstr "Plot speichern..."
 
-#: ../src/gui/mainFrame.cpp:1474
+#: ../src/gui/mainFrame.cpp:1475
 msgid ""
 "By Extension (svg,png)|*.svg;*.png|Scalable Vector Graphics File (*.svg)|*."
 "svg|PNG File (*.png)|*.png|All Files (*)|*"
@@ -1627,53 +1612,53 @@ msgstr ""
 "Dateierweiterung (svg,png)|*.svg;*.png|Skalierbare Vektorgrafik (*.svg)|*."
 "svg|PNG Datei (*.png)|*.png|Alle Dateien (*)|*"
 
-#: ../src/gui/mainFrame.cpp:1528
+#: ../src/gui/mainFrame.cpp:1529
 msgid "Select type for save"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1529
+#: ../src/gui/mainFrame.cpp:1530
 msgid "Choose file type"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1549 ../src/gui/mainFrame.cpp:1606
-#: ../src/gui/mainFrame.cpp:1642
+#: ../src/gui/mainFrame.cpp:1550 ../src/gui/mainFrame.cpp:1607
+#: ../src/gui/mainFrame.cpp:1643
 msgid "Choose resolution"
 msgstr "Auflösung auswählen"
 
-#: ../src/gui/mainFrame.cpp:1565
+#: ../src/gui/mainFrame.cpp:1566
 msgid "Unknown file extension. Please use \"svg\" or \"png\""
 msgstr "Unbekannte Dateierweiterung. Bitte verwenden Sie \"svg\" oder \"png\""
 
-#: ../src/gui/mainFrame.cpp:1576
+#: ../src/gui/mainFrame.cpp:1577
 msgid "Saved plot: "
 msgstr "Gespeicherter Plot:"
 
-#: ../src/gui/mainFrame.cpp:1583 ../src/gui/mainFrame.cpp:1635
+#: ../src/gui/mainFrame.cpp:1584 ../src/gui/mainFrame.cpp:1636
 msgid "Save Image..."
 msgstr "Speichere Bild..."
 
-#: ../src/gui/mainFrame.cpp:1584 ../src/gui/mainFrame.cpp:1636
+#: ../src/gui/mainFrame.cpp:1585 ../src/gui/mainFrame.cpp:1637
 msgid "PNG File (*.png)|*.png|All Files (*)|*"
 msgstr "PNG Datei (*.png)|*.png|Alle Dateien (*)|*"
 
-#: ../src/gui/mainFrame.cpp:1598
+#: ../src/gui/mainFrame.cpp:1599
 msgid "File already exists. Overwrite?"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1599 ../src/gui/mainFrame.cpp:2385
-#: ../src/gui/mainFrame.cpp:2483 ../src/gui/mainFrame.cpp:2507
+#: ../src/gui/mainFrame.cpp:1600 ../src/gui/mainFrame.cpp:2378
+#: ../src/gui/mainFrame.cpp:2475 ../src/gui/mainFrame.cpp:2498
 msgid "Overwrite?"
 msgstr "Überschreiben?"
 
-#: ../src/gui/mainFrame.cpp:1626 ../src/gui/mainFrame.cpp:1702
+#: ../src/gui/mainFrame.cpp:1627 ../src/gui/mainFrame.cpp:1703
 msgid "Saved 3D View :"
 msgstr "Gespeicherte 3D Ansicht"
 
-#: ../src/gui/mainFrame.cpp:1656
+#: ../src/gui/mainFrame.cpp:1657
 msgid "Program limitation"
 msgstr "Programmeinschränkung"
 
-#: ../src/gui/mainFrame.cpp:1657
+#: ../src/gui/mainFrame.cpp:1658
 msgid ""
 "Limitation on the screenshot dimension; please ensure that both width and "
 "height exceed the initial values,\n"
@@ -1685,104 +1670,96 @@ msgstr ""
 "kleiner als die ursprünglichen Werte sind. Sollte Sie dies stören, melden "
 "Sie bitte einen Bug."
 
-#: ../src/gui/mainFrame.cpp:1674
+#: ../src/gui/mainFrame.cpp:1675
 msgid "Number of frames"
 msgstr "Bilderanzahl"
 
-#: ../src/gui/mainFrame.cpp:1829
+#: ../src/gui/mainFrame.cpp:1835
 msgid "Cannot animate with no filters."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1908
+#: ../src/gui/mainFrame.cpp:1913
 msgid "Animating"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1909
+#: ../src/gui/mainFrame.cpp:1914
 msgid "Performing refresh"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1938
+#: ../src/gui/mainFrame.cpp:1940
 msgid "Filter property change failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1958
+#: ../src/gui/mainFrame.cpp:1962
 msgid "Refresh failed on frame :"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:1983
-msgid "Scene generation failed"
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:1984
-msgid "Unable to generate scene for frame "
-msgstr ""
-
-#: ../src/gui/mainFrame.cpp:1998
+#: ../src/gui/mainFrame.cpp:1991
 msgid "Image save failed for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2023
+#: ../src/gui/mainFrame.cpp:2016
 msgid "Ion save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2024
+#: ../src/gui/mainFrame.cpp:2017
 msgid "Unable to save ions for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2055
+#: ../src/gui/mainFrame.cpp:2048
 msgid "Plot save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2056
+#: ../src/gui/mainFrame.cpp:2049
 msgid "Unable to save plot or frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2097
+#: ../src/gui/mainFrame.cpp:2090
 msgid "Range save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2098
+#: ../src/gui/mainFrame.cpp:2091
 msgid "Unable to save range for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2127
+#: ../src/gui/mainFrame.cpp:2120
 msgid "Voxel save failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2128
+#: ../src/gui/mainFrame.cpp:2121
 msgid "Unable to save voxels for frame "
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2158
+#: ../src/gui/mainFrame.cpp:2150
 msgid "Animate failed"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2183 ../src/gui/mainFrame.cpp:2341
-#: ../src/gui/mainFrame.cpp:2437
+#: ../src/gui/mainFrame.cpp:2174 ../src/gui/mainFrame.cpp:2333
+#: ../src/gui/mainFrame.cpp:2430
 msgid "No filters means no data to export"
 msgstr "Keine Filter bedeutet keine Daten zum Exportieren"
 
-#: ../src/gui/mainFrame.cpp:2197
+#: ../src/gui/mainFrame.cpp:2188
 msgid "Package name"
 msgstr "Paketname"
 
-#: ../src/gui/mainFrame.cpp:2198
+#: ../src/gui/mainFrame.cpp:2189
 msgid "Package directory name"
 msgstr "Paketverzeichnis"
 
-#: ../src/gui/mainFrame.cpp:2200
+#: ../src/gui/mainFrame.cpp:2191
 msgid "AnalysisPackage"
 msgstr "Analysepaket"
 
-#: ../src/gui/mainFrame.cpp:2213
+#: ../src/gui/mainFrame.cpp:2204
 msgid "Package folder already exists, won't overwrite."
 msgstr "Paketverzeichnis existiert bereits. Werde es nicht überschreiben."
 
-#: ../src/gui/mainFrame.cpp:2214
+#: ../src/gui/mainFrame.cpp:2205
 msgid "Not available"
 msgstr "Nicht verfügbar"
 
-#: ../src/gui/mainFrame.cpp:2239
+#: ../src/gui/mainFrame.cpp:2230
 msgid ""
 "Package folder creation failed\n"
 "check writing to this location is possible."
@@ -1790,211 +1767,215 @@ msgstr ""
 "Anlegen des Paketverzeichnisses fehlgeschlagen\n"
 "Überprüfen Sie ob der angegenbene Ort schreibgeschützt ist."
 
-#: ../src/gui/mainFrame.cpp:2240
+#: ../src/gui/mainFrame.cpp:2231
 msgid "Folder creation failed"
 msgstr "Anlegen des Ordners ist fehlgeschlagen"
 
-#: ../src/gui/mainFrame.cpp:2259
+#: ../src/gui/mainFrame.cpp:2251
 msgid "Copying"
 msgstr "kopiere"
 
-#: ../src/gui/mainFrame.cpp:2260
+#: ../src/gui/mainFrame.cpp:2252
 msgid "Copying referenced files"
 msgstr "Copying referenced files"
 
-#: ../src/gui/mainFrame.cpp:2319
+#: ../src/gui/mainFrame.cpp:2311
 msgid "Error copying file"
 msgstr "Fehler beim Kopieren der Datei"
 
-#: ../src/gui/mainFrame.cpp:2328
+#: ../src/gui/mainFrame.cpp:2320
 msgid "Saved package: "
 msgstr "Gespeicherte Pakete: "
 
-#: ../src/gui/mainFrame.cpp:2351
+#: ../src/gui/mainFrame.cpp:2344
 msgid "Export"
 msgstr "Exportieren"
 
-#: ../src/gui/mainFrame.cpp:2356
+#: ../src/gui/mainFrame.cpp:2349
 msgid "POS Data (*.pos)|*.pos|All Files (*)|*"
 msgstr "POS-Daten (*.pos)|*.pos|All Files (*)|*"
 
-#: ../src/gui/mainFrame.cpp:2384 ../src/gui/mainFrame.cpp:2482
+#: ../src/gui/mainFrame.cpp:2377 ../src/gui/mainFrame.cpp:2474
 msgid "File already exists, overwrite?"
 msgstr "Datei existiert bereits. Überschreiben?"
 
-#: ../src/gui/mainFrame.cpp:2416
+#: ../src/gui/mainFrame.cpp:2409
 msgid "Saved ions: "
 msgstr "Gespeicherte Ionen:"
 
-#: ../src/gui/mainFrame.cpp:2441
+#: ../src/gui/mainFrame.cpp:2434
 msgid "Export Ranges"
 msgstr "Range exportieren"
 
-#: ../src/gui/mainFrame.cpp:2464
+#: ../src/gui/mainFrame.cpp:2456
 msgid "Save state..."
 msgstr "Speichere Status..."
 
-#: ../src/gui/mainFrame.cpp:2465
+#: ../src/gui/mainFrame.cpp:2457
 msgid "XML state file (*.xml)|*.xml|All Files (*)|*"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2506
+#: ../src/gui/mainFrame.cpp:2497
 msgid "Files have been referred to using relative paths. Keep relative paths?"
 msgstr ""
 "Auf Dateien wurde mit relativen Pfaden verwiesen. Relative Pfade beibehalten?"
 
-#: ../src/gui/mainFrame.cpp:2539
+#: ../src/gui/mainFrame.cpp:2530
 msgid "Saved state: "
 msgstr "Gespeicherter Status: "
 
-#: ../src/gui/mainFrame.cpp:2616
+#: ../src/gui/mainFrame.cpp:2606
 msgid "Range editor"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:2871
+#: ../src/gui/mainFrame.cpp:2863
 msgid "Manual not found locally. Launching web browser"
 msgstr "Anleitung konnte lokal nicht gefunden werden. Starte Webbrowser"
 
-#: ../src/gui/mainFrame.cpp:2880
+#: ../src/gui/mainFrame.cpp:2872
 msgid "Opening contact page in external web browser"
 msgstr "Öffne Kontaktseite in externem Browser"
 
-#: ../src/gui/mainFrame.cpp:2892
+#: ../src/gui/mainFrame.cpp:2880
 msgid "No filter stashes to edit."
 msgstr "Keine Filterstashes zum Bearbeiten."
 
-#: ../src/gui/mainFrame.cpp:2896
+#: ../src/gui/mainFrame.cpp:2884
 msgid "Filter Stashes"
 msgstr "Filter Stashes"
 
-#: ../src/gui/mainFrame.cpp:2925
+#: ../src/gui/mainFrame.cpp:2901
 msgid "Quick and dirty analysis for point data."
 msgstr "\"Quick and dirty\" Analyse von Punktdaten."
 
-#: ../src/gui/mainFrame.cpp:2935
+#: ../src/gui/mainFrame.cpp:2911
 msgid "Compiled with wx Version: "
 msgstr "Kompiliert mit wx Version: "
 
-#: ../src/gui/mainFrame.cpp:2956
+#: ../src/gui/mainFrame.cpp:2932
 msgid "Press enter to store new stash"
 msgstr "Eingabe drücken um neuen Filterstash zu speichern"
 
-#: ../src/gui/mainFrame.cpp:2962
+#: ../src/gui/mainFrame.cpp:2938
 msgid "Press enter to restore stash"
 msgstr "Eingabe drücken um Stash wiederherzustellen"
 
-#: ../src/gui/mainFrame.cpp:2997
+#: ../src/gui/mainFrame.cpp:2971
 msgid "Unable to create stash, selection invalid"
 msgstr "Stash kann nicht erstellt werden, Auswahl ungültig"
 
-#: ../src/gui/mainFrame.cpp:3005
+#: ../src/gui/mainFrame.cpp:2978
 msgid "Created new filter tree stash"
 msgstr "Neuer Filterstash wurde erzeugt"
 
-#: ../src/gui/mainFrame.cpp:3132
+#: ../src/gui/mainFrame.cpp:3083
 msgid "Filter type not a data source - can't be at tree base"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3309
+#: ../src/gui/mainFrame.cpp:3223
 msgid "Moving - Hold ⌘ (command) to copy"
 msgstr "Verschieben - Halte ⌘ (command) um zu kopieren"
 
-#: ../src/gui/mainFrame.cpp:3311
+#: ../src/gui/mainFrame.cpp:3225
 msgid "Moving - Hold control to copy"
 msgstr "Verschieben - Halte Strg zum kopieren"
 
-#: ../src/gui/mainFrame.cpp:3630
+#: ../src/gui/mainFrame.cpp:3545
 msgid "Press enter to store new camera"
 msgstr "Eingabe drücken um neue Kamera zu speichern"
 
-#: ../src/gui/mainFrame.cpp:3632
+#: ../src/gui/mainFrame.cpp:3547
 msgid "Press enter to restore camera"
 msgstr "Eingabe drücken um Kamera wiederherzustellen"
 
-#: ../src/gui/mainFrame.cpp:3657 ../src/gui/mainFrame.cpp:3698
+#: ../src/gui/mainFrame.cpp:3572 ../src/gui/mainFrame.cpp:3613
 msgid "Restored camera: "
 msgstr "Wiederhergestellte Kamera: "
 
-#: ../src/gui/mainFrame.cpp:3677
+#: ../src/gui/mainFrame.cpp:3590
 msgid "Stored camera: "
 msgstr "Gespeicherte Kamera: "
 
-#: ../src/gui/mainFrame.cpp:3759
+#: ../src/gui/mainFrame.cpp:3676
 msgid "Select an item from the filter tree before choosing a new filter"
 msgstr ""
 "Aktivieren Sie zuerst ein Punkt aus dem Filterverlauf bevor Sie einen neuen "
 "Filter auswählen"
 
-#: ../src/gui/mainFrame.cpp:3761
+#: ../src/gui/mainFrame.cpp:3678
 msgid "Load data source (file->open) before choosing a new filter"
 msgstr "Lade Datenquelle (Datei->öffnen) vor dem Auswählen eines neuen Filters"
 
-#: ../src/gui/mainFrame.cpp:3785
+#: ../src/gui/mainFrame.cpp:3704
 msgid "Select RNG File..."
 msgstr "RNG Datei auswählen..."
 
-#: ../src/gui/mainFrame.cpp:3806
+#: ../src/gui/mainFrame.cpp:3725
 msgid "Failed reading range file."
 msgstr "Fehler beim Lesen der Rangedatei."
 
-#: ../src/gui/mainFrame.cpp:3810
+#: ../src/gui/mainFrame.cpp:3729
 msgid "Error loading file"
 msgstr "Fehler beim Laden der Datei"
 
-#: ../src/gui/mainFrame.cpp:3870 ../src/gui/mainFrame.cpp:3937
-#: ../src/gui/mainFrame.cpp:5268 ../src/gui/mainFrame.cpp:5802
+#: ../src/gui/mainFrame.cpp:3788 ../src/gui/mainFrame.cpp:3882
+#: ../src/gui/mainFrame.cpp:5284 ../src/gui/mainFrame.cpp:5803
 msgid "Cons."
 msgstr "Kons."
 
-#: ../src/gui/mainFrame.cpp:3904
+#: ../src/gui/mainFrame.cpp:3844
 msgid "Refresh Aborted."
 msgstr "Aktualisieren abgebrochen"
 
-#: ../src/gui/mainFrame.cpp:3941
+#: ../src/gui/mainFrame.cpp:3886
 msgid "*Cons."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:3943
+#: ../src/gui/mainFrame.cpp:3888
 msgid "§Cons."
 msgstr "§Kons."
 
-#: ../src/gui/mainFrame.cpp:4037
+#: ../src/gui/mainFrame.cpp:3945
+msgid "Complete"
+msgstr ""
+
+#: ../src/gui/mainFrame.cpp:4030
 msgid "msgs"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4077
+#: ../src/gui/mainFrame.cpp:4071
 msgid "Autosave complete."
 msgstr "Autosave beendet."
 
-#: ../src/gui/mainFrame.cpp:4273
-msgid "Aborted."
-msgstr "Abgebrochen"
+#: ../src/gui/mainFrame.cpp:4281
+msgid "Aborting...."
+msgstr ""
 
-#: ../src/gui/mainFrame.cpp:4324
+#: ../src/gui/mainFrame.cpp:4344
 msgid "Updated."
 msgstr "Updated."
 
-#: ../src/gui/mainFrame.cpp:4331
+#: ../src/gui/mainFrame.cpp:4352
 msgid "\\% Done (Esc aborts)"
 msgstr "\\% fertig (Esc abbrechen)"
 
-#: ../src/gui/mainFrame.cpp:4333
+#: ../src/gui/mainFrame.cpp:4354
 msgid "\\% Done"
 msgstr "\\% fertig"
 
-#: ../src/gui/mainFrame.cpp:4609
+#: ../src/gui/mainFrame.cpp:4624
 msgid "Tip: You can shift-click to force full refresh, if required"
 msgstr "Tipp: Verwende shift-click um komplettes Aktualisieren zu erzwingen"
 
-#: ../src/gui/mainFrame.cpp:4672
+#: ../src/gui/mainFrame.cpp:4686
 msgid "No data to save"
 msgstr "Keine Daten zum Sichern"
 
-#: ../src/gui/mainFrame.cpp:4842
+#: ../src/gui/mainFrame.cpp:4860
 msgid "Aborting..."
 msgstr "Abbrechen..."
 
-#: ../src/gui/mainFrame.cpp:4848
+#: ../src/gui/mainFrame.cpp:4866
 msgid ""
 "Waiting for refresh to abort. Exiting could lead to the program "
 "backgrounding. Exit anyway? "
@@ -2002,63 +1983,63 @@ msgstr ""
 "Waiting for refresh to abort. Exiting could lead to the program "
 "backgrounding. Exit anyway? "
 
-#: ../src/gui/mainFrame.cpp:4849 ../src/gui/mainFrame.cpp:4869
+#: ../src/gui/mainFrame.cpp:4867 ../src/gui/mainFrame.cpp:4887
 msgid "Confirmation request"
 msgstr "Bestätigungsabfrage"
 
-#: ../src/gui/mainFrame.cpp:4868
+#: ../src/gui/mainFrame.cpp:4886
 msgid "Are you sure you wish to exit 3Depict?"
 msgstr "Sind Sie sicher, dass Sie 3Depict beenden wollen?"
 
-#: ../src/gui/mainFrame.cpp:5296
+#: ../src/gui/mainFrame.cpp:5312
 msgid "Update Notice: New version "
 msgstr "Updatenotiz: Neue Version "
 
-#: ../src/gui/mainFrame.cpp:5296
+#: ../src/gui/mainFrame.cpp:5312
 msgid " found online."
 msgstr " online gefunden."
 
-#: ../src/gui/mainFrame.cpp:5300
+#: ../src/gui/mainFrame.cpp:5316
 msgid "Online Check: "
 msgstr "Überprüfe online:"
 
-#: ../src/gui/mainFrame.cpp:5300
+#: ../src/gui/mainFrame.cpp:5316
 msgid " is up-to-date."
 msgstr "ist up-to-date."
 
-#: ../src/gui/mainFrame.cpp:5395
+#: ../src/gui/mainFrame.cpp:5406
 msgid "An auto-save state was found, would you like to restore it?."
 msgstr "Ein auto-save Status wurde gefunden. Wollen Sie ihn wiederherstellen?"
 
-#: ../src/gui/mainFrame.cpp:5396
+#: ../src/gui/mainFrame.cpp:5407
 msgid "Autosave"
 msgstr "Automatisch speichern"
 
-#: ../src/gui/mainFrame.cpp:5403
+#: ../src/gui/mainFrame.cpp:5414
 msgid "Unable to load autosave file.."
 msgstr "Kann Autosavedatei nicht laden.."
 
-#: ../src/gui/mainFrame.cpp:5600
+#: ../src/gui/mainFrame.cpp:5605
 msgid "List of available filters"
 msgstr "Liste der verfügbaren Filter"
 
-#: ../src/gui/mainFrame.cpp:5602
+#: ../src/gui/mainFrame.cpp:5607
 msgid "Tree - drag to move items, hold ⌘ for copy. Tap delete to remove items"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5604
+#: ../src/gui/mainFrame.cpp:5609
 msgid ""
 "Tree - drag to move items, hold Ctrl for copy. Tap delete to remove items."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5606
+#: ../src/gui/mainFrame.cpp:5611
 msgid ""
 "Enable/Disable automatic updates of data when filter change takes effect"
 msgstr ""
 "Ein/Ausschalten vom automatischen Aktualisieren der Daten wenn Änderungen am "
 "Filter wirksam werden"
 
-#: ../src/gui/mainFrame.cpp:5609
+#: ../src/gui/mainFrame.cpp:5614
 msgid ""
 "Enable/Disable \"Alpha blending\" (transparency) in rendering system. "
 "Blending is used to smooth objects (avoids artefacts known as \"jaggies\") "
@@ -2070,7 +2051,7 @@ msgstr ""
 "und transparente Oberflächen zu generieren. Ausschalten erlaubt schnelleres "
 "Renden führt jedoch zu blockigerer Darstellung."
 
-#: ../src/gui/mainFrame.cpp:5610
+#: ../src/gui/mainFrame.cpp:5615
 msgid ""
 "Enable/Disable lighting calculations in rendering, for objects that request "
 "this. Lighting provides important depth cues for objects comprised of 3D "
@@ -2081,7 +2062,7 @@ msgstr ""
 "umrandete Objekte. Deaktivieren erlaubt u.U. schnelleres Rendern bei "
 "komplizierten Szenen."
 
-#: ../src/gui/mainFrame.cpp:5611
+#: ../src/gui/mainFrame.cpp:5616
 msgid ""
 "Enable/Disable weak randomisation (Galois linear feedback shift register). "
 "Strong randomisation uses a much slower random selection method, but "
@@ -2093,14 +2074,14 @@ msgstr ""
 "Auswahlmethode bietet dafür aber einen besseren Schutz gegen unbeabsichtigte "
 "Korrelationen und wird für die endgültige Analyse empfohlen."
 
-#: ../src/gui/mainFrame.cpp:5613
+#: ../src/gui/mainFrame.cpp:5618
 msgid ""
 "Limit the number of points that can be displayed in the 3D  scene. Does not "
 "affect filter tree calculations. Disabling this can severely reduce "
 "performance, due to large numbers of points being visible at once."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5614
+#: ../src/gui/mainFrame.cpp:5619
 msgid ""
 "Enable/Disable caching of intermediate results during filter updates. "
 "Disabling caching will use less system RAM, though changes to any filter "
@@ -2112,30 +2093,30 @@ msgstr ""
 "bei Änderungen der Filterparameter der ganze Filterbaum neu berechnet wird. "
 "Dies erhöht den Rechenaufwand deutlich."
 
-#: ../src/gui/mainFrame.cpp:5616
+#: ../src/gui/mainFrame.cpp:5621
 msgid "Camera data information"
 msgstr "Kamerainformation"
 
-#: ../src/gui/mainFrame.cpp:5620
+#: ../src/gui/mainFrame.cpp:5625
 msgid "Enable/disable visual effects on final 3D output"
 msgstr "Ein/Ausschalten von visuellen Effekten in der finalen 3D Ausgabe."
 
-#: ../src/gui/mainFrame.cpp:5622
+#: ../src/gui/mainFrame.cpp:5627
 msgid "Enable cropping post-process effect"
 msgstr "Cropping post-Prozess Effect einschalten"
 
-#: ../src/gui/mainFrame.cpp:5625
+#: ../src/gui/mainFrame.cpp:5630
 msgid ""
 "Colour based 3D effect enable/disable - requires appropriate colour filter "
 "3D glasses."
 msgstr ""
 "Farbbasierte 3D-Effekte ein/ausschalten - erfordert geeignete 3D-Brillen"
 
-#: ../src/gui/mainFrame.cpp:5626
+#: ../src/gui/mainFrame.cpp:5631
 msgid "Glasses colour mode"
 msgstr "Brillenfarbmodus"
 
-#: ../src/gui/mainFrame.cpp:5628
+#: ../src/gui/mainFrame.cpp:5633
 msgid ""
 "Level of separation between left and right images, which sets 3D depth to "
 "visual distortion tradeoff"
@@ -2143,95 +2124,95 @@ msgstr ""
 "Level of separation between left and right images, which sets 3D depth to "
 "visual distortion tradeoff"
 
-#: ../src/gui/mainFrame.cpp:5632
+#: ../src/gui/mainFrame.cpp:5637
 msgid "X"
 msgstr "X"
 
-#: ../src/gui/mainFrame.cpp:5633
+#: ../src/gui/mainFrame.cpp:5638
 msgid "Y"
 msgstr "Y"
 
-#: ../src/gui/mainFrame.cpp:5634
+#: ../src/gui/mainFrame.cpp:5639
 msgid "Save raw data to file"
 msgstr "Speichere Rohdaten in Datei"
 
-#: ../src/gui/mainFrame.cpp:5635
+#: ../src/gui/mainFrame.cpp:5640
 msgid "Copy raw data to clipboard"
 msgstr "Kopiere Rohdaten in die Zwischenablage"
 
-#: ../src/gui/mainFrame.cpp:5636
+#: ../src/gui/mainFrame.cpp:5641
 msgid "Manage \"stashed\" data."
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5637
+#: ../src/gui/mainFrame.cpp:5642
 msgid "Program text output"
 msgstr "Programm Textausgabe"
 
-#: ../src/gui/mainFrame.cpp:5638
+#: ../src/gui/mainFrame.cpp:5643
 msgid "Select active camera, or type to create new named camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5639
+#: ../src/gui/mainFrame.cpp:5644
 msgid "Remove the selected camera"
 msgstr "Ausgewählte Kamera entfernen"
 
-#: ../src/gui/mainFrame.cpp:5640
+#: ../src/gui/mainFrame.cpp:5645
 msgid "Perform cropping from coordinate frame of camera"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5641
+#: ../src/gui/mainFrame.cpp:5646
 msgid ""
 "Set the maximum amount of RAM to use in order to speed repeat computations"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5642
+#: ../src/gui/mainFrame.cpp:5647
 msgid "Collapse the filter tree"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5643
+#: ../src/gui/mainFrame.cpp:5648
 msgid "Expand the filter tree"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5644
+#: ../src/gui/mainFrame.cpp:5649
 msgid "Process the filter tree, hold shift to purge cached filter data"
 msgstr ""
 
-#: ../src/gui/mainFrame.cpp:5762
+#: ../src/gui/mainFrame.cpp:5763
 msgid "Crop"
 msgstr "Zuschneiden"
 
-#: ../src/gui/mainFrame.cpp:5763
+#: ../src/gui/mainFrame.cpp:5764
 msgid "Stereo"
 msgstr "Stereo"
 
-#: ../src/gui/mainFrame.cpp:5780
-#: ../src/backend/filters/externalProgram.cpp:577
-#: ../src/backend/filters/ionColour.cpp:312
-#: ../src/backend/filters/spectrumPlot.cpp:418
+#: ../src/gui/mainFrame.cpp:5781
+#: ../src/backend/filters/externalProgram.cpp:596
+#: ../src/backend/filters/ionColour.cpp:316
+#: ../src/backend/filters/spectrumPlot.cpp:604
 msgid "Data"
 msgstr "Daten"
 
-#: ../src/gui/mainFrame.cpp:5781
+#: ../src/gui/mainFrame.cpp:5782
 msgid "Cam"
 msgstr "Cam"
 
-#: ../src/gui/mainFrame.cpp:5782
+#: ../src/gui/mainFrame.cpp:5783
 msgid "Post"
 msgstr "Post"
 
-#: ../src/gui/mainFrame.cpp:5783
+#: ../src/gui/mainFrame.cpp:5784
 msgid "Tools"
 msgstr "Werkz."
 
-#: ../src/gui/mainFrame.cpp:5801
+#: ../src/gui/mainFrame.cpp:5802
 msgid "Raw"
 msgstr "Roh"
 
-#: ../src/gui/mathglPane.cpp:241
+#: ../src/gui/mathglPane.cpp:259
 msgid "No plots selected."
 msgstr "Kein Plot ausgewählt."
 
-#: ../src/gui/mathglPane.cpp:1165
+#: ../src/gui/mathglPane.cpp:1198
 msgid ""
 "Unable to allocate requested memory.\n"
 " Try a lower resolution, or save as vector (SVG)."
@@ -2239,44 +2220,44 @@ msgstr ""
 "Kann den notwendigen Speicher nicht zuordnen. Versuche eine geringer "
 "Auflösung oder speichere als Vektografik (svg)."
 
-#: ../src/gui/mathglPane.cpp:1167
+#: ../src/gui/mathglPane.cpp:1200
 msgid "Plotting functions returned an error:\n"
 msgstr "Plot-Funktion meldete einen Fehler:\n"
 
-#: ../src/gui/mathglPane.cpp:1169
+#: ../src/gui/mathglPane.cpp:1202
 msgid "File readback check failed"
 msgstr "File readback check failed"
 
-#: ../src/gui/mathglPane.cpp:1171
+#: ../src/gui/mathglPane.cpp:1204
 msgid "Filesize during readback appears to be zero."
 msgstr "Filesize during readback appears to be zero."
 
-#: ../src/3Depict.cpp:381
+#: ../src/3Depict.cpp:375
 msgid "File : "
 msgstr "Datei : "
 
-#: ../src/3Depict.cpp:381
+#: ../src/3Depict.cpp:375
 msgid " does not exist. Skipping"
 msgstr " existiert nicht. Überspringe"
 
-#: ../src/backend/configFile.cpp:186
+#: ../src/backend/configFile.cpp:187
 msgid "Config file present, but is not valid (root node test)"
 msgstr "Konfigurationsdatei vorhanden, aber nicht gültig (root node test)"
 
-#: ../src/backend/configFile.cpp:227
+#: ../src/backend/configFile.cpp:228
 msgid "Unable to interpret recent file entry"
 msgstr "Kann den letzten Dateieintrag nicht interpretieren"
 
-#: ../src/backend/configFile.cpp:267
+#: ../src/backend/configFile.cpp:268
 msgid "Unable to determine filter type in defaults listing."
 msgstr "Kann den Filtertyp im Defaultslisting nicht bestimmen."
 
-#: ../src/backend/configFile.cpp:604
+#: ../src/backend/configFile.cpp:605
 msgid "Online access for non win32/apple platforms is intentionally disabled, "
 msgstr ""
 "Onlinezugang für nicht Win32/apple systeme wurde absichtlich deaktiviert."
 
-#: ../src/backend/configFile.cpp:605
+#: ../src/backend/configFile.cpp:606
 msgid ""
 "regardless of the settings you use here. Use your package manager to keep up-"
 "to-date"
@@ -2284,8 +2265,10 @@ msgstr ""
 "Nutzen Sie Ihren Paketmanager um up-to-date zu sein unabhängig von den "
 "Einstellungen die Sie hier verwenden"
 
-#: ../src/backend/plot.cpp:28 ../src/backend/filters/voxelise.cpp:123
-#: ../src/backend/filters/voxelise.cpp:133
+#: ../src/backend/plot.cpp:28 ../src/backend/filters/algorithms/mass.cpp:25
+#: ../src/backend/filters/voxelise.cpp:124
+#: ../src/backend/filters/voxelise.cpp:130
+#: ../src/backend/filters/spectrumPlot.cpp:76
 msgid "None"
 msgstr "Keiner"
 
@@ -2321,419 +2304,474 @@ msgstr ""
 msgid "Scatter"
 msgstr ""
 
-#: ../src/backend/plot.cpp:787 ../src/backend/plot.cpp:795
+#: ../src/backend/plot.cpp:739 ../src/backend/plot.cpp:747
 msgid "Multiple data types"
 msgstr ""
 
-#: ../src/backend/plot.cpp:1559
+#: ../src/backend/plot.cpp:1581
 msgid "error"
 msgstr "Fehler"
 
-#: ../src/backend/plot.cpp:1772
+#: ../src/backend/plot.cpp:1821
 msgid "Amplitude"
 msgstr ""
 
-#: ../src/backend/filtertree.cpp:1060
+#: ../src/backend/filtertree.cpp:1146
 msgid "WARNING: Skipping node "
 msgstr "WARNUNG: Skipping node "
 
-#: ../src/backend/filtertree.cpp:1060
+#: ../src/backend/filtertree.cpp:1146
 msgid " as it was not recognised"
 msgstr " wurde nicht erkannt."
 
-#: ../src/backend/filtertree.cpp:1098
+#: ../src/backend/filtertree.cpp:1184
 msgid "Error processing node: "
 msgstr "Fehler beim Verarbeiten von Node: "
 
-#: ../src/backend/filters/externalProgram.cpp:545
-#: ../src/backend/filters/externalProgram.cpp:559
+#: ../src/backend/filters/externalProgram.cpp:259
+msgid "Collate Input"
+msgstr ""
+
+#: ../src/backend/filters/externalProgram.cpp:348
+msgid "Execute"
+msgstr ""
+
+#: ../src/backend/filters/externalProgram.cpp:389
+msgid "Collate output"
+msgstr ""
+
+#: ../src/backend/filters/externalProgram.cpp:564
+#: ../src/backend/filters/externalProgram.cpp:578
 msgid "Command"
 msgstr "Befehl"
 
-#: ../src/backend/filters/externalProgram.cpp:548
+#: ../src/backend/filters/externalProgram.cpp:567
 msgid ""
 "Full command to send to operating system. See manual for escape sequence "
 "meanings"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:552
+#: ../src/backend/filters/externalProgram.cpp:571
 msgid "Work Dir"
 msgstr "Arbeitsverzeichnis"
 
-#: ../src/backend/filters/externalProgram.cpp:555
+#: ../src/backend/filters/externalProgram.cpp:574
 msgid "Directory to run the command in"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:562
+#: ../src/backend/filters/externalProgram.cpp:581
 msgid "Cleanup input"
 msgstr "Bereinige Eingabe"
 
-#: ../src/backend/filters/externalProgram.cpp:565
+#: ../src/backend/filters/externalProgram.cpp:584
 msgid "Erase input files when command completed"
 msgstr ""
 
-#: ../src/backend/filters/externalProgram.cpp:570
+#: ../src/backend/filters/externalProgram.cpp:589
 msgid "Cache"
 msgstr "Zwischenspeicher"
 
-#: ../src/backend/filters/externalProgram.cpp:573
+#: ../src/backend/filters/externalProgram.cpp:592
 msgid ""
 "Assume program does not alter its output, unless inputs from 3Depict are "
 "altered"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:60
-#: ../src/backend/filters/compositionProfile.cpp:44
+#: ../src/backend/filters/algorithms/mass.cpp:26
+msgid "Flat TOF"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:33
+msgid "INsufficient bins to perform fit"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:34
+msgid "Insufficient counts to perform fit"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:35
+msgid "Insufficient data to perform fit"
+msgstr ""
+
+#: ../src/backend/filters/algorithms/mass.cpp:36
+msgid "Data did not appear to be random noise - cannot fit noise level"
+msgstr ""
+
+#: ../src/backend/filters/ionClip.cpp:65
+#: ../src/backend/filters/compositionProfile.cpp:53
 msgid "Sphere"
 msgstr "Kugel"
 
-#: ../src/backend/filters/ionClip.cpp:61
+#: ../src/backend/filters/ionClip.cpp:66
 msgid "Plane"
 msgstr "Ebene"
 
-#: ../src/backend/filters/ionClip.cpp:62
-#: ../src/backend/filters/compositionProfile.cpp:43
+#: ../src/backend/filters/ionClip.cpp:67
 msgid "Cylinder"
 msgstr "Zylinder"
 
-#: ../src/backend/filters/ionClip.cpp:63
+#: ../src/backend/filters/ionClip.cpp:68
 msgid "Aligned box"
 msgstr "Ausgerichtete Box"
 
-#: ../src/backend/filters/ionClip.cpp:488
-#: ../src/backend/filters/compositionProfile.cpp:943
+#: ../src/backend/filters/ionClip.cpp:497
+#: ../src/backend/filters/compositionProfile.cpp:971
 msgid "Primitive"
 msgstr "Primitiv"
 
-#: ../src/backend/filters/ionClip.cpp:491
+#: ../src/backend/filters/ionClip.cpp:500
 msgid "Shape of clipping object"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:497
-#: ../src/backend/filters/compositionProfile.cpp:949
+#: ../src/backend/filters/ionClip.cpp:506
+#: ../src/backend/filters/compositionProfile.cpp:977
 msgid "Show Primitive"
 msgstr "Zeige Primitiv"
 
-#: ../src/backend/filters/ionClip.cpp:500
+#: ../src/backend/filters/ionClip.cpp:509
 msgid "Display the 3D interaction object"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:505
+#: ../src/backend/filters/ionClip.cpp:514
 msgid "Invert Clip"
 msgstr "Invertiere Clip"
 
-#: ../src/backend/filters/ionClip.cpp:508
+#: ../src/backend/filters/ionClip.cpp:517
 msgid ""
 "Switch between retaining points inside (false) and outside (true) of "
 "primitive"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:522
-#: ../src/backend/filters/compositionProfile.cpp:1005
+#: ../src/backend/filters/ionClip.cpp:531
+#: ../src/backend/filters/compositionProfile.cpp:1034
 msgid "Position for centre of sphere"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:527
-#: ../src/backend/filters/ionClip.cpp:587
-#: ../src/backend/filters/compositionProfile.cpp:988
-#: ../src/backend/filters/compositionProfile.cpp:1010
-#: ../src/backend/filters/spatialAnalysis.cpp:111
-#: ../src/backend/filters/spatialAnalysis.cpp:791
+#: ../src/backend/filters/ionClip.cpp:536
+#: ../src/backend/filters/ionClip.cpp:596
+#: ../src/backend/filters/compositionProfile.cpp:1017
+#: ../src/backend/filters/compositionProfile.cpp:1039
+#: ../src/backend/filters/spatialAnalysis.cpp:121
+#: ../src/backend/filters/spatialAnalysis.cpp:863
 msgid "Radius"
 msgstr "Radius"
 
-#: ../src/backend/filters/ionClip.cpp:530
-#: ../src/backend/filters/compositionProfile.cpp:1013
+#: ../src/backend/filters/ionClip.cpp:539
+#: ../src/backend/filters/compositionProfile.cpp:1042
 msgid "Radius of sphere"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:544
+#: ../src/backend/filters/ionClip.cpp:553
 msgid "Position that plane passes through"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:549
+#: ../src/backend/filters/ionClip.cpp:558
 msgid "Plane Normal"
 msgstr "Plane Normal"
 
-#: ../src/backend/filters/ionClip.cpp:552
+#: ../src/backend/filters/ionClip.cpp:561
 msgid "Perpendicular direction for plane"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:566
+#: ../src/backend/filters/ionClip.cpp:575
 msgid "Centre of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:571
-#: ../src/backend/filters/compositionProfile.cpp:972
-#: ../src/backend/filters/spatialAnalysis.cpp:782
-#: ../src/backend/filters/transform.cpp:1277
+#: ../src/backend/filters/ionClip.cpp:580
+#: ../src/backend/filters/compositionProfile.cpp:1001
+#: ../src/backend/filters/spatialAnalysis.cpp:854
+#: ../src/backend/filters/transform.cpp:1263
 msgid "Axis"
 msgstr "Achse"
 
-#: ../src/backend/filters/ionClip.cpp:574
+#: ../src/backend/filters/ionClip.cpp:583
 msgid "Positive vector for cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:579
-#: ../src/backend/filters/compositionProfile.cpp:980
+#: ../src/backend/filters/ionClip.cpp:588
+#: ../src/backend/filters/compositionProfile.cpp:1009
 msgid "Lock Axis Mag."
 msgstr "Achsen Vergr. sperren"
 
-#: ../src/backend/filters/ionClip.cpp:582
+#: ../src/backend/filters/ionClip.cpp:591
 msgid "Prevent changing length of cylinder during 3D interaction"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:590
-#: ../src/backend/filters/compositionProfile.cpp:991
-#: ../src/backend/filters/spatialAnalysis.cpp:794
+#: ../src/backend/filters/ionClip.cpp:599
+#: ../src/backend/filters/compositionProfile.cpp:1020
+#: ../src/backend/filters/spatialAnalysis.cpp:866
 msgid "Radius of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:603
+#: ../src/backend/filters/ionClip.cpp:612
 msgid "Centre of axis aligned box"
 msgstr ""
 
-#: ../src/backend/filters/ionClip.cpp:608
+#: ../src/backend/filters/ionClip.cpp:617
 msgid "Corner offset"
 msgstr "Corner offset"
 
-#: ../src/backend/filters/ionClip.cpp:611
+#: ../src/backend/filters/ionClip.cpp:620
 msgid "Vector to corner of box"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:80
-#: ../src/backend/filters/clusterAnalysis.cpp:1045
+#: ../src/backend/filters/clusterAnalysis.cpp:82
+#: ../src/backend/filters/clusterAnalysis.cpp:1040
 msgid "Size Distribution"
 msgstr "Größenverteilung"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:81
+#: ../src/backend/filters/clusterAnalysis.cpp:83
 msgid "Chemistry Distribution"
 msgstr "Chemische Verteilung"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:499
+#: ../src/backend/filters/clusterAnalysis.cpp:493
 msgid "No range data. Can't cluster."
 msgstr "Keine Rangedaten. Clusteranalyse nicht möglich."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:510
+#: ../src/backend/filters/clusterAnalysis.cpp:504
 msgid ""
 "No ranges selected for cluster \"core\". Cannot continue with clustering."
 msgstr ""
 "Kein Range für cluster \"core\" ausgewählt. Kann mit Clusteranalyse nicht "
 "weitermachen."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:519
+#: ../src/backend/filters/clusterAnalysis.cpp:513
 msgid ""
 "No ranges selected for cluster \"bulk\". Cannot continue with clustering."
 msgstr ""
 "Kein Range für \"bulk\" ausgewählt. Kann mit Clusteranalyse nicht "
 "weitermachen."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:789
+#: ../src/backend/filters/clusterAnalysis.cpp:679
+msgid "Morphology Plot"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:680
+msgid "\\lambda_1:\\lambda_2 ratio"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:681
+msgid "\\lambda_2:\\lambda_3 ratio"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:726
+msgid "No clusters had sufficient dimensionality to compute singular values"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:784
 msgid "Found :"
 msgstr "Gefunden:"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:791
+#: ../src/backend/filters/clusterAnalysis.cpp:786
 msgid " clusters"
 msgstr " Cluster"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:873
+#: ../src/backend/filters/clusterAnalysis.cpp:868
 msgid "Compositions (fractional, core+bulk)"
 msgstr "Zusammensetzungen (fractional, core+bulk)"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:875
+#: ../src/backend/filters/clusterAnalysis.cpp:870
 msgid "Compositions (fractional, core only)"
 msgstr "Zusammensetzungen (fractional, core only)"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:893
+#: ../src/backend/filters/clusterAnalysis.cpp:888
 msgid "Frequencies (core+bulk)"
 msgstr "Häufigkeiten (core+bulk)"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:924
+#: ../src/backend/filters/clusterAnalysis.cpp:919
 msgid "Core Link + Erode"
 msgstr "Core Link + Erode"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:928
-#: ../src/backend/filters/clusterAnalysis.cpp:936
-#: ../src/backend/filters/spatialAnalysis.cpp:541
-#: ../src/backend/filters/spatialAnalysis.cpp:549
-#: ../src/backend/filters/transform.cpp:1140
-#: ../src/backend/filters/ionInfo.cpp:441
+#: ../src/backend/filters/clusterAnalysis.cpp:923
+#: ../src/backend/filters/clusterAnalysis.cpp:931
+#: ../src/backend/filters/spatialAnalysis.cpp:605
+#: ../src/backend/filters/spatialAnalysis.cpp:613
+#: ../src/backend/filters/transform.cpp:1126
+#: ../src/backend/filters/ionInfo.cpp:546
 msgid "Algorithm"
 msgstr "Algorithmus"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:932
+#: ../src/backend/filters/clusterAnalysis.cpp:927
 msgid "Cluster algorithm mode"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:943
+#: ../src/backend/filters/clusterAnalysis.cpp:938
 msgid "Core Classify"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:946
+#: ../src/backend/filters/clusterAnalysis.cpp:941
 msgid ""
 "Enable core-classifcation pre-step in clustering (Stephenson et al, 2007)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:952
+#: ../src/backend/filters/clusterAnalysis.cpp:947
 msgid "Core Classify Dist"
 msgstr "Core Classify Dist"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:955
+#: ../src/backend/filters/clusterAnalysis.cpp:950
 msgid "Restrict only atoms by distance to be cluster sources"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:960
+#: ../src/backend/filters/clusterAnalysis.cpp:955
 msgid "Classify Knn Max"
 msgstr "Classify Knn Max"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:963
+#: ../src/backend/filters/clusterAnalysis.cpp:958
 msgid ""
 "Require that the kth NN (this number) is within the classify distance, to be "
 "a cluster source"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:969
+#: ../src/backend/filters/clusterAnalysis.cpp:964
 msgid "Core Link Dist"
 msgstr "Core Link Dist"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:972
+#: ../src/backend/filters/clusterAnalysis.cpp:967
 msgid "Distance between clusters to allow linking"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:977
+#: ../src/backend/filters/clusterAnalysis.cpp:972
 msgid "Bulk Link"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:980
-#: ../src/backend/filters/clusterAnalysis.cpp:998
+#: ../src/backend/filters/clusterAnalysis.cpp:975
+#: ../src/backend/filters/clusterAnalysis.cpp:993
 msgid "Enable  linking of non-cluster species - eg for composition analysis "
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:987
+#: ../src/backend/filters/clusterAnalysis.cpp:982
 msgid "Bulk Link (Envelope) Dist"
 msgstr "Bulk Link (Envelope) Dist"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:990
+#: ../src/backend/filters/clusterAnalysis.cpp:985
 msgid ""
 "Distance from core points that form cluster that is used to grab surrounding "
 "bulk points"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:995
+#: ../src/backend/filters/clusterAnalysis.cpp:990
 msgid "Erosion"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1004
+#: ../src/backend/filters/clusterAnalysis.cpp:999
 msgid "Erode Dist"
 msgstr "Erode Dist"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1007
+#: ../src/backend/filters/clusterAnalysis.cpp:1002
 msgid ""
 "Distance from unclustered material in which bulk points are eroded from "
 "cluster"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1013
+#: ../src/backend/filters/clusterAnalysis.cpp:1008
 msgid "Clustering Params"
 msgstr "Cluster Parameter"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1018
+#: ../src/backend/filters/clusterAnalysis.cpp:1013
 msgid "Size Cropping"
 msgstr "Größeneinschrankungen"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1021
+#: ../src/backend/filters/clusterAnalysis.cpp:1016
 msgid "Remove clusters based upon size distribution"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1028
+#: ../src/backend/filters/clusterAnalysis.cpp:1023
 msgid "Min Size"
 msgstr "Min Größe"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1031
+#: ../src/backend/filters/clusterAnalysis.cpp:1026
 msgid "Remove clusters below this size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1036
+#: ../src/backend/filters/clusterAnalysis.cpp:1031
 msgid "Max Size"
 msgstr "Max Größe"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1039
+#: ../src/backend/filters/clusterAnalysis.cpp:1034
 msgid "Remove clusters above this size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1048
+#: ../src/backend/filters/clusterAnalysis.cpp:1043
 msgid "Show number of clusters as a function of cluster size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1054
+#: ../src/backend/filters/clusterAnalysis.cpp:1049
 msgid "Log Scale"
 msgstr "Log. Skala"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1057
+#: ../src/backend/filters/clusterAnalysis.cpp:1052
 msgid "Use logarithmic scale for size distribution"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1075
+#: ../src/backend/filters/clusterAnalysis.cpp:1059
+msgid "Morphology Dist."
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:1062
+msgid "Create a plot showing cluster aspect ratio"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:1068
 msgid "Cluster Id"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1078
+#: ../src/backend/filters/clusterAnalysis.cpp:1071
 msgid "Assign cluster output a unique per-cluster value (id)."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1085
+#: ../src/backend/filters/clusterAnalysis.cpp:1078
 msgid "Chemistry Dist."
 msgstr "Chemistry Dist."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1088
+#: ../src/backend/filters/clusterAnalysis.cpp:1081
 msgid "Create a plot showing chemistry for each cluster size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1095
-#: ../src/backend/filters/compositionProfile.cpp:1053
-#: ../src/backend/filters/spatialAnalysis.cpp:849
-#: ../src/backend/filters/ionInfo.cpp:412
+#: ../src/backend/filters/clusterAnalysis.cpp:1088
+#: ../src/backend/filters/compositionProfile.cpp:1087
+#: ../src/backend/filters/spatialAnalysis.cpp:921
+#: ../src/backend/filters/ionInfo.cpp:462
 msgid "Normalise"
 msgstr "Normalisieren"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1098
+#: ../src/backend/filters/clusterAnalysis.cpp:1091
 msgid "Convert cluster counts to composition"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1104
+#: ../src/backend/filters/clusterAnalysis.cpp:1097
 msgid "Postprocess"
 msgstr "Postprozess"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1123
+#: ../src/backend/filters/clusterAnalysis.cpp:1116
 msgid "If selected, use as \"core\" ion type (can make clusters)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1128
+#: ../src/backend/filters/clusterAnalysis.cpp:1121
 msgid "Core Ranges"
 msgstr "Core Ranges"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1142
+#: ../src/backend/filters/clusterAnalysis.cpp:1135
 msgid ""
 "If selected, use as \"bulk\" ion type (can be included in existing clusters)"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1147
+#: ../src/backend/filters/clusterAnalysis.cpp:1140
 msgid "Bulk Ranges"
 msgstr "Bulk Ranges"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1163
+#: ../src/backend/filters/clusterAnalysis.cpp:1156
 msgid "Max. Sep + Erode"
 msgstr "Max. Sep + Erode"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1891
+#: ../src/backend/filters/clusterAnalysis.cpp:1894
 msgid " --------------------------- Parameter selection notice ------------- "
 msgstr " --------------------------- Parameterauswahl Notiz ------------- "
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1892
+#: ../src/backend/filters/clusterAnalysis.cpp:1895
 msgid "You have specified a bulk distance larger than half your link distance."
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1893
+#: ../src/backend/filters/clusterAnalysis.cpp:1896
 msgid ""
 "You can do this; thats OK, but the output is no longer independent of the "
 "computational process;"
@@ -2741,7 +2779,7 @@ msgstr ""
 "Sie könne das machen, das ist in Ordnung, aber die Ausgabe ist nicht länger "
 "unabhängig vom Berechnungsprozess"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1894
+#: ../src/backend/filters/clusterAnalysis.cpp:1897
 msgid ""
 "This will be a problem in the case where two or more clusters can equally "
 "lay claim to a \"bulk\" ion. "
@@ -2749,7 +2787,7 @@ msgstr ""
 "Dies ist ein Problem wenn zwei oder mehrere Cluster auf dasselbe \"bulk\" "
 "Ion Anspruch erheben. "
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1895
+#: ../src/backend/filters/clusterAnalysis.cpp:1898
 msgid ""
 " If your inter-cluster distance is sufficiently large (larger than your bulk "
 "linking distance), then you can get away with this."
@@ -2757,7 +2795,7 @@ msgstr ""
 " If your inter-cluster distance is sufficiently large (larger than your bulk "
 "linking distance), then you can get away with this."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1896
+#: ../src/backend/filters/clusterAnalysis.cpp:1899
 msgid ""
 " In theory it is possible to \"join\" the clusters, but this has not been "
 "implemented for speed reasons."
@@ -2765,7 +2803,7 @@ msgstr ""
 "Theoretisch ist es möglich die Cluster zu 'verbinden', dies wurde jedoch aus "
 "Gescheindigkeitsgründen nicht implementiert."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1897
+#: ../src/backend/filters/clusterAnalysis.cpp:1900
 msgid ""
 "If you want this, please contact the author, or just use the source to add "
 "this in yourself."
@@ -2773,773 +2811,791 @@ msgstr ""
 "Sollten Sie dies wollen, kontaktieren Sie den Autor oder verwenden Sie den "
 "Sourcecode um es selbst hinzuzufügen."
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1898
+#: ../src/backend/filters/clusterAnalysis.cpp:1901
 msgid "---------------------------------------------------------------------- "
 msgstr "---------------------------------------------------------------------- "
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1908
-#: ../src/backend/filters/spatialAnalysis.cpp:1971
-#: ../src/backend/filters/spatialAnalysis.cpp:2314
-#: ../src/backend/filters/spatialAnalysis.cpp:2605
-#: ../src/backend/filters/spatialAnalysis.cpp:3293
-#: ../src/backend/filters/transform.cpp:991
+#: ../src/backend/filters/clusterAnalysis.cpp:1911
+#: ../src/backend/filters/spatialAnalysis.cpp:2060
+#: ../src/backend/filters/spatialAnalysis.cpp:2489
+#: ../src/backend/filters/spatialAnalysis.cpp:2775
+#: ../src/backend/filters/spatialAnalysis.cpp:3449
+#: ../src/backend/filters/transform.cpp:977
 msgid "Collate"
 msgstr "Abgleichen"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1927
+#: ../src/backend/filters/clusterAnalysis.cpp:1930
 msgid "Build Core"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:1945
-msgid "Classify Core"
-msgstr ""
-
-#: ../src/backend/filters/clusterAnalysis.cpp:2038
-msgid "Build Bulk"
-msgstr ""
-
-#: ../src/backend/filters/clusterAnalysis.cpp:2058
+#: ../src/backend/filters/clusterAnalysis.cpp:1958
 msgid "Core"
 msgstr "Kern"
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2203
+#: ../src/backend/filters/clusterAnalysis.cpp:2097
 msgid "Bulk"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2333
+#: ../src/backend/filters/clusterAnalysis.cpp:2227
 msgid "Erode"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2407
+#: ../src/backend/filters/clusterAnalysis.cpp:2301
 msgid "Re-Collate"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2675
-#: ../src/backend/filters/clusterAnalysis.cpp:2879
+#: ../src/backend/filters/clusterAnalysis.cpp:2358
+msgid "Classify Core"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:2449
+msgid "Build Bulk"
+msgstr ""
+
+#: ../src/backend/filters/clusterAnalysis.cpp:2694
+#: ../src/backend/filters/clusterAnalysis.cpp:2893
 msgid "Cluster Size"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2676
-#: ../src/backend/filters/clusterAnalysis.cpp:2883
+#: ../src/backend/filters/clusterAnalysis.cpp:2695
+#: ../src/backend/filters/clusterAnalysis.cpp:2897
 msgid "Frequency"
 msgstr ""
 
-#: ../src/backend/filters/clusterAnalysis.cpp:2881
+#: ../src/backend/filters/clusterAnalysis.cpp:2895
 msgid "Composition"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:110
+#: ../src/backend/filters/voxelise.cpp:111
 msgid "None (Raw count)"
 msgstr "Keine (Roh count)"
 
-#: ../src/backend/filters/voxelise.cpp:111
+#: ../src/backend/filters/voxelise.cpp:112
 msgid "Volume (Density)"
 msgstr "Volumen (Dichte)"
 
-#: ../src/backend/filters/voxelise.cpp:112
+#: ../src/backend/filters/voxelise.cpp:113
 msgid "All Ions (conc)"
 msgstr "Alle Ionen (Konz)"
 
-#: ../src/backend/filters/voxelise.cpp:113
+#: ../src/backend/filters/voxelise.cpp:114
 msgid "Ratio (Num/Denom)"
 msgstr "Verhältnis (Zähler/Nenner)"
 
-#: ../src/backend/filters/voxelise.cpp:117
+#: ../src/backend/filters/voxelise.cpp:118
 msgid "Point Cloud"
 msgstr "Punktwolke"
 
-#: ../src/backend/filters/voxelise.cpp:118
+#: ../src/backend/filters/voxelise.cpp:119
 msgid "Isosurface"
 msgstr "Isosurface"
 
-#: ../src/backend/filters/voxelise.cpp:119
+#: ../src/backend/filters/voxelise.cpp:120
 msgid "Axial slice"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:124
-msgid "Gaussian (2𝜎)"
-msgstr "Gauss (2𝜎)"
-
-#: ../src/backend/filters/voxelise.cpp:128
-msgid "Zero"
-msgstr "Null"
+#: ../src/backend/filters/voxelise.cpp:125
+msgid "Gaussian (blur)"
+msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:129
-msgid "Bounce"
-msgstr "Bounce"
+#: ../src/backend/filters/voxelise.cpp:126
+msgid "Lapl. of Gauss. (edges)"
+msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:134
+#: ../src/backend/filters/voxelise.cpp:131
 msgid "Linear"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:601
+#: ../src/backend/filters/voxelise.cpp:564
 msgid "Voxel Limits (min,max): ("
 msgstr "Voxel Grenzen (min,max): ("
 
-#: ../src/backend/filters/voxelise.cpp:751
+#: ../src/backend/filters/voxelise.cpp:709
 msgid "Fixed width"
 msgstr "Fixe Breite"
 
-#: ../src/backend/filters/voxelise.cpp:755
+#: ../src/backend/filters/voxelise.cpp:713
 msgid "If true, use fixed size voxels, otherwise use fixed count"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:761
+#: ../src/backend/filters/voxelise.cpp:719
 msgid "Bin width x"
 msgstr "Bin-Breite x"
 
-#: ../src/backend/filters/voxelise.cpp:765
+#: ../src/backend/filters/voxelise.cpp:723
 msgid "Voxel size in X direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:769
+#: ../src/backend/filters/voxelise.cpp:727
 msgid "Bin width y"
 msgstr "Bin-Breite y"
 
-#: ../src/backend/filters/voxelise.cpp:772
+#: ../src/backend/filters/voxelise.cpp:730
 msgid "Voxel size in Y direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:778
+#: ../src/backend/filters/voxelise.cpp:736
 msgid "Bin width z"
 msgstr "Bin-Breite Z"
 
-#: ../src/backend/filters/voxelise.cpp:781
+#: ../src/backend/filters/voxelise.cpp:739
 msgid "Voxel size in Z direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:788
+#: ../src/backend/filters/voxelise.cpp:746
 msgid "Num bins x"
 msgstr "Anzahl Bins x"
 
-#: ../src/backend/filters/voxelise.cpp:792
+#: ../src/backend/filters/voxelise.cpp:750
 msgid "Number of voxels to use in X direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:797
+#: ../src/backend/filters/voxelise.cpp:755
 msgid "Num bins y"
 msgstr "Anzahl Bins y"
 
-#: ../src/backend/filters/voxelise.cpp:800
+#: ../src/backend/filters/voxelise.cpp:758
 msgid "Number of voxels to use in Y direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:806
+#: ../src/backend/filters/voxelise.cpp:764
 msgid "Num bins z"
 msgstr "Anzahl Bins z"
 
-#: ../src/backend/filters/voxelise.cpp:808
+#: ../src/backend/filters/voxelise.cpp:766
 msgid "Number of voxels to use in Z direction"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:838
+#: ../src/backend/filters/voxelise.cpp:796
 msgid "Normalise by"
 msgstr "Normalisieren mit"
 
-#: ../src/backend/filters/voxelise.cpp:841
+#: ../src/backend/filters/voxelise.cpp:799
 msgid "Method to use to normalise scalar value in each voxel"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:844
+#: ../src/backend/filters/voxelise.cpp:802
 msgid "Computation"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:851
+#: ../src/backend/filters/voxelise.cpp:809
 msgid "Numerator"
 msgstr "Zähler"
 
-#: ../src/backend/filters/voxelise.cpp:854
+#: ../src/backend/filters/voxelise.cpp:812
 msgid "Parmeter \"a\" used in fraction (a/b) to get voxel value"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:871
+#: ../src/backend/filters/voxelise.cpp:829
 msgid "Enable this ion for numerator"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:883
+#: ../src/backend/filters/voxelise.cpp:841
 msgid "Denominator"
 msgstr "Nenner"
 
-#: ../src/backend/filters/voxelise.cpp:886
+#: ../src/backend/filters/voxelise.cpp:844
 msgid "Parameter \"b\" used in fraction (a/b) to get voxel value"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:899
+#: ../src/backend/filters/voxelise.cpp:857
 msgid "Enable this ion for denominator contribution"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:920
-#: ../src/backend/filters/voxelise.cpp:955
+#: ../src/backend/filters/voxelise.cpp:879
+#: ../src/backend/filters/voxelise.cpp:910
 msgid "Filtering"
 msgstr "Filtern"
 
-#: ../src/backend/filters/voxelise.cpp:924
+#: ../src/backend/filters/voxelise.cpp:883
 msgid "Smoothing method to use on voxels"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:927
+#: ../src/backend/filters/voxelise.cpp:886
 msgid "Processing"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:933
-msgid "Kernel Bins"
-msgstr "Kernel Bins"
+#: ../src/backend/filters/voxelise.cpp:892
+msgid "Standard Dev"
+msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:937
-msgid "Number of bins in convolution kernel"
+#: ../src/backend/filters/voxelise.cpp:896
+msgid "Filtering Scale"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:948
-msgid "Exterior values"
-msgstr "Exterior values"
+#: ../src/backend/filters/voxelise.cpp:902
+msgid "Kernel Size"
+msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:951
-msgid "Method to use to treat boundaries of voxel data for convolution"
+#: ../src/backend/filters/voxelise.cpp:906
+msgid ""
+"Filter radius, in multiples of std. dev. Larger -> slower, more accurate"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:971
+#: ../src/backend/filters/voxelise.cpp:926
 msgid "Representation"
 msgstr "Representation"
 
-#: ../src/backend/filters/voxelise.cpp:974
+#: ../src/backend/filters/voxelise.cpp:929
 msgid "3D display method"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:985
+#: ../src/backend/filters/voxelise.cpp:940
 msgid "Spot size"
 msgstr "Spot size"
 
-#: ../src/backend/filters/voxelise.cpp:988
+#: ../src/backend/filters/voxelise.cpp:943
 msgid "Size of the spots to use for display"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:993
-#: ../src/backend/filters/voxelise.cpp:1028
+#: ../src/backend/filters/voxelise.cpp:948
+#: ../src/backend/filters/voxelise.cpp:983
+#: ../src/backend/filters/voxelise.cpp:1055
 msgid "Transparency"
 msgstr "Transparenz"
 
-#: ../src/backend/filters/voxelise.cpp:996
+#: ../src/backend/filters/voxelise.cpp:951
 msgid "How \"see through\" each point is (0 - opaque, 1 - invisible)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1005
+#: ../src/backend/filters/voxelise.cpp:960
 msgid "Surf. param."
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1008
+#: ../src/backend/filters/voxelise.cpp:963
 msgid "Isovalue"
 msgstr "Isovalue"
 
-#: ../src/backend/filters/voxelise.cpp:1011
+#: ../src/backend/filters/voxelise.cpp:966
 msgid "Scalar value to show as isosurface"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1016
-#: ../src/backend/filters/voxelise.cpp:1081
-#: ../src/backend/filters/spatialAnalysis.cpp:2019
-#: ../src/backend/filters/spatialAnalysis.cpp:2073
+#: ../src/backend/filters/voxelise.cpp:971
+#: ../src/backend/filters/voxelise.cpp:1036
+#: ../src/backend/filters/spatialAnalysis.cpp:2106
+#: ../src/backend/filters/spatialAnalysis.cpp:2159
 msgid "Surface"
 msgstr "Oberfläche"
 
-#: ../src/backend/filters/voxelise.cpp:1023
+#: ../src/backend/filters/voxelise.cpp:978
 msgid "Colour of isosurface"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1031
+#: ../src/backend/filters/voxelise.cpp:986
+#: ../src/backend/filters/voxelise.cpp:1058
 msgid "How \"see through\" each facet is (0 - opaque, 1 - invisible)"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1042
+#: ../src/backend/filters/voxelise.cpp:997
 msgid "Slice param."
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1050
+#: ../src/backend/filters/voxelise.cpp:1005
 msgid "Slice Axis"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1053
+#: ../src/backend/filters/voxelise.cpp:1008
 msgid "Normal for the planar slice"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1060
+#: ../src/backend/filters/voxelise.cpp:1015
 msgid "Slice Coord"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1063
+#: ../src/backend/filters/voxelise.cpp:1018
 msgid "Fractional coordinate that slice plane passes through"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1068
+#: ../src/backend/filters/voxelise.cpp:1023
 msgid "Interp. Mode"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1076
+#: ../src/backend/filters/voxelise.cpp:1031
 msgid "Interpolation mode for direction normal to slice"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1092
+#: ../src/backend/filters/voxelise.cpp:1047
 msgid "Colour mode"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1095
-#: ../src/backend/filters/ionColour.cpp:265
+#: ../src/backend/filters/voxelise.cpp:1050
+#: ../src/backend/filters/ionColour.cpp:269
 msgid "Colour scheme used to assign points colours by value"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1100
-#: ../src/backend/filters/ionColour.cpp:277
+#: ../src/backend/filters/voxelise.cpp:1063
+#: ../src/backend/filters/ionColour.cpp:281
 msgid "Show Bar"
 msgstr "Zeige Balken"
 
-#: ../src/backend/filters/voxelise.cpp:1107
+#: ../src/backend/filters/voxelise.cpp:1070
 msgid "Auto Bounds"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1108
+#: ../src/backend/filters/voxelise.cpp:1071
 msgid "Auto-compute min/max values in map"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1118
-#: ../src/backend/filters/ionColour.cpp:298
+#: ../src/backend/filters/voxelise.cpp:1081
+#: ../src/backend/filters/ionColour.cpp:302
 msgid "Map start"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1119
-#: ../src/backend/filters/ionColour.cpp:299
+#: ../src/backend/filters/voxelise.cpp:1082
+#: ../src/backend/filters/ionColour.cpp:303
 msgid "Assign points with this value to the first colour in map"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1126
-#: ../src/backend/filters/ionColour.cpp:306
+#: ../src/backend/filters/voxelise.cpp:1089
+#: ../src/backend/filters/ionColour.cpp:310
 msgid "Map end"
 msgstr ""
 
-#: ../src/backend/filters/voxelise.cpp:1127
-#: ../src/backend/filters/ionColour.cpp:307
+#: ../src/backend/filters/voxelise.cpp:1090
+#: ../src/backend/filters/ionColour.cpp:311
 msgid "Assign points with this value to the last colour in map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:261
+#: ../src/backend/filters/ionColour.cpp:265
 msgid "Colour Map"
 msgstr "Farbtabelle"
 
-#: ../src/backend/filters/ionColour.cpp:269
+#: ../src/backend/filters/ionColour.cpp:273
 msgid "Reverse map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:270
+#: ../src/backend/filters/ionColour.cpp:274
 msgid "Reverse the colour scale"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:283
+#: ../src/backend/filters/ionColour.cpp:287
 msgid "Opacity"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:290
+#: ../src/backend/filters/ionColour.cpp:294
 msgid "Num Colours"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:292
+#: ../src/backend/filters/ionColour.cpp:296
 msgid "Number of unique colours to use in colour map"
 msgstr ""
 
-#: ../src/backend/filters/ionColour.cpp:413
+#: ../src/backend/filters/ionColour.cpp:417 ../src/backend/filter.cpp:172
 msgid "Aborted"
 msgstr "Abgebrochen"
 
-#: ../src/backend/filters/compositionProfile.cpp:568
+#: ../src/backend/filters/compositionProfile.cpp:51
+msgid "Cylinder (axial)"
+msgstr ""
+
+#: ../src/backend/filters/compositionProfile.cpp:52
+msgid "Cylinder (radial)"
+msgstr ""
+
+#: ../src/backend/filters/compositionProfile.cpp:591
 msgid "Distance"
 msgstr "Abstand"
 
-#: ../src/backend/filters/compositionProfile.cpp:576
+#: ../src/backend/filters/compositionProfile.cpp:599
 msgid "Fraction"
 msgstr "Anteil"
 
-#: ../src/backend/filters/compositionProfile.cpp:578
+#: ../src/backend/filters/compositionProfile.cpp:601
 msgid "Density (\\frac{\\#}{len^3})"
 msgstr "Dichte (\\frac{\\#}{len^3})"
 
-#: ../src/backend/filters/compositionProfile.cpp:605
+#: ../src/backend/filters/compositionProfile.cpp:628
 msgid "Freq. Profile"
 msgstr "Häufigkeitsprofil"
 
-#: ../src/backend/filters/compositionProfile.cpp:657
+#: ../src/backend/filters/compositionProfile.cpp:680
 msgid "No data remained in profile - cannot display result"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:937
+#: ../src/backend/filters/compositionProfile.cpp:965
 msgid "Primitive type"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:941
+#: ../src/backend/filters/compositionProfile.cpp:969
 msgid "Basic shape to use for profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:953
+#: ../src/backend/filters/compositionProfile.cpp:981
 msgid "Display the 3D composition profile interaction object"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:967
-#: ../src/backend/filters/spatialAnalysis.cpp:777
+#: ../src/backend/filters/compositionProfile.cpp:996
+#: ../src/backend/filters/spatialAnalysis.cpp:849
 msgid "Position for centre of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:975
+#: ../src/backend/filters/compositionProfile.cpp:1004
 msgid "Vector between ends of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:983
+#: ../src/backend/filters/compositionProfile.cpp:1012
 msgid "Prevent length of cylinder changing during interaction"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1023
+#: ../src/backend/filters/compositionProfile.cpp:1055
 msgid "Fixed Bin Num"
 msgstr "Fix. Bin-Anz."
 
-#: ../src/backend/filters/compositionProfile.cpp:1026
+#: ../src/backend/filters/compositionProfile.cpp:1058
 msgid ""
 "If true, use a fixed number of bins for profile, otherwise use fixed step "
 "size"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1032
-#: ../src/backend/filters/spatialAnalysis.cpp:612
-#: ../src/backend/filters/spatialAnalysis.cpp:754
+#: ../src/backend/filters/compositionProfile.cpp:1065
+#: ../src/backend/filters/spatialAnalysis.cpp:690
+#: ../src/backend/filters/spatialAnalysis.cpp:826
 msgid "Num Bins"
 msgstr "Bin-Anz."
 
-#: ../src/backend/filters/compositionProfile.cpp:1037
+#: ../src/backend/filters/compositionProfile.cpp:1070
 msgid "Number of bins to use for profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1042
-#: ../src/backend/filters/spectrumPlot.cpp:386
+#: ../src/backend/filters/compositionProfile.cpp:1076
+#: ../src/backend/filters/spectrumPlot.cpp:572
 msgid "Bin width"
 msgstr "Bin-Breite"
 
-#: ../src/backend/filters/compositionProfile.cpp:1048
+#: ../src/backend/filters/compositionProfile.cpp:1082
 msgid "Size of each bin in profile"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1057
+#: ../src/backend/filters/compositionProfile.cpp:1091
 msgid "Convert bin counts into relative frequencies in each bin"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1061
+#: ../src/backend/filters/compositionProfile.cpp:1095
 msgid "Min. events"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1065
+#: ../src/backend/filters/compositionProfile.cpp:1099
 msgid "Drop data that does not have this many events"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1068
+#: ../src/backend/filters/compositionProfile.cpp:1102
 msgid "Settings"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1090
-#: ../src/backend/filters/spectrumPlot.cpp:445
+#: ../src/backend/filters/compositionProfile.cpp:1124
+#: ../src/backend/filters/spectrumPlot.cpp:661
 msgid "Plot Type"
 msgstr "Plot Type"
 
-#: ../src/backend/filters/compositionProfile.cpp:1093
+#: ../src/backend/filters/compositionProfile.cpp:1127
 msgid "Visual style for plot"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1103
+#: ../src/backend/filters/compositionProfile.cpp:1137
 msgid "Colour of plot"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1119
+#: ../src/backend/filters/compositionProfile.cpp:1153
 msgid "Err. Estimator"
 msgstr "Fehlerschätzer"
 
-#: ../src/backend/filters/compositionProfile.cpp:1122
+#: ../src/backend/filters/compositionProfile.cpp:1156
 msgid "Method of estimating error associated with each bin"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1129
+#: ../src/backend/filters/compositionProfile.cpp:1163
 msgid "Avg. Window"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1132
+#: ../src/backend/filters/compositionProfile.cpp:1166
 msgid "Number of bins to include in moving average filter"
 msgstr ""
 
-#: ../src/backend/filters/compositionProfile.cpp:1136
+#: ../src/backend/filters/compositionProfile.cpp:1170
 msgid "Error analysis"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:101
+#: ../src/backend/filters/spatialAnalysis.cpp:111
 msgid "Local Density"
 msgstr "Lokale Dichte"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:102
+#: ../src/backend/filters/spatialAnalysis.cpp:112
 msgid "Density Filtering"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:103
+#: ../src/backend/filters/spatialAnalysis.cpp:113
 msgid "Radial Distribution"
 msgstr "Radial Distribution"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:104
+#: ../src/backend/filters/spatialAnalysis.cpp:114
 msgid "Axial Distribution"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:105
+#: ../src/backend/filters/spatialAnalysis.cpp:115
 msgid "Binomial Distribution"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:106
+#: ../src/backend/filters/spatialAnalysis.cpp:116
 msgid "Point Em/Replacement"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:110
+#: ../src/backend/filters/spatialAnalysis.cpp:120
 msgid "Neighbour Count"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:544
+#: ../src/backend/filters/spatialAnalysis.cpp:608
 msgid "Spatial analysis algorithm to use"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:567
+#: ../src/backend/filters/spatialAnalysis.cpp:631
 msgid "Stop Mode"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:570
+#: ../src/backend/filters/spatialAnalysis.cpp:634
 msgid "Method to use to terminate algorithm when examining each point"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:577
+#: ../src/backend/filters/spatialAnalysis.cpp:641
 msgid "NN Max"
 msgstr "NN Max"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:580
+#: ../src/backend/filters/spatialAnalysis.cpp:644
 msgid "Maximum number of neighbours to examine"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:586
+#: ../src/backend/filters/spatialAnalysis.cpp:651
 msgid "Normalise bins"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:589
+#: ../src/backend/filters/spatialAnalysis.cpp:654
 msgid ""
 "Normalise counts by binwidth. Needed when comparing NN histograms against "
 "one another"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:596
+#: ../src/backend/filters/spatialAnalysis.cpp:660
+msgid "Show Random"
+msgstr ""
+
+#: ../src/backend/filters/spatialAnalysis.cpp:663
+msgid "Show a fitted (density matched) theoretical distribution"
+msgstr ""
+
+#: ../src/backend/filters/spatialAnalysis.cpp:674
 msgid "Dist Max"
 msgstr "Abst. Max."
 
-#: ../src/backend/filters/spatialAnalysis.cpp:599
+#: ../src/backend/filters/spatialAnalysis.cpp:677
 msgid "Maximum distance from each point for search"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:615
-#: ../src/backend/filters/spatialAnalysis.cpp:757
+#: ../src/backend/filters/spatialAnalysis.cpp:693
+#: ../src/backend/filters/spatialAnalysis.cpp:829
 msgid "Number of bins for output 1D RDF plot"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:621
+#: ../src/backend/filters/spatialAnalysis.cpp:699
 msgid "Surface Remove"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:624
+#: ../src/backend/filters/spatialAnalysis.cpp:702
 msgid ""
 "Exclude surface as part of source to minimise bias in RDF (at cost of "
 "increased noise)"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:631
+#: ../src/backend/filters/spatialAnalysis.cpp:709
 msgid "Remove Dist"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:634
+#: ../src/backend/filters/spatialAnalysis.cpp:712
 msgid "Minimum distance to remove from surface"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:642
-#: ../src/backend/filters/spatialAnalysis.cpp:762
+#: ../src/backend/filters/spatialAnalysis.cpp:720
+#: ../src/backend/filters/spatialAnalysis.cpp:834
 msgid "Plot colour "
 msgstr "Plotfarbe "
 
-#: ../src/backend/filters/spatialAnalysis.cpp:645
-#: ../src/backend/filters/spatialAnalysis.cpp:765
+#: ../src/backend/filters/spatialAnalysis.cpp:723
+#: ../src/backend/filters/spatialAnalysis.cpp:837
 msgid "Colour of output plot"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:649
-#: ../src/backend/filters/spatialAnalysis.cpp:743
-#: ../src/backend/filters/spatialAnalysis.cpp:748
-#: ../src/backend/filters/spatialAnalysis.cpp:797
-#: ../src/backend/filters/spatialAnalysis.cpp:836
+#: ../src/backend/filters/spatialAnalysis.cpp:727
+#: ../src/backend/filters/spatialAnalysis.cpp:815
+#: ../src/backend/filters/spatialAnalysis.cpp:820
+#: ../src/backend/filters/spatialAnalysis.cpp:869
+#: ../src/backend/filters/spatialAnalysis.cpp:908
 msgid "Alg. Params."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:665
+#: ../src/backend/filters/spatialAnalysis.cpp:740
 msgid "Source"
 msgstr "Quelle"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:668
+#: ../src/backend/filters/spatialAnalysis.cpp:743
 msgid "Ions to use for initiating RDF search"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:681
+#: ../src/backend/filters/spatialAnalysis.cpp:756
 msgid "Enable/disable ion as source"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:687
+#: ../src/backend/filters/spatialAnalysis.cpp:762
 msgid "Source Ion"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:700
+#: ../src/backend/filters/spatialAnalysis.cpp:772
 msgid "Enable/disable all ions as target"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:712
+#: ../src/backend/filters/spatialAnalysis.cpp:784
 msgid "Enable/disable this ion as target"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:717
+#: ../src/backend/filters/spatialAnalysis.cpp:789
 msgid "Target Ion"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:727
+#: ../src/backend/filters/spatialAnalysis.cpp:799
 msgid "Cutoff"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:730
+#: ../src/backend/filters/spatialAnalysis.cpp:802
 msgid "Remove points with local density above/below this value"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:736
+#: ../src/backend/filters/spatialAnalysis.cpp:808
 msgid "Retain Upper"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:739
+#: ../src/backend/filters/spatialAnalysis.cpp:811
 msgid "Retain either points with density above (enabled) or below cutoff"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:785
+#: ../src/backend/filters/spatialAnalysis.cpp:857
 msgid "Vector between centre and end of cylinder"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:804
-#: ../src/backend/filters/spatialAnalysis.cpp:3432
-#: ../src/backend/filters/spatialAnalysis.cpp:3491
+#: ../src/backend/filters/spatialAnalysis.cpp:876
+#: ../src/backend/filters/spatialAnalysis.cpp:3587
+#: ../src/backend/filters/spatialAnalysis.cpp:3646
 msgid "Block size"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:807
+#: ../src/backend/filters/spatialAnalysis.cpp:879
 msgid "Number of ions to use per block"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:814
+#: ../src/backend/filters/spatialAnalysis.cpp:886
 msgid "Max Block Aspect"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:817
+#: ../src/backend/filters/spatialAnalysis.cpp:889
 msgid ""
 "Maximum allowable block aspect ratio. Blocks above this aspect are "
 "discarded. Setting too high decreases correlation strength. Too low causes "
 "loss of statistical power."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:828
+#: ../src/backend/filters/spatialAnalysis.cpp:900
 msgid "Extrusion Direction"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:831
+#: ../src/backend/filters/spatialAnalysis.cpp:903
 msgid "Direction in which blocks are extended during construction."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:840
+#: ../src/backend/filters/spatialAnalysis.cpp:912
 msgid "Plot Counts"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:843
+#: ../src/backend/filters/spatialAnalysis.cpp:915
 msgid "Show the counts in the binomial histogram"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:852
+#: ../src/backend/filters/spatialAnalysis.cpp:924
 msgid ""
 "Normalise the counts in the binomial histogram to a probability density "
 "function"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:866
+#: ../src/backend/filters/spatialAnalysis.cpp:938
 msgid "Display Grid"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:875
+#: ../src/backend/filters/spatialAnalysis.cpp:947
 msgid "View Options"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:881
+#: ../src/backend/filters/spatialAnalysis.cpp:953
 msgid "Data File"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:885
+#: ../src/backend/filters/spatialAnalysis.cpp:957
 msgid "Pos file of points to subtract/replace/etc"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:890
+#: ../src/backend/filters/spatialAnalysis.cpp:962
 msgid "Match Tol."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:893
+#: ../src/backend/filters/spatialAnalysis.cpp:965
 msgid "Tolerance to allow for matching"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:909
+#: ../src/backend/filters/spatialAnalysis.cpp:981
 msgid "Replacment condition"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:915
+#: ../src/backend/filters/spatialAnalysis.cpp:987
 msgid "Replace value"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:918
+#: ../src/backend/filters/spatialAnalysis.cpp:990
 msgid "Use value data from file when replacing ions"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:923
+#: ../src/backend/filters/spatialAnalysis.cpp:995
 msgid "Replacement"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2005
-#: ../src/backend/filters/spatialAnalysis.cpp:2059
-#: ../src/backend/filters/spatialAnalysis.cpp:2320
-#: ../src/backend/filters/spatialAnalysis.cpp:2611
-#: ../src/backend/filters/spatialAnalysis.cpp:3136
+#: ../src/backend/filters/spatialAnalysis.cpp:2092
+#: ../src/backend/filters/spatialAnalysis.cpp:2145
+#: ../src/backend/filters/spatialAnalysis.cpp:2495
+#: ../src/backend/filters/spatialAnalysis.cpp:2781
+#: ../src/backend/filters/spatialAnalysis.cpp:3300
 msgid "Build"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2113
-#: ../src/backend/filters/spatialAnalysis.cpp:2347
-#: ../src/backend/filters/spatialAnalysis.cpp:2638
+#: ../src/backend/filters/spatialAnalysis.cpp:2199
+#: ../src/backend/filters/spatialAnalysis.cpp:2518
+#: ../src/backend/filters/spatialAnalysis.cpp:2805
 msgid "Analyse"
 msgstr "Analyse"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2195
-#: ../src/backend/filters/spatialAnalysis.cpp:2258
+#: ../src/backend/filters/spatialAnalysis.cpp:2278
+#: ../src/backend/filters/spatialAnalysis.cpp:2368
+#: ../src/backend/filters/spatialAnalysis.cpp:2434
 msgid "Radial Distance"
 msgstr "Radialer Abstand"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2197
+#: ../src/backend/filters/spatialAnalysis.cpp:2280
+#: ../src/backend/filters/spatialAnalysis.cpp:2373
 msgid "Count/Distance"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2202
+#: ../src/backend/filters/spatialAnalysis.cpp:2285
+#: ../src/backend/filters/spatialAnalysis.cpp:2378
 msgid "NN Freq."
 msgstr "NN Freq."
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2249
+#: ../src/backend/filters/spatialAnalysis.cpp:2425
 msgid "Warning, "
 msgstr "Warnung, "
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2250
+#: ../src/backend/filters/spatialAnalysis.cpp:2426
 msgid ""
 " points were unable to find neighbour points that exceeded the search "
 "radius, and thus terminated prematurely"
@@ -3547,760 +3603,778 @@ msgstr ""
 " Punkte konnten keine Nachbapunkte die den Suchradius überschritten finden "
 "und beendeten vorzeitig."
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2260
+#: ../src/backend/filters/spatialAnalysis.cpp:2436
 msgid " RDF"
 msgstr " RDF"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2543
-#: ../src/backend/filters/spatialAnalysis.cpp:2844
+#: ../src/backend/filters/spatialAnalysis.cpp:2714
+#: ../src/backend/filters/spatialAnalysis.cpp:3011
 msgid "Number Density (\\#/Vol^3)"
 msgstr "Number Density (\\#/Vol^3)"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2565
-#: ../src/backend/filters/spatialAnalysis.cpp:2864
+#: ../src/backend/filters/spatialAnalysis.cpp:2736
+#: ../src/backend/filters/spatialAnalysis.cpp:3031
 msgid "Warning,"
 msgstr "Warnung,"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2566
-#: ../src/backend/filters/spatialAnalysis.cpp:2865
+#: ../src/backend/filters/spatialAnalysis.cpp:2737
+#: ../src/backend/filters/spatialAnalysis.cpp:3032
 msgid " points were un-analysable. These have been dropped"
 msgstr " points were un-analysable. These have been dropped"
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2588
-#: ../src/backend/filters/spatialAnalysis.cpp:2887
+#: ../src/backend/filters/spatialAnalysis.cpp:2759
+#: ../src/backend/filters/spatialAnalysis.cpp:3054
 msgid "And so on..."
 msgstr "Und so weiter..."
 
-#: ../src/backend/filters/spatialAnalysis.cpp:2970
+#: ../src/backend/filters/spatialAnalysis.cpp:3136
 msgid "Extract"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3045
+#: ../src/backend/filters/spatialAnalysis.cpp:3209
 msgid "Reduce"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3155
+#: ../src/backend/filters/spatialAnalysis.cpp:3318
 msgid "Compute"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3200
+#: ../src/backend/filters/spatialAnalysis.cpp:3364
 msgid "Insufficient points to complete analysis"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3223
+#: ../src/backend/filters/spatialAnalysis.cpp:3387
 msgid "Axial Distance"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3225
+#: ../src/backend/filters/spatialAnalysis.cpp:3389
 msgid " 1D Dist. Func."
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3302
+#: ../src/backend/filters/spatialAnalysis.cpp:3458
 msgid "Binomial"
 msgstr ""
 
-#: ../src/backend/filters/spatialAnalysis.cpp:3434
-#: ../src/backend/filters/spatialAnalysis.cpp:3493
+#: ../src/backend/filters/spatialAnalysis.cpp:3589
+#: ../src/backend/filters/spatialAnalysis.cpp:3648
 msgid "Rel. Frequency"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:78
+#: ../src/backend/filters/transform.cpp:81
 msgid "Translate"
 msgstr "Translate"
 
-#: ../src/backend/filters/transform.cpp:79
+#: ../src/backend/filters/transform.cpp:82
 msgid "Scale (isotropic)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:80
+#: ../src/backend/filters/transform.cpp:83
 msgid "Scale (anisotropic)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:81
+#: ../src/backend/filters/transform.cpp:84
 msgid "Rotate"
 msgstr "Rotieren"
 
-#: ../src/backend/filters/transform.cpp:82
+#: ../src/backend/filters/transform.cpp:85
 msgid "Value Shuffle"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:83
+#: ../src/backend/filters/transform.cpp:86
 msgid "Spatial Noise"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:84
+#: ../src/backend/filters/transform.cpp:87
 msgid "Translate Value"
 msgstr "Translate Wert"
 
-#: ../src/backend/filters/transform.cpp:88
+#: ../src/backend/filters/transform.cpp:91
 msgid "Specify"
 msgstr "Angeben"
 
-#: ../src/backend/filters/transform.cpp:89
+#: ../src/backend/filters/transform.cpp:92
 msgid "Boundbox Centre"
 msgstr "Boundbox Zentrum"
 
-#: ../src/backend/filters/transform.cpp:90
+#: ../src/backend/filters/transform.cpp:93
 msgid "Mass Centre"
 msgstr "Massen-Zentrum"
 
-#: ../src/backend/filters/transform.cpp:1010
+#: ../src/backend/filters/transform.cpp:996
 msgid "Mass-to-Charge (amu/e)"
 msgstr "Masse-zu-Ladung (amu/e)"
 
-#: ../src/backend/filters/transform.cpp:1064
+#: ../src/backend/filters/transform.cpp:1050
 msgid "Shuffle"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1088
+#: ../src/backend/filters/transform.cpp:1074
 msgid "Splice"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1136
+#: ../src/backend/filters/transform.cpp:1122
 msgid "Algorithm to use to transform point data"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1153
+#: ../src/backend/filters/transform.cpp:1139
 msgid "Origin mode"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1156
+#: ../src/backend/filters/transform.cpp:1142
 msgid "Select how transform origin is computed"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1161
+#: ../src/backend/filters/transform.cpp:1147
 msgid "Show marker"
 msgstr "Zeige Markierung"
 
-#: ../src/backend/filters/transform.cpp:1165
+#: ../src/backend/filters/transform.cpp:1151
 msgid "Display an interactive object to set transform origin"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1167
+#: ../src/backend/filters/transform.cpp:1153
 msgid "Display a small marker to denote transform origin"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1183
+#: ../src/backend/filters/transform.cpp:1169
 msgid "Translation"
 msgstr "Translation"
 
-#: ../src/backend/filters/transform.cpp:1186
+#: ../src/backend/filters/transform.cpp:1172
 msgid "Translation vector for transform"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1198
+#: ../src/backend/filters/transform.cpp:1184
 msgid "Offset"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1202
+#: ../src/backend/filters/transform.cpp:1188
 msgid "Scalar to use to offset each point's associated value"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1219
-#: ../src/backend/filters/transform.cpp:1246
+#: ../src/backend/filters/transform.cpp:1205
+#: ../src/backend/filters/transform.cpp:1232
 msgid "Origin of scale trasnform"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1226
-#: ../src/backend/filters/transform.cpp:1253
+#: ../src/backend/filters/transform.cpp:1212
+#: ../src/backend/filters/transform.cpp:1239
 msgid "Scale Fact."
 msgstr "Skalierungsfaktor"
 
-#: ../src/backend/filters/transform.cpp:1229
-#: ../src/backend/filters/transform.cpp:1256
+#: ../src/backend/filters/transform.cpp:1215
+#: ../src/backend/filters/transform.cpp:1242
 msgid "Enlargement factor for scaling around origin"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1272
+#: ../src/backend/filters/transform.cpp:1258
 msgid "Origin of rotation"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1280
+#: ../src/backend/filters/transform.cpp:1266
 msgid "Axis around which to revolve"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1285
+#: ../src/backend/filters/transform.cpp:1271
 msgid "Angle (deg)"
 msgstr "Winkel (deg)"
 
-#: ../src/backend/filters/transform.cpp:1288
+#: ../src/backend/filters/transform.cpp:1274
 msgid "Angle to perform rotation (ACW, as viewed from axis towards origin)"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1305
+#: ../src/backend/filters/transform.cpp:1291
 msgid "Noise Type"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1308
+#: ../src/backend/filters/transform.cpp:1294
 msgid "Method to use to degrade point data"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1315
+#: ../src/backend/filters/transform.cpp:1301
 msgid "Noise level"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1317
+#: ../src/backend/filters/transform.cpp:1303
 msgid "Standard dev."
 msgstr "Standardabweichung"
 
-#: ../src/backend/filters/transform.cpp:1325
+#: ../src/backend/filters/transform.cpp:1311
 msgid "Amplitude of noise"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1337
+#: ../src/backend/filters/transform.cpp:1323
 msgid "Transform Params"
 msgstr "Transformationsparameter"
 
-#: ../src/backend/filters/transform.cpp:1675
+#: ../src/backend/filters/transform.cpp:1660
 msgid "White"
 msgstr ""
 
-#: ../src/backend/filters/transform.cpp:1677
+#: ../src/backend/filters/transform.cpp:1662
 msgid "Gaussian"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:55
+#: ../src/backend/filters/boundingBox.cpp:60
 msgid "Box only"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:56
+#: ../src/backend/filters/boundingBox.cpp:61
 msgid "Tick"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:57
+#: ../src/backend/filters/boundingBox.cpp:62
 msgid "Dimension"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:525
+#: ../src/backend/filters/boundingBox.cpp:531
 msgid "If true, show box, otherwise hide box"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:538
+#: ../src/backend/filters/boundingBox.cpp:544
 msgid "Style"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:541
+#: ../src/backend/filters/boundingBox.cpp:547
 msgid "Box display mode"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:544
+#: ../src/backend/filters/boundingBox.cpp:550
 msgid "Display mode"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:553
+#: ../src/backend/filters/boundingBox.cpp:559
 msgid "Fixed Tick Num"
 msgstr "Fixed Tick Num"
 
-#: ../src/backend/filters/boundingBox.cpp:557
+#: ../src/backend/filters/boundingBox.cpp:563
 msgid ""
 "If true, evenly use specified number of ticks. Otherwise, use distance to "
 "determine tick count"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:565
+#: ../src/backend/filters/boundingBox.cpp:571
 msgid "Num X"
 msgstr "Num X"
 
-#: ../src/backend/filters/boundingBox.cpp:568
+#: ../src/backend/filters/boundingBox.cpp:574
 msgid "Tick count in X direction"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:573
+#: ../src/backend/filters/boundingBox.cpp:579
 msgid "Num Y"
 msgstr "Num Y"
 
-#: ../src/backend/filters/boundingBox.cpp:576
+#: ../src/backend/filters/boundingBox.cpp:582
 msgid "Tick count in Y direction"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:581
+#: ../src/backend/filters/boundingBox.cpp:587
 msgid "Num Z"
 msgstr "Num Z"
 
-#: ../src/backend/filters/boundingBox.cpp:584
+#: ../src/backend/filters/boundingBox.cpp:590
 msgid "Tick count in Z direction"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:590
+#: ../src/backend/filters/boundingBox.cpp:596
 msgid "Spacing X"
 msgstr "X-Abstand"
 
-#: ../src/backend/filters/boundingBox.cpp:594
+#: ../src/backend/filters/boundingBox.cpp:600
 msgid "Distance between ticks on X axis"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:598
+#: ../src/backend/filters/boundingBox.cpp:604
 msgid "Spacing Y"
 msgstr "Y-Abstand"
 
-#: ../src/backend/filters/boundingBox.cpp:602
+#: ../src/backend/filters/boundingBox.cpp:608
 msgid "Distance between ticks on Y axis"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:606
+#: ../src/backend/filters/boundingBox.cpp:612
 msgid "Spacing Z"
 msgstr "Z-Abstand"
 
-#: ../src/backend/filters/boundingBox.cpp:610
+#: ../src/backend/filters/boundingBox.cpp:616
 msgid "Distance between ticks on Z axis"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:613
+#: ../src/backend/filters/boundingBox.cpp:619
 msgid "Tick marks"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:620
+#: ../src/backend/filters/boundingBox.cpp:626
 msgid "Box Colour"
 msgstr "Box Farbe"
 
-#: ../src/backend/filters/boundingBox.cpp:624
+#: ../src/backend/filters/boundingBox.cpp:630
 msgid "Colour of the bounding box"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:629
+#: ../src/backend/filters/boundingBox.cpp:635
 msgid "Line thickness"
 msgstr "Linienbreite"
 
-#: ../src/backend/filters/boundingBox.cpp:633
+#: ../src/backend/filters/boundingBox.cpp:639
 msgid "Thickness of the lines used to draw the box"
 msgstr ""
 
-#: ../src/backend/filters/boundingBox.cpp:641
-#: ../src/backend/filters/annotation.cpp:843
+#: ../src/backend/filters/boundingBox.cpp:647
+#: ../src/backend/filters/annotation.cpp:848
 msgid "Font Size"
 msgstr "Schriftgröße"
 
-#: ../src/backend/filters/boundingBox.cpp:644
+#: ../src/backend/filters/boundingBox.cpp:650
 msgid "Relative size for text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:68
+#: ../src/backend/filters/annotation.cpp:73
 msgid "Arrow"
 msgstr "Pfeil"
 
-#: ../src/backend/filters/annotation.cpp:69
+#: ../src/backend/filters/annotation.cpp:74
 msgid "Text"
 msgstr "Text"
 
-#: ../src/backend/filters/annotation.cpp:70
+#: ../src/backend/filters/annotation.cpp:75
 msgid "Arrow+Text"
 msgstr "Pfeil+Text"
 
-#: ../src/backend/filters/annotation.cpp:71
+#: ../src/backend/filters/annotation.cpp:76
 msgid "Angle"
 msgstr "Winkel"
 
-#: ../src/backend/filters/annotation.cpp:72
+#: ../src/backend/filters/annotation.cpp:77
 msgid "Ruler"
 msgstr "Lineal"
 
-#: ../src/backend/filters/annotation.cpp:519
+#: ../src/backend/filters/annotation.cpp:524
 msgid "Enable"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:522
+#: ../src/backend/filters/annotation.cpp:527
 msgid "Enable/disable annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:543
+#: ../src/backend/filters/annotation.cpp:548
 msgid "Type or style of annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:559
-#: ../src/backend/filters/annotation.cpp:663
+#: ../src/backend/filters/annotation.cpp:564
+#: ../src/backend/filters/annotation.cpp:668
 msgid "Text of annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:567
+#: ../src/backend/filters/annotation.cpp:572
 msgid "Position of annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:571
-#: ../src/backend/filters/annotation.cpp:678
-#: ../src/backend/filters/annotation.cpp:737
-#: ../src/backend/filters/annotation.cpp:826
+#: ../src/backend/filters/annotation.cpp:576
+#: ../src/backend/filters/annotation.cpp:683
+#: ../src/backend/filters/annotation.cpp:742
+#: ../src/backend/filters/annotation.cpp:831
 msgid "Up dir"
 msgstr "Up dir"
 
-#: ../src/backend/filters/annotation.cpp:575
-#: ../src/backend/filters/annotation.cpp:830
+#: ../src/backend/filters/annotation.cpp:580
+#: ../src/backend/filters/annotation.cpp:835
 msgid "Vector for up direction of annotation text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:579
-#: ../src/backend/filters/annotation.cpp:685
-#: ../src/backend/filters/annotation.cpp:729
-#: ../src/backend/filters/annotation.cpp:834
+#: ../src/backend/filters/annotation.cpp:584
+#: ../src/backend/filters/annotation.cpp:690
+#: ../src/backend/filters/annotation.cpp:734
+#: ../src/backend/filters/annotation.cpp:839
 msgid "Across dir"
 msgstr "Across dir"
 
-#: ../src/backend/filters/annotation.cpp:583
-#: ../src/backend/filters/annotation.cpp:838
+#: ../src/backend/filters/annotation.cpp:588
+#: ../src/backend/filters/annotation.cpp:843
 msgid "Reading direction for annotation"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:588
-#: ../src/backend/filters/annotation.cpp:670
-#: ../src/backend/filters/annotation.cpp:764
+#: ../src/backend/filters/annotation.cpp:593
+#: ../src/backend/filters/annotation.cpp:675
+#: ../src/backend/filters/annotation.cpp:769
 msgid "Text size"
 msgstr "Textgröße"
 
-#: ../src/backend/filters/annotation.cpp:592
-#: ../src/backend/filters/annotation.cpp:674
-#: ../src/backend/filters/annotation.cpp:846
+#: ../src/backend/filters/annotation.cpp:597
+#: ../src/backend/filters/annotation.cpp:679
+#: ../src/backend/filters/annotation.cpp:851
 msgid "Relative size of annotation text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:604
-#: ../src/backend/filters/annotation.cpp:645
+#: ../src/backend/filters/annotation.cpp:609
+#: ../src/backend/filters/annotation.cpp:650
 msgid "3D position for tail of arrow"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:612
-#: ../src/backend/filters/annotation.cpp:654
+#: ../src/backend/filters/annotation.cpp:617
+#: ../src/backend/filters/annotation.cpp:659
 msgid "3D Position to which arrow points"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:615
-#: ../src/backend/filters/annotation.cpp:725
+#: ../src/backend/filters/annotation.cpp:620
+#: ../src/backend/filters/annotation.cpp:730
 msgid "Positioning"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:620
-#: ../src/backend/filters/annotation.cpp:692
+#: ../src/backend/filters/annotation.cpp:625
+#: ../src/backend/filters/annotation.cpp:697
 msgid "Tip radius"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:624
+#: ../src/backend/filters/annotation.cpp:629
 msgid "Size of the arrow head"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:628
+#: ../src/backend/filters/annotation.cpp:633
 msgid "Line size"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:632
+#: ../src/backend/filters/annotation.cpp:637
 msgid "Thickness of line used to draw arrow stem"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:666
+#: ../src/backend/filters/annotation.cpp:671
 msgid "Options"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:702
+#: ../src/backend/filters/annotation.cpp:707
 msgid "Position A"
 msgstr "Position A"
 
-#: ../src/backend/filters/annotation.cpp:706
+#: ../src/backend/filters/annotation.cpp:711
 msgid "Location of first non-central vertex"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:710
+#: ../src/backend/filters/annotation.cpp:715
 msgid "Origin "
 msgstr "Ursprung "
 
-#: ../src/backend/filters/annotation.cpp:714
+#: ../src/backend/filters/annotation.cpp:719
 msgid "Location of central vertex"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:718
+#: ../src/backend/filters/annotation.cpp:723
 msgid "Position B"
 msgstr "Position B"
 
-#: ../src/backend/filters/annotation.cpp:722
+#: ../src/backend/filters/annotation.cpp:727
 msgid "Location of second non-central vertex"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:733
+#: ../src/backend/filters/annotation.cpp:738
 msgid "Reading direction for angle text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:738
+#: ../src/backend/filters/annotation.cpp:743
 msgid "Vector for up direction of angle text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:746
+#: ../src/backend/filters/annotation.cpp:751
 msgid "Reflexive"
 msgstr "Reflexive"
 
-#: ../src/backend/filters/annotation.cpp:749
+#: ../src/backend/filters/annotation.cpp:754
 msgid "Measure interor (enabled) or exterior angle (disabled)"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:754
+#: ../src/backend/filters/annotation.cpp:759
 msgid "Show Angle"
 msgstr "Zeige Winkel"
 
-#: ../src/backend/filters/annotation.cpp:758
+#: ../src/backend/filters/annotation.cpp:763
 msgid "Display angle text (when enabled)"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:768
+#: ../src/backend/filters/annotation.cpp:773
 msgid "Size of angle text"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:786
+#: ../src/backend/filters/annotation.cpp:791
 msgid "Digit format"
 msgstr "Zahlenformat"
 
-#: ../src/backend/filters/annotation.cpp:790
+#: ../src/backend/filters/annotation.cpp:795
 msgid ""
 "Format of angle text; # for numeral position, '.' for separator, eg ##.## "
 "gives 12.34"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:796
-#: ../src/backend/filters/annotation.cpp:881
+#: ../src/backend/filters/annotation.cpp:801
+#: ../src/backend/filters/annotation.cpp:886
 msgid "Sphere size"
 msgstr "Kugelgröße"
 
-#: ../src/backend/filters/annotation.cpp:800
-#: ../src/backend/filters/annotation.cpp:885
+#: ../src/backend/filters/annotation.cpp:805
+#: ../src/backend/filters/annotation.cpp:890
 msgid "Marker sphere size for manipulating tool"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:814
+#: ../src/backend/filters/annotation.cpp:819
 msgid "Ruler beginning 3D location"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:822
+#: ../src/backend/filters/annotation.cpp:827
 msgid "Ruler finish 3D location"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:852
+#: ../src/backend/filters/annotation.cpp:857
 msgid "Fixed ticks"
 msgstr "Fixe Marker"
 
-#: ../src/backend/filters/annotation.cpp:855
+#: ../src/backend/filters/annotation.cpp:860
 msgid ""
 "Use fixed (enabled) number of text markers, or one every fixed distance "
 "(disabled)"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:862
+#: ../src/backend/filters/annotation.cpp:867
 msgid "Num Ticks"
 msgstr "Anzahl Marker"
 
-#: ../src/backend/filters/annotation.cpp:865
+#: ../src/backend/filters/annotation.cpp:870
 msgid "Number of tick marks along ruler"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:872
+#: ../src/backend/filters/annotation.cpp:877
 msgid "Tick Spacing"
 msgstr "Markerabstand"
 
-#: ../src/backend/filters/annotation.cpp:875
+#: ../src/backend/filters/annotation.cpp:880
 msgid "Distance between tick marks along ruler"
 msgstr ""
 
-#: ../src/backend/filters/annotation.cpp:899
+#: ../src/backend/filters/annotation.cpp:904
 msgid "Colour for ruler and ticks"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:445
+#: ../src/backend/filters/ionDownsample.cpp:191
+msgid "Sampling"
+msgstr ""
+
+#: ../src/backend/filters/ionDownsample.cpp:447
 msgid "By Count"
 msgstr "Nach Anzahl"
 
-#: ../src/backend/filters/ionDownsample.cpp:448
+#: ../src/backend/filters/ionDownsample.cpp:450
 msgid "Sample up to a fixed number of ions"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:454
+#: ../src/backend/filters/ionDownsample.cpp:456
 msgid "Per Species"
 msgstr "Nach Spezies"
 
-#: ../src/backend/filters/ionDownsample.cpp:458
+#: ../src/backend/filters/ionDownsample.cpp:460
 msgid "Use species specific (from ranging) sampling values"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:487
+#: ../src/backend/filters/ionDownsample.cpp:489
 msgid "Sampling value for species"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:495
-#: ../src/backend/filters/ionDownsample.cpp:519
+#: ../src/backend/filters/ionDownsample.cpp:497
+#: ../src/backend/filters/ionDownsample.cpp:521
 msgid "Sampling rates"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:503
+#: ../src/backend/filters/ionDownsample.cpp:505
 msgid "Output Count"
 msgstr "Ausgabe Anzahl"
 
-#: ../src/backend/filters/ionDownsample.cpp:506
+#: ../src/backend/filters/ionDownsample.cpp:508
 msgid "Sample up to this value of points"
 msgstr ""
 
-#: ../src/backend/filters/ionDownsample.cpp:511
+#: ../src/backend/filters/ionDownsample.cpp:513
 msgid "Out Fraction"
 msgstr "Ausgabe Anteil"
 
-#: ../src/backend/filters/ionDownsample.cpp:515
+#: ../src/backend/filters/ionDownsample.cpp:517
 msgid "Sample this fraction of points"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:30
+#: ../src/backend/filters/ionInfo.cpp:37
 msgid "Rectilinear"
 msgstr "Geradlinig"
 
-#: ../src/backend/filters/ionInfo.cpp:31
+#: ../src/backend/filters/ionInfo.cpp:38
 msgid "Convex hull"
 msgstr "Konvexe Hülle"
 
-#: ../src/backend/filters/ionInfo.cpp:193
+#: ../src/backend/filters/ionInfo.cpp:200
 msgid "No ions"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:231
+#: ../src/backend/filters/ionInfo.cpp:226
+#: ../src/backend/filters/spectrumPlot.cpp:432
+msgid ""
+"Background fit failed - input data was considered ill formed (gauss-test)"
+msgstr ""
+
+#: ../src/backend/filters/ionInfo.cpp:227
+msgid "Following data has not been corrected"
+msgstr ""
+
+#: ../src/backend/filters/ionInfo.cpp:279
 msgid "--Counts--"
 msgstr "- Anzahl -"
 
-#: ../src/backend/filters/ionInfo.cpp:241
+#: ../src/backend/filters/ionInfo.cpp:289
 msgid "Total Ranged\t"
 msgstr "Gesamt ranged\t"
 
-#: ../src/backend/filters/ionInfo.cpp:246
+#: ../src/backend/filters/ionInfo.cpp:294
 msgid "Total (incl. unranged)\t"
 msgstr "Total (inkl. nicht geranged)"
 
-#: ../src/backend/filters/ionInfo.cpp:259
+#: ../src/backend/filters/ionInfo.cpp:307
 msgid "n/a"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:269
+#: ../src/backend/filters/ionInfo.cpp:317
 msgid "Unranged"
 msgstr "Nicht Geranged"
 
-#: ../src/backend/filters/ionInfo.cpp:282
+#: ../src/backend/filters/ionInfo.cpp:330
 msgid "Number of points : "
 msgstr "Anzahl der Punkte: "
 
-#: ../src/backend/filters/ionInfo.cpp:311
+#: ../src/backend/filters/ionInfo.cpp:359
 msgid "Rectilinear Bounds : "
 msgstr "Geradlinige Grenzen:"
 
-#: ../src/backend/filters/ionInfo.cpp:316
+#: ../src/backend/filters/ionInfo.cpp:364
 msgid "Volume (len^3): "
 msgstr "Volumen (Läng.^3)"
 
-#: ../src/backend/filters/ionInfo.cpp:332
+#: ../src/backend/filters/ionInfo.cpp:381
 msgid "Convex Volume (len^3): "
 msgstr "Konvexes Volumen (Läng.^3)"
 
-#: ../src/backend/filters/ionInfo.cpp:334
+#: ../src/backend/filters/ionInfo.cpp:384
 msgid "Unable to compute volume"
 msgstr "Kann Volumen nicht berechnen"
 
-#: ../src/backend/filters/ionInfo.cpp:363
+#: ../src/backend/filters/ionInfo.cpp:413
 msgid "Ranged Density (pts/vol):"
 msgstr "Ranged Dichte (pts / vol):"
 
-#: ../src/backend/filters/ionInfo.cpp:368
+#: ../src/backend/filters/ionInfo.cpp:418
 msgid "Total Density (pts/vol):"
 msgstr "Gesamtdichte (pts / vol):"
 
-#: ../src/backend/filters/ionInfo.cpp:396
+#: ../src/backend/filters/ionInfo.cpp:445
 msgid "Compositions"
 msgstr "Zusammensetzungen"
 
-#: ../src/backend/filters/ionInfo.cpp:397
+#: ../src/backend/filters/ionInfo.cpp:446
 msgid "Display compositional data for points in console"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:401
+#: ../src/backend/filters/ionInfo.cpp:450
 msgid "Counts"
 msgstr "Anzahl"
 
-#: ../src/backend/filters/ionInfo.cpp:402
+#: ../src/backend/filters/ionInfo.cpp:451
 msgid "Display count data for points in console"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:416
-msgid "Normalise count data"
+#: ../src/backend/filters/ionInfo.cpp:458
+msgid "Ion data"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:421
-msgid "Ion data"
+#: ../src/backend/filters/ionInfo.cpp:466
+msgid "Normalise count data"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:426
+#: ../src/backend/filters/ionInfo.cpp:530
 msgid "Volume"
 msgstr "Volumen"
 
-#: ../src/backend/filters/ionInfo.cpp:429
+#: ../src/backend/filters/ionInfo.cpp:533
 msgid "Compute volume for point data"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:444
+#: ../src/backend/filters/ionInfo.cpp:549
 msgid "Select volume counting technique"
 msgstr ""
 
-#: ../src/backend/filters/ionInfo.cpp:457
+#: ../src/backend/filters/ionInfo.cpp:562
 msgid "Volume data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:56
+#: ../src/backend/filters/dataLoad.cpp:61
 msgid "Auto"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:57
+#: ../src/backend/filters/dataLoad.cpp:62
 msgid "Little"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:58
+#: ../src/backend/filters/dataLoad.cpp:63
 msgid "Big"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:61
+#: ../src/backend/filters/dataLoad.cpp:66
 msgid "POS Data"
 msgstr "Pos-Daten"
 
-#: ../src/backend/filters/dataLoad.cpp:62
+#: ../src/backend/filters/dataLoad.cpp:67
 msgid "Text Data"
 msgstr "Text-Daten"
 
-#: ../src/backend/filters/dataLoad.cpp:63
+#: ../src/backend/filters/dataLoad.cpp:68
 msgid "ATO Data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:238
+#: ../src/backend/filters/dataLoad.cpp:248
 msgid " does not exist"
 msgstr " existiert nicht"
 
-#: ../src/backend/filters/dataLoad.cpp:276
-#: ../src/backend/filters/dataLoad.cpp:289
-#: ../src/backend/filters/dataLoad.cpp:332
-#: ../src/backend/filters/dataLoad.cpp:343
-#: ../src/backend/filters/dataLoad.cpp:404
+#: ../src/backend/filters/dataLoad.cpp:275
+msgid "Reading File"
+msgstr ""
+
+#: ../src/backend/filters/dataLoad.cpp:290
+#: ../src/backend/filters/dataLoad.cpp:303
+#: ../src/backend/filters/dataLoad.cpp:347
+#: ../src/backend/filters/dataLoad.cpp:358
+#: ../src/backend/filters/dataLoad.cpp:418
 msgid "Error loading file: "
 msgstr "Fehler beim Laden der Datei: "
 
-#: ../src/backend/filters/dataLoad.cpp:306
+#: ../src/backend/filters/dataLoad.cpp:320
 msgid "Sampling is active, loaded "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:307
+#: ../src/backend/filters/dataLoad.cpp:321
 msgid " available."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:315
+#: ../src/backend/filters/dataLoad.cpp:329
 msgid "Loaded entire dataset, "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:315
-#: ../src/backend/filters/dataLoad.cpp:414
+#: ../src/backend/filters/dataLoad.cpp:329
+#: ../src/backend/filters/dataLoad.cpp:428
 msgid " points."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:358
+#: ../src/backend/filters/dataLoad.cpp:373
 msgid ""
 "Data file contained incorrect number of columns -- should be 3 or 4, was "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:413
+#: ../src/backend/filters/dataLoad.cpp:427
 msgid "Loaded dataset, "
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:445
+#: ../src/backend/filters/dataLoad.cpp:460
 msgid ""
 "Warning:One or more bounds of the loaded data approaches the limits of "
 "numerical stability for the internal data type(magnitude too large). "
@@ -4310,296 +4384,344 @@ msgstr ""
 "der numerischen Stabilität des internen Datentyps (Größenordnung zu groß). "
 "Erwägen Sie die Daten vor dem Laden zu skalieren. "
 
-#: ../src/backend/filters/dataLoad.cpp:469
-#: ../src/backend/filters/dataLoad.cpp:490
-#: ../src/backend/filters/rangeFile.cpp:569
-#: ../src/backend/filters/rangeFile.cpp:589
+#: ../src/backend/filters/dataLoad.cpp:484
+#: ../src/backend/filters/dataLoad.cpp:505
+#: ../src/backend/filters/rangeFile.cpp:558
+#: ../src/backend/filters/rangeFile.cpp:578
 msgid "File"
 msgstr "Datei"
 
-#: ../src/backend/filters/dataLoad.cpp:470
+#: ../src/backend/filters/dataLoad.cpp:485
 msgid "File from which to load data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:473
+#: ../src/backend/filters/dataLoad.cpp:488
 msgid ""
 "Readable files (*.xml, *.pos, *.txt,*.csv, *.ato)|*.xml;*.pos;*.txt;*.csv;*."
 "ato|All Files|*"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:483
+#: ../src/backend/filters/dataLoad.cpp:498
 msgid "File type"
 msgstr "Dateityp"
 
-#: ../src/backend/filters/dataLoad.cpp:485
+#: ../src/backend/filters/dataLoad.cpp:500
 msgid "Type of file to be loaded"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:500
+#: ../src/backend/filters/dataLoad.cpp:515
 msgid "Entries per point"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:501
+#: ../src/backend/filters/dataLoad.cpp:516
 msgid "Number of decimal values in file per 3D point (normally 4)"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:520
+#: ../src/backend/filters/dataLoad.cpp:535
 msgid "File \"Endianness\""
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:521
+#: ../src/backend/filters/dataLoad.cpp:536
 msgid "On-disk data storage format. If file won't load, just try each"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:546
+#: ../src/backend/filters/dataLoad.cpp:561
 msgid "Relative offset of each entry in file for point's X position"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:554
+#: ../src/backend/filters/dataLoad.cpp:569
 msgid "Relative offset of each entry in file for point's Y position"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:562
+#: ../src/backend/filters/dataLoad.cpp:577
 msgid "Relative offset of each entry in file for point's Z position"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:570
+#: ../src/backend/filters/dataLoad.cpp:585
 msgid ""
 "Relative offset of each entry in file to use for scalar value of 3D point"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:573
+#: ../src/backend/filters/dataLoad.cpp:588
 msgid "Value Label"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:577
+#: ../src/backend/filters/dataLoad.cpp:592
 msgid "Name for the scalar value associated with each point"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:580
+#: ../src/backend/filters/dataLoad.cpp:595
 msgid "Format params."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:586
+#: ../src/backend/filters/dataLoad.cpp:601
 msgid "Enabled"
 msgstr "Aktiviert"
 
-#: ../src/backend/filters/dataLoad.cpp:590
+#: ../src/backend/filters/dataLoad.cpp:605
 msgid "Load this file?"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:601
+#: ../src/backend/filters/dataLoad.cpp:616
 msgid "Sample data"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:604
+#: ../src/backend/filters/dataLoad.cpp:619
 msgid ""
 "Perform random selection on file contents, instead of loading entire file"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:611
+#: ../src/backend/filters/dataLoad.cpp:626
 msgid "Load Limit (MB)"
 msgstr "Ladelimit (MB)"
 
-#: ../src/backend/filters/dataLoad.cpp:614
+#: ../src/backend/filters/dataLoad.cpp:629
 msgid "Limit for size of data to load"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:621
+#: ../src/backend/filters/dataLoad.cpp:636
 msgid "Monitor"
 msgstr "Monitor"
 
-#: ../src/backend/filters/dataLoad.cpp:625
+#: ../src/backend/filters/dataLoad.cpp:640
 msgid ""
 "Watch file timestamp to track changes to file contents from other programs"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:629
+#: ../src/backend/filters/dataLoad.cpp:644
 msgid "Load params."
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:636
+#: ../src/backend/filters/dataLoad.cpp:651
 msgid "Default colour "
 msgstr "Bevorzugte Farbe "
 
-#: ../src/backend/filters/dataLoad.cpp:639
+#: ../src/backend/filters/dataLoad.cpp:654
 msgid "Default colour for points, if not overridden by other filters"
 msgstr ""
 
-#: ../src/backend/filters/dataLoad.cpp:644
+#: ../src/backend/filters/dataLoad.cpp:659
 msgid "Draw Size"
 msgstr "Draw Size"
 
-#: ../src/backend/filters/dataLoad.cpp:647
+#: ../src/backend/filters/dataLoad.cpp:662
 msgid "Default size for points, if not overridden by other filters"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:122
+#: ../src/backend/filters/spectrumPlot.cpp:77
+msgid "Maximum"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:78
+msgid "Max in limit"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:79
+msgid "Probability"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:170
 msgid "Extrema"
 msgstr "Extrema"
 
-#: ../src/backend/filters/spectrumPlot.cpp:171
+#: ../src/backend/filters/spectrumPlot.cpp:219
 msgid "count"
 msgstr "Anzahl"
 
-#: ../src/backend/filters/spectrumPlot.cpp:256
+#: ../src/backend/filters/spectrumPlot.cpp:304
 msgid "Mixed data"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:390
+#: ../src/backend/filters/spectrumPlot.cpp:441
+msgid "Background:"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:496
+msgid "Relative "
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:499
+msgid "Probability Density"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:576
 msgid "Step size for spectrum"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:395
+#: ../src/backend/filters/spectrumPlot.cpp:581
 msgid "Auto Min/max"
 msgstr "Auto Min/max"
 
-#: ../src/backend/filters/spectrumPlot.cpp:399
+#: ../src/backend/filters/spectrumPlot.cpp:585
 msgid "Automatically compute spectrum upper and lower bound"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:404
+#: ../src/backend/filters/spectrumPlot.cpp:590
 msgid "Min"
 msgstr "Min"
 
-#: ../src/backend/filters/spectrumPlot.cpp:407
+#: ../src/backend/filters/spectrumPlot.cpp:593
 msgid "Starting position for spectrum"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:412
+#: ../src/backend/filters/spectrumPlot.cpp:598
 msgid "Max"
 msgstr "Max"
 
-#: ../src/backend/filters/spectrumPlot.cpp:415
+#: ../src/backend/filters/spectrumPlot.cpp:601
 msgid "Ending position for spectrum"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:423
+#: ../src/backend/filters/spectrumPlot.cpp:609
 msgid "Logarithmic"
 msgstr "Logarithmisch"
 
-#: ../src/backend/filters/spectrumPlot.cpp:426
+#: ../src/backend/filters/spectrumPlot.cpp:612
 msgid "Convert the plot to logarithmic mode"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:448
+#: ../src/backend/filters/spectrumPlot.cpp:624
+msgid "Normalisation"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:627
+msgid "Rescale the plot height, to make inter-spectrum comparisons easier"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:634
+msgid "Lower Bound"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:638
+msgid "Do not use data below this x-value for normalisation"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:642
+msgid "Upper Bound"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:646
+msgid "Do not use data above this x-value for normalisation"
+msgstr ""
+
+#: ../src/backend/filters/spectrumPlot.cpp:664
 msgid "Visual style of plot"
 msgstr ""
 
-#: ../src/backend/filters/spectrumPlot.cpp:455
+#: ../src/backend/filters/spectrumPlot.cpp:671
 msgid "Colour of plotted spectrum"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:151
+#: ../src/backend/filters/rangeFile.cpp:153
 msgid "Pre-Allocate"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:282 ../src/backend/filter.cpp:48
+#: ../src/backend/filters/rangeFile.cpp:288 ../src/backend/filter.cpp:54
 msgid "Range"
 msgstr "Range"
 
-#: ../src/backend/filters/rangeFile.cpp:572
+#: ../src/backend/filters/rangeFile.cpp:561
 msgid "File to use for range data"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:582
+#: ../src/backend/filters/rangeFile.cpp:571
 msgid "Drop unranged"
 msgstr "Nicht gerangete ausschalten"
 
-#: ../src/backend/filters/rangeFile.cpp:584
+#: ../src/backend/filters/rangeFile.cpp:573
 msgid "Remove unranged points when generating output"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:594
+#: ../src/backend/filters/rangeFile.cpp:583
 msgid "Legend"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:596
+#: ../src/backend/filters/rangeFile.cpp:585
 msgid "Display colour legend for enabled ions"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:600
+#: ../src/backend/filters/rangeFile.cpp:589
 msgid "View"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:616
+#: ../src/backend/filters/rangeFile.cpp:605
 msgid "All Ions"
 msgstr "Alle Ionen"
 
-#: ../src/backend/filters/rangeFile.cpp:617
+#: ../src/backend/filters/rangeFile.cpp:606
 msgid "Enable/disable all ions at once"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:625
+#: ../src/backend/filters/rangeFile.cpp:614
 msgid "Species"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:632
+#: ../src/backend/filters/rangeFile.cpp:621
 msgid "IonID "
 msgstr "IonID "
 
-#: ../src/backend/filters/rangeFile.cpp:633
+#: ../src/backend/filters/rangeFile.cpp:622
 msgid "Enable/disable specified ion"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:643
+#: ../src/backend/filters/rangeFile.cpp:632
 msgid "Active Ion "
 msgstr "Actives Ion "
 
-#: ../src/backend/filters/rangeFile.cpp:645
+#: ../src/backend/filters/rangeFile.cpp:634
 msgid "If true, ion is used in output"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:655
+#: ../src/backend/filters/rangeFile.cpp:644
 msgid "Colour "
 msgstr "Farbe"
 
-#: ../src/backend/filters/rangeFile.cpp:659
+#: ../src/backend/filters/rangeFile.cpp:648
 msgid "Colour used to represent ion"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:682
+#: ../src/backend/filters/rangeFile.cpp:671
 msgid "All Ranges"
 msgstr "Alle Range"
 
-#: ../src/backend/filters/rangeFile.cpp:683
+#: ../src/backend/filters/rangeFile.cpp:672
 msgid "Enable/disable all ranges"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:698
+#: ../src/backend/filters/rangeFile.cpp:687
 msgid "Active Rng "
 msgstr "Activer Rng "
 
-#: ../src/backend/filters/rangeFile.cpp:701
+#: ../src/backend/filters/rangeFile.cpp:690
 msgid ""
 "Enable/disable specified range (ion must also be enabled to activiate range)"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:705
+#: ../src/backend/filters/rangeFile.cpp:694
 msgid "Ion "
 msgstr "Ion "
 
-#: ../src/backend/filters/rangeFile.cpp:708
+#: ../src/backend/filters/rangeFile.cpp:697
 msgid "Name of ion associate to this range"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:717
+#: ../src/backend/filters/rangeFile.cpp:706
 msgid "Start rng "
 msgstr "Start rng "
 
-#: ../src/backend/filters/rangeFile.cpp:720
+#: ../src/backend/filters/rangeFile.cpp:709
 msgid "Start value for range"
 msgstr ""
 
-#: ../src/backend/filters/rangeFile.cpp:725
+#: ../src/backend/filters/rangeFile.cpp:714
 msgid "End rng "
 msgstr "End rng "
 
-#: ../src/backend/filters/rangeFile.cpp:728
+#: ../src/backend/filters/rangeFile.cpp:717
 msgid "Stopping value for range`"
 msgstr ""
 
-#: ../src/backend/state.cpp:137
+#: ../src/backend/state.cpp:151
 msgid ""
 "This file is a \"state\" file for the 3Depict program, and stores "
 "information about a particular analysis session. This file should be a valid "
@@ -4609,29 +4731,29 @@ msgstr ""
 "Informationen über die jeweiligen Analysesitzung. Dies sollte ein gültige "
 "\"XML\" Datei sein."
 
-#: ../src/backend/state.cpp:290
+#: ../src/backend/state.cpp:318
 msgid "Failed to allocate parser"
 msgstr "Kann Parser nicht zuordnen"
 
-#: ../src/backend/state.cpp:325
+#: ../src/backend/state.cpp:353
 msgid ""
 "Unable to retrieve root node in input state file... Is this really a non-"
 "empty XML file?"
 msgstr ""
 
-#: ../src/backend/state.cpp:332
+#: ../src/backend/state.cpp:360
 msgid "Base state node missing. Is this really a state XML file??"
 msgstr ""
 
-#: ../src/backend/state.cpp:361
+#: ../src/backend/state.cpp:389
 msgid "State was created by a newer version of this program.. "
 msgstr "Status wurde von einer neueren Version dieses Programmes erstellt.. "
 
-#: ../src/backend/state.cpp:362
+#: ../src/backend/state.cpp:390
 msgid "file reading will continue, but may fail."
 msgstr "Datei wird weiter eingelesen kann aber unter Umständen fehlschlagen."
 
-#: ../src/backend/state.cpp:367
+#: ../src/backend/state.cpp:395
 msgid ""
 "Warning, unparseable version number in state file. File reading will "
 "continue, but may fail"
@@ -4639,265 +4761,274 @@ msgstr ""
 "Warnung: Nicht lesbare Versionsnummer in Statusdatei. Datei wird weiter "
 "eingelesen kann aber unter Umständen fehlschlagen."
 
-#: ../src/backend/state.cpp:374
+#: ../src/backend/state.cpp:402
 msgid "Unable to find the \"writer\" node"
 msgstr "Kann \"writer\" node nicht finden"
 
-#: ../src/backend/state.cpp:384
+#: ../src/backend/state.cpp:412
 msgid "Unable to find the \"backcolour\" node."
 msgstr "Unable to find the \"backcolour\" node."
 
-#: ../src/backend/state.cpp:391
+#: ../src/backend/state.cpp:419
 msgid "\"backcolour\" node missing \"r\" value."
 msgstr "\"backcolour\" node fehlt \"r\" Wert."
 
-#: ../src/backend/state.cpp:396
+#: ../src/backend/state.cpp:424
 msgid "Unable to interpret \"backColour\" node's \"r\" value."
 msgstr "Kann \"backColour\" node's \"r\" Wert nicht interpretieren."
 
-#: ../src/backend/state.cpp:404
+#: ../src/backend/state.cpp:432
 msgid "\"backcolour\" node missing \"g\" value."
 msgstr "\"backcolour\" node fehlt \"g\" Wert."
 
-#: ../src/backend/state.cpp:410
+#: ../src/backend/state.cpp:438
 msgid "Unable to interpret \"backColour\" node's \"g\" value."
 msgstr "Kann \"backColour\" node's \"g\" Wert nicht interpretieren."
 
-#: ../src/backend/state.cpp:418
+#: ../src/backend/state.cpp:446
 msgid "\"backcolour\" node missing \"b\" value."
 msgstr "\"backcolour\" node fehlt \"b\" Wert."
 
-#: ../src/backend/state.cpp:424
+#: ../src/backend/state.cpp:452
 msgid "Unable to interpret \"backColour\" node's \"b\" value."
 msgstr "Kann \"backColour\" node's \"b\" Wert nicht interpretieren."
 
-#: ../src/backend/state.cpp:431
+#: ../src/backend/state.cpp:459
 msgid "\"backcolour\"s rgb values must be in range [0,1]"
 msgstr "\"backcolour\"s rgb Wert muss im Bereich [0,1] liegen"
 
-#: ../src/backend/state.cpp:459
+#: ../src/backend/state.cpp:487
 msgid "Unable to find or interpret \"showaxis\" node"
 msgstr "Kann \"showaxis\" node nicht interpretieren"
 
-#: ../src/backend/state.cpp:503
+#: ../src/backend/state.cpp:531
 msgid "Unable to locate \"filtertree\" node."
 msgstr "Kann \"filtertree\" node nicht finden."
 
-#: ../src/backend/state.cpp:519
+#: ../src/backend/state.cpp:547
 msgid "Cameras section missing \"active\" node."
 msgstr "Cameras section fehlt \"active\" node."
 
-#: ../src/backend/state.cpp:527
+#: ../src/backend/state.cpp:555
 msgid "Unable to find property \"value\"  for \"cameras->active\" node."
 msgstr "Kann \"Eigenschaftswert\"  für \"Kamera->aktiv\" Node nicht finden."
 
-#: ../src/backend/state.cpp:533
+#: ../src/backend/state.cpp:561
 msgid "Unable to interpret property \"value\"  for \"cameras->active\" node."
 msgstr ""
 "Kann \"Eigenschaftswert\"  für \"Kamera->aktiv\" Node nicht interpretieren."
 
-#: ../src/backend/state.cpp:552
+#: ../src/backend/state.cpp:580
 msgid "Failed to interpret camera state for camera : "
 msgstr ""
 
-#: ../src/backend/state.cpp:560
+#: ../src/backend/state.cpp:588
 msgid "Unable to interpret the camera type for camera : "
 msgstr "Kann den Kameratype nicht interpretieren für :"
 
-#: ../src/backend/state.cpp:596
+#: ../src/backend/state.cpp:632
 msgid "Unable to locate stash name for stash "
 msgstr "Kann den Stashnamen für Stash  nicht finden"
 
-#: ../src/backend/state.cpp:603
+#: ../src/backend/state.cpp:639
 msgid "Empty stash name for stash "
 msgstr "Leerer Stashname für Stash"
 
-#: ../src/backend/state.cpp:612
+#: ../src/backend/state.cpp:648
 msgid "No filter tree for stash:"
 msgstr ""
 
-#: ../src/backend/state.cpp:618
+#: ../src/backend/state.cpp:654
 msgid "For stash "
 msgstr "Für Stash "
 
-#: ../src/backend/state.cpp:650
+#: ../src/backend/state.cpp:686
 msgid "Unrecognised effect :"
 msgstr "Nichterkannter Effekt :"
 
-#: ../src/backend/state.cpp:660
+#: ../src/backend/state.cpp:696
 msgid "Duplicate effect found"
 msgstr "Doppelter Effekt gefunden"
 
-#: ../src/backend/state.cpp:660
+#: ../src/backend/state.cpp:696
 msgid " cannot use."
 msgstr "kann nicht   verwenden."
 
-#: ../src/backend/state.cpp:670
+#: ../src/backend/state.cpp:706
 msgid "Error reading effect : "
 msgstr "Fehler beim Lesen:"
 
-#: ../src/backend/state.cpp:866
+#: ../src/backend/state.cpp:898
 msgid "-merge"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:199
+#: ../src/backend/filtertreeAnalyse.cpp:223
 msgid ""
 "Parent filter has no output, but filter requires input -- there is no point "
 "in placing a child filter here."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:200
+#: ../src/backend/filtertreeAnalyse.cpp:224
 msgid "Leaf-only filter with child"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:210
+#: ../src/backend/filtertreeAnalyse.cpp:234
 msgid ""
 "Parent filters' output will be blocked by child, without use. Parent results "
 "will be dropped."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:211
-#: ../src/backend/filtertreeAnalyse.cpp:225
+#: ../src/backend/filtertreeAnalyse.cpp:235
+#: ../src/backend/filtertreeAnalyse.cpp:249
 msgid "Bad parent->child pair"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:224
+#: ../src/backend/filtertreeAnalyse.cpp:248
 msgid ""
 "First filter does not output anything useable by child filter. Child filter "
 "not useful."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:304
+#: ../src/backend/filtertreeAnalyse.cpp:328
 msgid "Spatial results possibly altered"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:305
+#: ../src/backend/filtertreeAnalyse.cpp:329
 msgid ""
 "Filters and settings selected that could alter reported results that depend "
 "upon density. Check to see if spatial sampling may be happening in the "
 "filter tree - this warning is provisional only."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:373
+#: ../src/backend/filtertreeAnalyse.cpp:397
 msgid "Filter needs parent \""
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:374
+#: ../src/backend/filtertreeAnalyse.cpp:398
 msgid ""
 "\" but does not have one. Filter may not function correctly until this "
 "parent is given."
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:375
+#: ../src/backend/filtertreeAnalyse.cpp:399
 msgid "Filter missing needed parent"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:555
+#: ../src/backend/filtertreeAnalyse.cpp:430
+msgid "Bad range filter settings"
+msgstr ""
+
+#: ../src/backend/filtertreeAnalyse.cpp:431
+msgid ""
+"Rangefile set to drop unranged data, however a child filter requires it."
+msgstr ""
+
+#: ../src/backend/filtertreeAnalyse.cpp:613
 msgid "Composition results possibly altered"
 msgstr ""
 
-#: ../src/backend/filtertreeAnalyse.cpp:556
+#: ../src/backend/filtertreeAnalyse.cpp:614
 msgid ""
 "Filters and settings selected that could bias reported composition. Check to "
 "see if species biasing may occcur in the filter tree - this warning is "
 "provisional only."
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:43 ../src/backend/APT/APTFileIO.cpp:78
-#: ../src/backend/APT/APTFileIO.cpp:102
+#: ../src/backend/APT/APTFileIO.cpp:44 ../src/backend/APT/APTFileIO.cpp:79
+#: ../src/backend/APT/APTFileIO.cpp:103
 msgid "Error opening file"
 msgstr "Fehler beim Öffnen der Datei"
 
-#: ../src/backend/APT/APTFileIO.cpp:44
+#: ../src/backend/APT/APTFileIO.cpp:45
 msgid "Only found header, no data"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:45
+#: ../src/backend/APT/APTFileIO.cpp:46
 msgid "Unable to reopen file after first scan"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:46
+#: ../src/backend/APT/APTFileIO.cpp:47
 msgid "Error whilst reading file contents"
 msgstr "Fehler beim Lesen des Dateiinhaltes"
 
-#: ../src/backend/APT/APTFileIO.cpp:47 ../src/backend/APT/APTFileIO.cpp:48
+#: ../src/backend/APT/APTFileIO.cpp:48 ../src/backend/APT/APTFileIO.cpp:49
 msgid "Unexpected file format"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:49
+#: ../src/backend/APT/APTFileIO.cpp:50
 msgid "Insufficient memory to continue"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:53
+#: ../src/backend/APT/APTFileIO.cpp:54
 msgid "Memory allocation failure on POS load"
 msgstr "Speicherzuweisungsfeher beim Laden der pos-Datei"
 
-#: ../src/backend/APT/APTFileIO.cpp:54
+#: ../src/backend/APT/APTFileIO.cpp:55
 msgid "Error opening pos file"
 msgstr "Fehler beim Öffnen der pos-Datei"
 
-#: ../src/backend/APT/APTFileIO.cpp:55
+#: ../src/backend/APT/APTFileIO.cpp:56
 msgid "Pos file empty"
 msgstr "Pos-Datei ist leer"
 
-#: ../src/backend/APT/APTFileIO.cpp:56
+#: ../src/backend/APT/APTFileIO.cpp:57
 msgid "Pos file size appears to have non-integer number of entries"
 msgstr ""
 "Pos-Dateigröße scheint eine nicht ganzzahlige Anzahl an Einträgen zu haben"
 
-#: ../src/backend/APT/APTFileIO.cpp:57
+#: ../src/backend/APT/APTFileIO.cpp:58
 msgid "Error reading from pos file (after open)"
 msgstr "Fehler beim Lesen aus pos-Datei (nach dem öffnen)"
 
-#: ../src/backend/APT/APTFileIO.cpp:58
+#: ../src/backend/APT/APTFileIO.cpp:59
 msgid "Error - Found NaN in pos file"
 msgstr "Fehler - Fand NaN in pos-Datei"
 
-#: ../src/backend/APT/APTFileIO.cpp:59
+#: ../src/backend/APT/APTFileIO.cpp:60
 msgid "Error - Found Inf in pos file"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:60
+#: ../src/backend/APT/APTFileIO.cpp:61
 msgid "Pos load aborted by interrupt."
 msgstr "Pos laden durch Interrupt abgebrochen."
 
-#: ../src/backend/APT/APTFileIO.cpp:79
+#: ../src/backend/APT/APTFileIO.cpp:80
 msgid "No numerical data found"
 msgstr "Keine numerischen Daten gefunden"
 
-#: ../src/backend/APT/APTFileIO.cpp:80
+#: ../src/backend/APT/APTFileIO.cpp:81
 msgid "Error re-opening file, after first scan"
 msgstr "Fehler beim nochmaligen Öffnen der Datei nach dem ersten Scan"
 
-#: ../src/backend/APT/APTFileIO.cpp:81
+#: ../src/backend/APT/APTFileIO.cpp:82
 msgid "Unable to read file contents after open"
 msgstr "Kann den Dateiinhalt nach dem Öffnen nich lesen"
 
-#: ../src/backend/APT/APTFileIO.cpp:82
+#: ../src/backend/APT/APTFileIO.cpp:83
 msgid "Error interpreting field in file"
 msgstr "Fehler beim Interpretieren eine Feldes in der Datei"
 
-#: ../src/backend/APT/APTFileIO.cpp:83
+#: ../src/backend/APT/APTFileIO.cpp:84
 msgid "Incorrect number of fields in file"
 msgstr "Die Datei enthält eine falsche Anzahl von Feldern"
 
-#: ../src/backend/APT/APTFileIO.cpp:84 ../src/backend/APT/APTFileIO.cpp:106
+#: ../src/backend/APT/APTFileIO.cpp:85 ../src/backend/APT/APTFileIO.cpp:107
 msgid "Unable to allocate memory to store data"
 msgstr "Kann Speicher nicht zuordnen"
 
-#: ../src/backend/APT/APTFileIO.cpp:103
+#: ../src/backend/APT/APTFileIO.cpp:104
 msgid "File is empty"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:104
+#: ../src/backend/APT/APTFileIO.cpp:105
 msgid "Filesize does not match expected format"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:105
+#: ../src/backend/APT/APTFileIO.cpp:106
 msgid "File version number not <4, as expected"
 msgstr ""
 
-#: ../src/backend/APT/APTFileIO.cpp:107
+#: ../src/backend/APT/APTFileIO.cpp:108
 msgid "Unable to detect endian-ness in file"
 msgstr ""
 
@@ -5004,7 +5135,7 @@ msgstr ""
 msgid "Range file is exceedingly large. Refusing to open"
 msgstr ""
 
-#: ../src/backend/APT/APTRanges.cpp:1403
+#: ../src/backend/APT/APTRanges.cpp:1404
 msgid ""
 "Range headings do not match order of the ions listed in the name "
 "specifications. The name specification ordering will be used when reading "
@@ -5013,15 +5144,15 @@ msgid ""
 "Check range-species associations actually match what you expect."
 msgstr ""
 
-#: ../src/backend/filter.cpp:46
+#: ../src/backend/filter.cpp:52
 msgid "2D Plot"
 msgstr ""
 
-#: ../src/backend/filter.cpp:47
+#: ../src/backend/filter.cpp:53
 msgid "Draw"
 msgstr "Zeichnen"
 
-#: ../src/backend/filter.cpp:49
+#: ../src/backend/filter.cpp:55
 msgid "Voxel"
 msgstr "Voxel"
 
@@ -5045,7 +5176,7 @@ msgstr "Begrenzungs-Box"
 msgid "Ion Sampler"
 msgstr "Ion Sampler"
 
-#: ../src/backend/filters/ionInfo.h:91
+#: ../src/backend/filters/ionInfo.h:103
 msgid "Ion info"
 msgstr "Ioneninfo"
 
@@ -5057,11 +5188,11 @@ msgstr "POS-Daten"
 msgid "Ext. Program"
 msgstr "Ext. Programm"
 
-#: ../src/backend/filters/rangeFile.h:93
+#: ../src/backend/filters/rangeFile.h:96
 msgid "Ranging"
 msgstr "Ranging"
 
-#: ../src/backend/filters/compositionProfile.h:117
+#: ../src/backend/filters/compositionProfile.h:120
 msgid "Comp. Prof."
 msgstr "Konz. Prof."
 
@@ -5172,6 +5303,33 @@ msgid ""
 "views"
 msgstr ""
 
+#~ msgid "Last Outputs"
+#~ msgstr "Letzte Ausgabe"
+
+#~ msgid "Type"
+#~ msgstr "Type"
+
+#~ msgid "Num"
+#~ msgstr "Num"
+
+#~ msgid "Aborted."
+#~ msgstr "Abgebrochen"
+
+#~ msgid "Gaussian (2𝜎)"
+#~ msgstr "Gauss (2𝜎)"
+
+#~ msgid "Zero"
+#~ msgstr "Null"
+
+#~ msgid "Bounce"
+#~ msgstr "Bounce"
+
+#~ msgid "Kernel Bins"
+#~ msgstr "Kernel Bins"
+
+#~ msgid "Exterior values"
+#~ msgstr "Exterior values"
+
 #~ msgid "Inconsistent number of columns found"
 #~ msgstr "Inkonsistente Anzahl an Spalten gefunden"
 
diff --git a/translations/makeTranslations b/translations/makeTranslations
index 1be87fe..93abd38 100755
--- a/translations/makeTranslations
+++ b/translations/makeTranslations
@@ -5,6 +5,8 @@
 #I Shouldn't need to create some mega ultimate makefile.am for that, then
 #follow some random poorly documented hierarchy and naming system. (Looking at you gettextize!)
 
+#Usage: makeTranslation [install|update]
+
 #extract program name from sources -- Now with added case sensitivity hack!
 PROGRAM_NAME=`cat ../src/common/constants.cpp | grep PROGRAM_NAME | awk -F= '{print $2}' | sed 's/;//g' | sed 's/\"//g' | sed 's/;//' | sed 's/^\s*//'`
 #Where do we want to install the translations? (if using this script).

-- 
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