[3depict] 07/07: * Add files not caught by git-import-orig (?)

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


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

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

commit 4974a7b2826e8929ff230ade54c431895778bb8c
Author: D Haley <mycae at gmx.com>
Date:   Thu Aug 4 01:43:45 2016 +0200

    * Add files not caught by git-import-orig (?)
---
 data/textures/tex-source/3Depict-icon-hires.png    |  Bin 0 -> 58757 bytes
 m4/ax_compare_version.m4                           |  177 ++
 packaging/RPM/3Depict-0.0.19-font-path.patch       |   27 +
 packaging/RPM/3Depict-0.0.19-manual-pdf-loc.patch  |   16 +
 .../patches/iconv-fix-alias2.patch                 |   13 +
 .../patches/mathgl-disable-things                  |   82 +
 .../patches/mathgl-fix-pthread-and-linking         |   34 +
 .../mingw-debian-cross/patches/qhull-ptr.patch     |   17 +
 .../patches/qhull2015-cmakefile-replacement        |  624 ++++++
 src/backend/APT/vtk.cpp                            |  175 ++
 src/backend/APT/vtk.h                              |   54 +
 src/backend/filters/profile.cpp                    | 1993 ++++++++++++++++++++
 src/backend/filters/profile.h                      |  159 ++
 13 files changed, 3371 insertions(+)

diff --git a/data/textures/tex-source/3Depict-icon-hires.png b/data/textures/tex-source/3Depict-icon-hires.png
new file mode 100644
index 0000000..9a56ed0
Binary files /dev/null and b/data/textures/tex-source/3Depict-icon-hires.png differ
diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4
new file mode 100644
index 0000000..74dc0fd
--- /dev/null
+++ b/m4/ax_compare_version.m4
@@ -0,0 +1,177 @@
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_compare_version.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+#
+# DESCRIPTION
+#
+#   This macro compares two version strings. Due to the various number of
+#   minor-version numbers that can exist, and the fact that string
+#   comparisons are not compatible with numeric comparisons, this is not
+#   necessarily trivial to do in a autoconf script. This macro makes doing
+#   these comparisons easy.
+#
+#   The six basic comparisons are available, as well as checking equality
+#   limited to a certain number of minor-version levels.
+#
+#   The operator OP determines what type of comparison to do, and can be one
+#   of:
+#
+#    eq  - equal (test A == B)
+#    ne  - not equal (test A != B)
+#    le  - less than or equal (test A <= B)
+#    ge  - greater than or equal (test A >= B)
+#    lt  - less than (test A < B)
+#    gt  - greater than (test A > B)
+#
+#   Additionally, the eq and ne operator can have a number after it to limit
+#   the test to that number of minor versions.
+#
+#    eq0 - equal up to the length of the shorter version
+#    ne0 - not equal up to the length of the shorter version
+#    eqN - equal up to N sub-version levels
+#    neN - not equal up to N sub-version levels
+#
+#   When the condition is true, shell commands ACTION-IF-TRUE are run,
+#   otherwise shell commands ACTION-IF-FALSE are run. The environment
+#   variable 'ax_compare_version' is always set to either 'true' or 'false'
+#   as well.
+#
+#   Examples:
+#
+#     AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
+#     AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
+#
+#   would both be true.
+#
+#     AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
+#     AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
+#
+#   would both be false.
+#
+#     AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
+#
+#   would be true because it is only comparing two minor versions.
+#
+#     AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
+#
+#   would be true because it is only comparing the lesser number of minor
+#   versions of the two values.
+#
+#   Note: The characters that separate the version numbers do not matter. An
+#   empty string is the same as version 0. OP is evaluated by autoconf, not
+#   configure, so must be a string, not a variable.
+#
+#   The author would like to acknowledge Guido Draheim whose advice about
+#   the m4_case and m4_ifvaln functions make this macro only include the
+#   portions necessary to perform the specific comparison specified by the
+#   OP argument in the final configure script.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Tim Toolan <toolan at ele.uri.edu>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 11
+
+dnl #########################################################################
+AC_DEFUN([AX_COMPARE_VERSION], [
+  AC_REQUIRE([AC_PROG_AWK])
+
+  # Used to indicate true or false condition
+  ax_compare_version=false
+
+  # Convert the two version strings to be compared into a format that
+  # allows a simple string comparison.  The end result is that a version
+  # string of the form 1.12.5-r617 will be converted to the form
+  # 0001001200050617.  In other words, each number is zero padded to four
+  # digits, and non digits are removed.
+  AS_VAR_PUSHDEF([A],[ax_compare_version_A])
+  A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  AS_VAR_PUSHDEF([B],[ax_compare_version_B])
+  B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
+  dnl # then the first line is used to determine if the condition is true.
+  dnl # The sed right after the echo is to remove any indented white space.
+  m4_case(m4_tolower($2),
+  [lt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [gt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [le],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],
+  [ge],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],[
+    dnl Split the operator from the subversion count if present.
+    m4_bmatch(m4_substr($2,2),
+    [0],[
+      # A count of zero means use the length of the shorter version.
+      # Determine the number of characters in A and B.
+      ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'`
+      ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'`
+
+      # Set A to no more than B's length and B to no more than A's length.
+      A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
+    ],
+    [[0-9]+],[
+      # A count greater than zero means use only that many subversions
+      A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+    ],
+    [.+],[
+      AC_WARNING(
+        [illegal OP numeric parameter: $2])
+    ],[])
+
+    # Pad zeros at end of numbers to make same length.
+    ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
+    B="$B`echo $A | sed 's/./0/g'`"
+    A="$ax_compare_version_tmp_A"
+
+    # Check for equality or inequality as necessary.
+    m4_case(m4_tolower(m4_substr($2,0,2)),
+    [eq],[
+      test "x$A" = "x$B" && ax_compare_version=true
+    ],
+    [ne],[
+      test "x$A" != "x$B" && ax_compare_version=true
+    ],[
+      AC_WARNING([illegal OP parameter: $2])
+    ])
+  ])
+
+  AS_VAR_POPDEF([A])dnl
+  AS_VAR_POPDEF([B])dnl
+
+  dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
+  if test "$ax_compare_version" = "true" ; then
+    m4_ifvaln([$4],[$4],[:])dnl
+    m4_ifvaln([$5],[else $5])dnl
+  fi
+]) dnl AX_COMPARE_VERSION
diff --git a/packaging/RPM/3Depict-0.0.19-font-path.patch b/packaging/RPM/3Depict-0.0.19-font-path.patch
new file mode 100644
index 0000000..02f52fc
--- /dev/null
+++ b/packaging/RPM/3Depict-0.0.19-font-path.patch
@@ -0,0 +1,27 @@
+diff -r 7abb69436c2b src/wx/wxcomponents.cpp
+--- src/wx/wxcomponents.cpp	Sat Aug 02 05:39:24 2014 -0400
++++ src/wx/wxcomponents.cpp	Sat Aug 02 05:40:51 2014 -0400
+@@ -547,16 +547,17 @@
+ 	//(Oh look Ma, I'm autoconf!)
+ 
+ 	const char *dirs[] = {	".",
+-				"/usr/share/fonts/truetype", //Old debian 
++				"/usr/local/share/fonts/truetype", // User fonts
+ 				"/usr/share/fonts/truetype/freefont", // New debian
+ 				"/usr/share/fonts/truetype/ttf-dejavu", //New debian
+-				"/usr/local/share/fonts/truetype", // User fonts
++				"/usr/share/fonts/truetype", //Old debian 
++				"/usr/share/fonts/dejavu", //Fedora
+ 				"/usr/X11R6/lib/X11/fonts/truetype",
+ 				"/usr/X11R6/lib64/X11/fonts/truetype",
+-				"/usr/lib/X11/fonts/truetype",// Fedora 32
+-				"/usr/lib64/X11/fonts/truetype", //Fedora 64
+-				"/usr/local/lib/X11/fonts/truetype", // Fedora 32 new
+-				"/usr/local/lib64/X11/fonts/truetype",// Fedora 64 new
++				"/usr/lib/X11/fonts/truetype",
++				"/usr/lib64/X11/fonts/truetype", 
++				"/usr/local/lib/X11/fonts/truetype", 
++				"/usr/local/lib64/X11/fonts/truetype",
+ 				"",
+ 				}; //MUST end with "".
+ 
diff --git a/packaging/RPM/3Depict-0.0.19-manual-pdf-loc.patch b/packaging/RPM/3Depict-0.0.19-manual-pdf-loc.patch
new file mode 100644
index 0000000..31178af
--- /dev/null
+++ b/packaging/RPM/3Depict-0.0.19-manual-pdf-loc.patch
@@ -0,0 +1,16 @@
+diff -r 7abb69436c2b src/gui/mainFrame.cpp
+--- src/gui/mainFrame.cpp	Sat Aug 02 05:39:24 2014 -0400
++++ src/gui/mainFrame.cpp	Sat Aug 02 05:40:32 2014 -0400
+@@ -2840,9 +2840,9 @@
+ 	string s;
+ 	s=locateDataFile("3Depict-manual.pdf");
+ 
+-	//Also Debian makes us use the lowercase "D", so check there too.
+-	if(!s.size())
+-		s=locateDataFile("3depict-manual.pdf");
++	//Also Fedora has diff dir
++	if(!wxFileExists(s))
++		s="/usr/share/doc/3Depict-0.0.8/3Depict-0.0.8-manual.pdf";
+ 
+ 	//FIXME: under windows, currently we use "manual.pdf"
+ 	if(!s.size())
diff --git a/packaging/mingw-debian-cross/patches/iconv-fix-alias2.patch b/packaging/mingw-debian-cross/patches/iconv-fix-alias2.patch
new file mode 100644
index 0000000..f11dc60
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/iconv-fix-alias2.patch
@@ -0,0 +1,13 @@
+--- a/lib/iconv.c.orig	2015-08-01 20:34:47.018022800 +0100
++++ b/lib/iconv.c	2015-08-01 20:35:06.783246600 +0100
+@@ -176,9 +176,6 @@
+ #include "aliases2.h"
+ #undef S
+ };
+-#ifdef __GNUC__
+-__inline
+-#endif
+ const struct alias *
+ aliases2_lookup (register const char *str)
+ {
+
diff --git a/packaging/mingw-debian-cross/patches/mathgl-disable-things b/packaging/mingw-debian-cross/patches/mathgl-disable-things
new file mode 100644
index 0000000..9de0e5c
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/mathgl-disable-things
@@ -0,0 +1,82 @@
+diff -r 5d7a3ac5d87d CMakeLists.txt
+--- a/CMakeLists.txt	Sat Apr 23 01:11:46 2016 +0100
++++ b/CMakeLists.txt	Sat Apr 23 01:14:12 2016 +0100
+@@ -94,17 +94,17 @@
+ set(MGL_LIB_INSTALL_DIR "lib" CACHE STRING "Set library install directory")
+ string(TIMESTAMP MGL_NIGHT "%d.%m.%y")
+ 
+-option(enable-double "Enable double precision in MathGL library" ON)
+-option(enable-mpi "Enable mpi" ON)
+-option(enable-opengl "Enable OpenGL support" ON)
+-option(enable-all-docs "Enable all documentation building")
++option(enable-double "Enable double precision in MathGL library" OFF)
++option(enable-mpi "Enable mpi" OFF)
++option(enable-opengl "Enable OpenGL support" OFF)
++option(enable-all-docs "Enable all documentation building" OFF)
+ #option(enable-doc "Enable documentation building")
+ option(enable-all "Enable all core features")
+-option(enable-all-widgets "Enable all Widgets")
+-option(enable-all-swig "Enable all SWIG based interfaces")
++option(enable-all-widgets "Enable all Widgets" OFF)
++option(enable-all-swig "Enable all SWIG based interfaces" OFF)
+ option(enable-rvalue "Enable move constructor support (need C++11)" OFF)
+-option(enable-pthread "Enable POSIX threads support" ON)
+-option(enable-pthr-widget "Enable POSIX threads for widgets" ON)
++option(enable-pthread "Enable POSIX threads support" OFF)
++option(enable-pthr-widget "Enable POSIX threads for widgets" OFF)
+ option(enable-openmp "Enable OpenMP support" OFF)
+ 
+ if(enable-pthread AND enable-openmp)
+@@ -114,7 +114,7 @@
+ option(enable-lgpl "Enable only LGPL part of MathGL")
+ option(enable-mgl2 "Use names 'libmgl2-*' instead of 'libmgl-*'")
+ option(enable-ltdl "Enable loading modules support" ON)
+-CMAKE_DEPENDENT_OPTION(enable-doc-site "Enable HTML documentation for website" OFF "NOT enable-all-docs" ON)
++CMAKE_DEPENDENT_OPTION(enable-doc-site "Enable HTML documentation for website" OFF "NOT enable-all-docs" O)
+ CMAKE_DEPENDENT_OPTION(enable-doc-html "Enable HTML documentation" OFF "NOT enable-all-docs" ON)
+ CMAKE_DEPENDENT_OPTION(enable-doc-info "Enable INFO documentation" OFF "NOT enable-all-docs" ON)
+ CMAKE_DEPENDENT_OPTION(enable-doc-pdf-ru "Enable Russian PDF documentation" OFF "NOT enable-all-docs" ON)
+@@ -128,16 +128,16 @@
+ CMAKE_DEPENDENT_OPTION(enable-png "Enable png support" ON "NOT enable-all" ON)
+ CMAKE_DEPENDENT_OPTION(enable-jpeg "Enable jpeg support" ON "NOT enable-all" ON)
+ MGL_DEPENDENT_OPTION(enable-gsl "Enable gsl support" ON "NOT enable-lgpl" ON "NOT enable-all" ON)
+-MGL_DEPENDENT_OPTION(enable-hdf4 "Enable hdf4 support" ON "NOT enable-lgpl" ON "NOT enable-all" ON)
+-MGL_DEPENDENT_OPTION(enable-hdf5 "Enable hdf5 support" ON "NOT enable-lgpl" ON "NOT enable-all" ON)
+-CMAKE_DEPENDENT_OPTION(enable-pdf "Enable pdf support" ON "NOT enable-all" ON)
+-CMAKE_DEPENDENT_OPTION(enable-gif "Enable gif support" ON "NOT enable-all" ON)
+-CMAKE_DEPENDENT_OPTION(enable-glut "Enable glut support" ON "NOT enable-all-widgets" ON)
+-CMAKE_DEPENDENT_OPTION(enable-fltk "Enable fltk widget" ON "NOT enable-all-widgets" ON)
+-CMAKE_DEPENDENT_OPTION(enable-wx "Enable wxWidget widget" ON "NOT enable-all-widgets" ON)
+-CMAKE_DEPENDENT_OPTION(enable-qt4 "Enable Qt4 widget" OFF "NOT enable-all-widgets" ON)
+-CMAKE_DEPENDENT_OPTION(enable-qt5 "Enable Qt5 widget" ON "NOT enable-all-widgets" ON)
+-CMAKE_DEPENDENT_OPTION(enable-qt5asqt "Set Qt5 as default libmgl-qt" ON "enable-qt5" ON)
++MGL_DEPENDENT_OPTION(enable-hdf4 "Enable hdf4 support" OFF "NOT enable-lgpl" OFF "NOT enable-all" OFF)
++MGL_DEPENDENT_OPTION(enable-hdf5 "Enable hdf5 support" OFF "NOT enable-lgpl" OFF "NOT enable-all" OFF)
++CMAKE_DEPENDENT_OPTION(enable-pdf "Enable pdf support" OFF "NOT enable-all" OFF)
++CMAKE_DEPENDENT_OPTION(enable-gif "Enable gif support" OFF "NOT enable-all" OFF)
++CMAKE_DEPENDENT_OPTION(enable-glut "Enable glut support" OFF "NOT enable-all-widgets" OFF)
++CMAKE_DEPENDENT_OPTION(enable-fltk "Enable fltk widget" OFF "NOT enable-all-widgets" OFF)
++CMAKE_DEPENDENT_OPTION(enable-wx "Enable wxWidget widget" OFF "NOT enable-all-widgets" OFF)
++CMAKE_DEPENDENT_OPTION(enable-qt4 "Enable Qt4 widget" OFF "NOT enable-all-widgets" OFF)
++CMAKE_DEPENDENT_OPTION(enable-qt5 "Enable Qt5 widget" OFF "NOT enable-all-widgets" OFF)
++CMAKE_DEPENDENT_OPTION(enable-qt5asqt "Set Qt5 as default libmgl-qt" OFF "enable-qt5" OFF)
+ 
+ if(UNIX AND enable-rvalue)
+ 	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
+@@ -149,11 +149,11 @@
+ set(QT_ENABLED ON)
+ endif(enable-qt4 OR enable-qt5)
+ 
+-CMAKE_DEPENDENT_OPTION(enable-json-sample "Enable JSON sample" ON "QT_ENABLED" ON)
+-MGL_DEPENDENT_OPTION(enable-python "Enable python interface" ON "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
+-MGL_DEPENDENT_OPTION(enable-lua "Enable Lua (v.5.1) interface" OFF "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
+-MGL_DEPENDENT_OPTION(enable-octave "Enable octave interface" ON "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
+-MGL_DEPENDENT_OPTION(enable-octave-install "Octave interface will install for all users" ON "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
++CMAKE_DEPENDENT_OPTION(enable-json-sample "Enable JSON sample" OFF "QT_ENABLED" OFF)
++MGL_DEPENDENT_OPTION(enable-python "Enable python interface" OFF "NOT enable-lgpl" OFF "NOT enable-all-swig" OFF)
++MGL_DEPENDENT_OPTION(enable-lua "Enable Lua (v.5.1) interface" OFF "NOT enable-lgpl" OFF "NOT enable-all-swig" OFF)
++MGL_DEPENDENT_OPTION(enable-octave "Enable octave interface" OFF "NOT enable-lgpl" OFF "NOT enable-all-swig" OFF)
++MGL_DEPENDENT_OPTION(enable-octave-install "Octave interface will install for all users" OFF "NOT enable-lgpl" OFF "NOT enable-all-swig" OFF)
+ 
+ include_directories( ${MathGL_SOURCE_DIR}/include ${MathGL_BINARY_DIR}/include)
+ set(MGL_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/include/mgl2")
diff --git a/packaging/mingw-debian-cross/patches/mathgl-fix-pthread-and-linking b/packaging/mingw-debian-cross/patches/mathgl-fix-pthread-and-linking
new file mode 100644
index 0000000..7f7fe88
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/mathgl-fix-pthread-and-linking
@@ -0,0 +1,34 @@
+diff -r 9fbd31e8af49 CMakeLists.txt
+--- a/CMakeLists.txt	Sun Sep 20 14:25:16 2015 +0100
++++ b/CMakeLists.txt	Sun Sep 20 14:26:23 2015 +0100
+@@ -12,9 +12,9 @@
+ set(MathGL_VERSION_MINOR 2.2)
+ set(MathGL_SOVERSION 7.2.0)
+ 
+-set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro")
+-set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro")
+-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro")
++set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -lpng")
++set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lpng")
++set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpng")
+ 
+ MACRO(MGL_DEPENDENT_OPTION option doc default depends1 force1 depends2 force2)
+   IF(${option}_ISSET MATCHES "^${option}_ISSET$")
+@@ -61,7 +61,7 @@
+ 
+ set(MGL_LIB_INSTALL_DIR "lib" CACHE STRING "Set library install directory")
+ 
+-option(enable-double "Enable double precision in MathGL library" OFF)
++option(enable-double "Enable double precision in MathGL library" ON)
+ option(enable-simple "Slightly increase drawing speed but disable mglDataA class")
+ option(enable-mpi "Enable mpi" OFF)
+ option(enable-opengl "Enable OpenGL support" OFF)
+@@ -70,7 +70,7 @@
+ option(enable-all "Enable all core features")
+ option(enable-all-widgets "Enable all Widgets" OFF)
+ option(enable-all-swig "Enable all SWIG based interfaces" OFF)
+-option(enable-pthread "Enable POSIX threads support" ON)
++option(enable-pthread "Enable POSIX threads support" OFF)
+ option(enable-openmp "Enable OpenMP support" OFF)
+ option(enable-lgpl "Enable only LGPL part of MathGL")
+ option(enable-mgl2 "Use names 'libmgl2-*' instead of 'libmgl-*'")
diff --git a/packaging/mingw-debian-cross/patches/qhull-ptr.patch b/packaging/mingw-debian-cross/patches/qhull-ptr.patch
new file mode 100644
index 0000000..6892493
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/qhull-ptr.patch
@@ -0,0 +1,17 @@
+diff -r ec21eff71acf src/libqhull_r/mem_r.h
+--- a/src/libqhull_r/mem_r.h	Sun Apr 24 16:13:34 2016 +0100
++++ b/src/libqhull_r/mem_r.h	Sun Apr 24 16:15:10 2016 +0100
+@@ -88,13 +88,7 @@
+     Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
+     This matches Qt convention and is easier to work with.
+ */
+-#if (defined(__MINGW64__)) && defined(_WIN64)
+ typedef long long ptr_intT;
+-#elif (_MSC_VER) && defined(_WIN64)
+-typedef long long ptr_intT;
+-#else
+-typedef long ptr_intT;
+-#endif
+ 
+ /*-<a                             href="qh-mem_r.htm#TOC"
+   >--------------------------------</a><a name="qhmemT">-</a>
diff --git a/packaging/mingw-debian-cross/patches/qhull2015-cmakefile-replacement b/packaging/mingw-debian-cross/patches/qhull2015-cmakefile-replacement
new file mode 100644
index 0000000..d152d4d
--- /dev/null
+++ b/packaging/mingw-debian-cross/patches/qhull2015-cmakefile-replacement
@@ -0,0 +1,624 @@
+# CMakeLists.txt -- CMake configuration file for qhull, qhull6, and related programs
+#
+# To install CMake
+#   Download from http://www.cmake.org/download/
+#
+# To find the available targets for CMake -G "..."
+#   cmake --help
+#
+# To build with MSYS/mingw
+#   cd build && cmake -G "MSYS Makefiles" .. && cmake ..
+#   make
+#   make install
+#
+# To uninstall on unix or MSYS/mingw
+#   xargs rm <build/install_manifest.txt
+#
+# To build Qhull with Visual Studio projects, run cmake twice
+#   To install bin/doc/include/lib in the current directory
+#      mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012" .. && cmake -DCMAKE_INSTALL_PREFIX=.. ..
+#      mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012 Win64" .. && cmake -DCMAKE_INSTALL_PREFIX=.. ..
+#   To install into Program Files/qhull
+#      mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012" .. && cmake ..
+#      mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012 Win64" .. && cmake ..
+#   To build for Visual Studio 2005 and install into Program Files/qhull
+#      mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 8 2005" .. && cmake  ..
+#      mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 8 2005 Win64" .. && cmake  ..
+#   Double click build-cmake/qhull-all.sln
+#   Build INSTALL to copy files into C:/Program Files/qhull
+#
+# Additional build targets
+#   qhullp -- Same as qhull using qh_QHpointer and deprecated libqhull_p
+#   user_egp -- Same as user_eg using qh_QHpointer and deprecated libqhull_p
+#
+# Notes on Visual Studio projects
+#   You may need to copy bin/msvcr80.dll into C:/Program Files/qhull/bin
+#   If using library debug targets, please rename with '_d' (e.g., qhullstatic_d.lib)
+# 
+# Troubleshooting
+#   "No CMAKE_C_COMPILER could be found"
+#     cmake was not able to find the build environment specified (e.g., Visual Studio 11)
+# 
+# To uninstall on Windows
+#   Delete C:/Program Files/qhull
+#
+# If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in
+#
+# For qhulltest, use the Qt build (src/qhull-all.pro)
+#
+# Qhull ships with cmake-derived sln and proj files for DevStudio 8 2005
+#   See eg/make-vcproj.sh
+#   Change to relative paths
+#   Remove ZERO_CHECK, ALL_BUILD, and INSTALL projects
+#   Change targets to bin/ and lib/ directories
+#   Disable incremental linking and ilk files (LinkIncremental="1")
+#   Disable Run-Time Type Info (rtti)
+#   Remove src/libqhullcpp from most of the AdditionalIncludeDirectories
+#   Remove CMAKE_INTDIR from PreprocessorDefinitions
+#   Adjust target names and destinations (e.g., lib/libqhullstatic_rd.a)
+#  
+# $Id: //main/2015/qhull/CMakeLists.txt#8 $$Change: 2066 $
+# $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+
+project(qhull)
+cmake_minimum_required(VERSION 2.6)
+set(CMAKE_BUILD_TYPE "Release")
+
+# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
+set(qhull_VERSION2 "2015.2 2016/01/18")  # not used, See global.c, global_r.c, rbox.c, rbox_r.c
+set(qhull_VERSION     "7.2.0")  # Advance every release
+
+# SOVERSION -- qhull 2003 = empty, 2009 = 5, 2010-2012 = 6, 2015 (reentrant) = 7
+set(qhull_SOVERSION 7) # For SOVERSION 
+
+include(CMakeModules/CheckLFS.cmake)
+option(WITH_LFS "Enable Large File Support" ON)
+check_lfs(WITH_LFS)
+
+if(INCLUDE_INSTALL_DIR)
+else()
+set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include)
+endif()
+if(LIB_INSTALL_DIR)
+else()
+set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib)
+endif()
+if(BIN_INSTALL_DIR)
+else()
+set(BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/bin)
+endif()
+if(MAN_INSTALL_DIR)
+else()
+    if(WIN32)
+        set(MAN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/man/man1)
+    else()
+        set(MAN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/man/man1)
+    endif()
+endif()
+if(DOC_INSTALL_DIR)
+else()
+    if(WIN32)
+        set(DOC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/doc)
+    else()
+        set(DOC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/qhull)
+    endif()
+endif()
+message(STATUS)
+message(STATUS "========== qhull Build Information ==========")
+message(STATUS "Build Version:                             ${qhull_VERSION}")
+message(STATUS "Install Prefix (CMAKE_INSTALL_PREFIX):     ${CMAKE_INSTALL_PREFIX}")
+message(STATUS "Binary Directory (BIN_INSTALL_DIR):        ${BIN_INSTALL_DIR}")
+message(STATUS "Library Directory (LIB_INSTALL_DIR):       ${LIB_INSTALL_DIR}")
+message(STATUS "Include Directory (INCLUDE_INSTALL_DIR):   ${INCLUDE_INSTALL_DIR}")
+message(STATUS "Documentation Directory (DOC_INSTALL_DIR): ${DOC_INSTALL_DIR}")
+message(STATUS "Man Pages Directory (MAN_INSTALL_DIR):     ${MAN_INSTALL_DIR}")
+message(STATUS "Build Type (CMAKE_BUILD_TYPE):             ${CMAKE_BUILD_TYPE}")
+message(STATUS "To override these options, add -D{OPTION_NAME}=... to the cmake command")
+message(STATUS "  Build the debug targets                  -DCMAKE_BUILD_TYPE=Debug")
+message(STATUS)
+message(STATUS "To build and install qhull, enter \"make\" and \"make install\"")
+message(STATUS "To smoketest qhull, enter \"ctest\"")
+message(STATUS)
+
+
+# ---------------------------------------
+# Define library source files and variables
+#
+# Files for individual targets are defined with the target
+# ---------------------------------------
+
+# Order libqhull object files by frequency of execution.  Small files at end.
+
+## Non-reentrant Qhull
+#set(
+#    libqhull_HEADERS
+#        src/libqhull/libqhull.h
+#        src/libqhull/geom.h
+#        src/libqhull/io.h
+#        src/libqhull/mem.h
+#        src/libqhull/merge.h
+#        src/libqhull/poly.h
+#        src/libqhull/qhull_a.h
+#        src/libqhull/qset.h
+#        src/libqhull/random.h
+#        src/libqhull/stat.h
+#        src/libqhull/user.h
+#)
+#set(
+#    libqhull_SOURCES
+#        src/libqhull/global.c
+#        src/libqhull/stat.c
+#        src/libqhull/geom2.c
+#        src/libqhull/poly2.c
+#        src/libqhull/merge.c
+#        src/libqhull/libqhull.c
+#        src/libqhull/geom.c
+#        src/libqhull/poly.c
+#        src/libqhull/qset.c
+#        src/libqhull/mem.c
+#        src/libqhull/random.c
+#        src/libqhull/usermem.c
+#        src/libqhull/userprintf.c
+#        src/libqhull/io.c
+#        src/libqhull/user.c
+#        src/libqhull/rboxlib.c
+#        src/libqhull/userprintf_rbox.c
+#        ${libqhull_HEADERS}
+#)
+
+set(
+    libqhull_DOC
+        src/libqhull/index.htm
+        src/libqhull/qh-geom.htm
+        src/libqhull/qh-globa.htm
+        src/libqhull/qh-io.htm
+        src/libqhull/qh-mem.htm
+        src/libqhull/qh-merge.htm
+        src/libqhull/qh-poly.htm
+        src/libqhull/qh-qhull.htm
+        src/libqhull/qh-set.htm
+        src/libqhull/qh-stat.htm
+        src/libqhull/qh-user.htm    
+        src/libqhull/DEPRECATED.txt    
+)
+
+#set(
+#    testqset_HEADERS
+#        src/libqhull/mem.h
+#        src/libqhull/qset.h
+#)
+#set(
+#    testqset_SOURCES
+#        src/libqhull/qset.c
+#        src/libqhull/mem.c
+#        src/libqhull/usermem.c
+#        src/testqset/testqset.c
+#        ${testqset_HEADERS}
+#)
+
+# Reeentrant Qhull
+
+set(
+    libqhullr_HEADERS
+        src/libqhull_r/libqhull_r.h
+        src/libqhull_r/geom_r.h
+        src/libqhull_r/io_r.h
+        src/libqhull_r/mem_r.h
+        src/libqhull_r/merge_r.h
+        src/libqhull_r/poly_r.h
+        src/libqhull_r/qhull_ra.h
+        src/libqhull_r/qset_r.h
+        src/libqhull_r/random_r.h
+        src/libqhull_r/stat_r.h
+        src/libqhull_r/user_r.h
+)
+set(
+    libqhullr_SOURCES
+        src/libqhull_r/global_r.c
+        src/libqhull_r/stat_r.c
+        src/libqhull_r/geom2_r.c
+        src/libqhull_r/poly2_r.c
+        src/libqhull_r/merge_r.c
+        src/libqhull_r/libqhull_r.c
+        src/libqhull_r/geom_r.c
+        src/libqhull_r/poly_r.c
+        src/libqhull_r/qset_r.c
+        src/libqhull_r/mem_r.c
+        src/libqhull_r/random_r.c
+        src/libqhull_r/usermem_r.c
+        src/libqhull_r/userprintf_r.c
+        src/libqhull_r/io_r.c
+        src/libqhull_r/user_r.c
+        src/libqhull_r/rboxlib_r.c
+        src/libqhull_r/userprintf_rbox_r.c
+        ${libqhullr_HEADERS}
+)
+
+set(
+    libqhullr_DOC
+        src/libqhull_r/index.htm
+        src/libqhull_r/qh-geom_r.htm
+        src/libqhull_r/qh-globa_r.htm
+        src/libqhull_r/qh-io_r.htm
+        src/libqhull_r/qh-mem_r.htm
+        src/libqhull_r/qh-merge_r.htm
+        src/libqhull_r/qh-poly_r.htm
+        src/libqhull_r/qh-qhull_r.htm
+        src/libqhull_r/qh-set_r.htm
+        src/libqhull_r/qh-stat_r.htm
+        src/libqhull_r/qh-user_r.htm    
+)
+
+set(
+    testqsetr_HEADERS
+        src/libqhull_r/mem_r.h
+        src/libqhull_r/qset_r.h
+)
+set(
+    testqsetr_SOURCES
+        src/libqhull_r/qset_r.c
+        src/libqhull_r/mem_r.c
+        src/libqhull_r/usermem_r.c
+        src/testqset_r/testqset_r.c
+        ${testqsetr_HEADERS}
+)
+
+# C++ interface to reentrant Qhull
+
+#set(
+#    libqhullcpp_HEADERS
+#        src/libqhullcpp/Coordinates.h
+#        src/libqhullcpp/functionObjects.h
+#        src/libqhullcpp/PointCoordinates.h
+#        src/libqhullcpp/Qhull.h
+#        src/libqhullcpp/QhullError.h
+#        src/libqhullcpp/QhullFacet.h
+#        src/libqhullcpp/QhullFacetList.h
+#        src/libqhullcpp/QhullFacetSet.h
+#        src/libqhullcpp/QhullHyperplane.h
+#        src/libqhullcpp/QhullIterator.h
+#        src/libqhullcpp/QhullLinkedList.h
+#        src/libqhullcpp/QhullPoint.h
+#        src/libqhullcpp/QhullPoints.h
+#        src/libqhullcpp/QhullPointSet.h
+#        src/libqhullcpp/QhullQh.h
+#        src/libqhullcpp/QhullRidge.h
+#        src/libqhullcpp/QhullSet.h
+#        src/libqhullcpp/QhullSets.h
+#        src/libqhullcpp/QhullStat.h
+#        src/libqhullcpp/QhullVertex.h
+#        src/libqhullcpp/QhullVertexSet.h
+#        src/libqhullcpp/RboxPoints.h
+#        src/libqhullcpp/RoadError.h
+#        src/libqhullcpp/RoadLogEvent.h
+#        src/qhulltest/RoadTest.h
+#)
+
+#set(
+#    libqhullcpp_SOURCES
+#        src/libqhullcpp/Coordinates.cpp
+#        src/libqhullcpp/PointCoordinates.cpp
+#        src/libqhullcpp/Qhull.cpp
+#        src/libqhullcpp/QhullFacet.cpp
+#        src/libqhullcpp/QhullFacetList.cpp
+#        src/libqhullcpp/QhullFacetSet.cpp
+#        src/libqhullcpp/QhullHyperplane.cpp
+#        src/libqhullcpp/QhullPoint.cpp
+#        src/libqhullcpp/QhullPointSet.cpp
+#        src/libqhullcpp/QhullPoints.cpp
+#        src/libqhullcpp/QhullQh.cpp
+#        src/libqhullcpp/QhullRidge.cpp
+#        src/libqhullcpp/QhullSet.cpp
+#        src/libqhullcpp/QhullStat.cpp
+#        src/libqhullcpp/QhullVertex.cpp
+#        src/libqhullcpp/QhullVertexSet.cpp
+#        src/libqhullcpp/RboxPoints.cpp
+#        src/libqhullcpp/RoadError.cpp
+#        src/libqhullcpp/RoadLogEvent.cpp
+#        ${libqhullcpp_HEADERS}
+#)
+#
+# Documentation files (index.htm refers to html/...)
+
+set(
+    doc_FILES
+        README.txt 
+        REGISTER.txt 
+        Announce.txt 
+        COPYING.txt 
+        index.htm
+)
+
+include_directories(${CMAKE_SOURCE_DIR}/src)
+
+if(CMAKE_BUILD_TYPE MATCHES "[dD]ebug")
+    set(qhull_CPP qhullcpp_d)
+    set(qhull_SHARED qhull_d) 
+    set(qhull_SHAREDP qhull_pd)
+    set(qhull_SHAREDR qhull_rd)
+    set(qhull_STATIC qhullstatic_d)
+    set(qhull_STATICR qhullstatic_rd)
+else()
+    set(qhull_CPP qhullcpp)
+    set(qhull_SHARED libqhull)  # Temporarily avoid name conflict with qhull executable
+    set(qhull_SHAREDP qhull_p)
+    set(qhull_SHAREDR qhull_r)
+    set(qhull_STATIC qhullstatic)
+    set(qhull_STATICR qhullstatic_r)
+endif()
+
+set(
+    qhull_TARGETS_INSTALL ${qhull_SHAREDR}
+)
+set(
+    qhull_TARGETS_TEST   # Unused
+        user_eg user_eg2 user_eg3 user_egp testqset testqset_r
+)
+
+# ---------------------------------------
+# Define shared library for reentrant qhull (installed)
+# ---------------------------------------
+
+add_library(${qhull_SHAREDR} SHARED 
+        ${libqhullr_SOURCES}
+        src/libqhull_r/qhull_r-exports.def)
+set_target_properties(${qhull_SHAREDR} PROPERTIES
+    SOVERSION ${qhull_SOVERSION}
+    VERSION ${qhull_VERSION})
+
+if(UNIX)
+    target_link_libraries(${qhull_SHAREDR} m)
+    if(APPLE)
+        set_target_properties(${qhull_SHAREDR} PROPERTIES 
+            INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
+    else()
+        set_target_properties(${qhull_SHAREDR} PROPERTIES 
+            INSTALL_RPATH "${LIB_INSTALL_DIR}"
+            INSTALL_RPATH_USE_LINK_PATH TRUE
+            BUILD_WITH_INSTALL_RPATH FALSE)
+    endif()
+endif(UNIX)
+
+# ---------------------------------------
+# Define shared library for non-reentrant qhull without qh_QHpointer
+# ---------------------------------------
+
+#add_library(${qhull_SHARED} SHARED 
+#        ${libqhull_SOURCES}
+#        src/libqhull/qhull-exports.def)
+#        
+#if(qhull_SHARED MATCHES "libqhull")
+#   set(qhull_OUTPUT_NAME qhull)
+#   set_target_properties(${qhull_SHARED} PROPERTIES
+#        OUTPUT_NAME "${qhull_OUTPUT_NAME}" )
+#endif()
+#
+#set_target_properties(${qhull_SHARED} PROPERTIES
+#    SOVERSION ${qhull_SOVERSION}
+#    VERSION ${qhull_VERSION})
+#
+#if(UNIX)
+#    target_link_libraries(${qhull_SHARED} m)
+#    if(APPLE)
+#        set_target_properties(${qhull_SHARED} PROPERTIES 
+#            INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
+#    else()
+#        set_target_properties(${qhull_SHARED} PROPERTIES 
+#            INSTALL_RPATH "${LIB_INSTALL_DIR}"
+#            INSTALL_RPATH_USE_LINK_PATH TRUE
+#            BUILD_WITH_INSTALL_RPATH FALSE)
+#    endif()
+#endif(UNIX)
+
+# ---------------------------------------
+# Define old shared library qhull with qh_QHpointer
+# ---------------------------------------
+
+#add_library(${qhull_SHAREDP} SHARED 
+#        ${libqhull_SOURCES}
+#        src/libqhull/qhull_p-exports.def)
+#set_target_properties(${qhull_SHAREDP} PROPERTIES
+#    COMPILE_DEFINITIONS "qh_QHpointer"
+#    SOVERSION ${qhull_SOVERSION}
+#    VERSION ${qhull_VERSION})
+#
+#if(UNIX)
+#    target_link_libraries(${qhull_SHAREDP} m)
+#    if(APPLE)
+#        set_target_properties(${qhull_SHAREDP} PROPERTIES 
+#            INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
+#    else()
+#        set_target_properties(${qhull_SHAREDP} PROPERTIES 
+#            INSTALL_RPATH "${LIB_INSTALL_DIR}"
+#            INSTALL_RPATH_USE_LINK_PATH TRUE
+#            BUILD_WITH_INSTALL_RPATH FALSE)
+#    endif()
+#endif(UNIX)
+
+# ---------------------------------------
+# Define static libraries qhullstatic (non-reentrant) and qhullstatic_r (reentrant)
+# ---------------------------------------
+
+#add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES})
+#set_target_properties(${qhull_STATIC} PROPERTIES
+#    VERSION ${qhull_VERSION})
+#
+#add_library(${qhull_STATICR} STATIC ${libqhullr_SOURCES})
+#set_target_properties(${qhull_STATICR} PROPERTIES
+#    VERSION ${qhull_VERSION})
+
+#if(UNIX)
+#    target_link_libraries(${qhull_STATIC} m)
+#    target_link_libraries(${qhull_STATICR} m)
+#endif(UNIX)
+#
+## ---------------------------------------
+## Define C++ static library qhullcpp
+##     Do not create libqhullcpp as a shared library.  Qhull C++ classes may change layout and size. 
+## ---------------------------------------
+#
+#add_library(${qhull_CPP} STATIC ${libqhullcpp_SOURCES})
+#set_target_properties(${qhull_CPP} PROPERTIES
+#    VERSION ${qhull_VERSION})
+
+# ---------------------------------------
+# Define qhull executables linked to qhullstatic library
+#   qhull is linked to reentrant qhull (more flexible)
+#   the others are linked to non-reentrant qhull (somewhat faster)
+# ---------------------------------------
+
+set(qhull_SOURCES       src/qhull/unix_r.c)
+set(rbox_SOURCES        src/rbox/rbox.c)
+set(qconvex_SOURCES     src/qconvex/qconvex.c)
+set(qdelaunay_SOURCES   src/qdelaunay/qdelaun.c)
+set(qvoronoi_SOURCES    src/qvoronoi/qvoronoi.c)
+set(qhalf_SOURCES       src/qhalf/qhalf.c)
+
+#add_executable(qhull ${qhull_SOURCES})
+#target_link_libraries(qhull ${qhull_STATICR})
+#
+#add_executable(rbox ${rbox_SOURCES})
+#target_link_libraries(rbox ${qhull_STATIC})
+#
+#add_executable(qconvex ${qconvex_SOURCES})
+#target_link_libraries(qconvex ${qhull_STATIC})
+#
+#add_executable(qdelaunay ${qdelaunay_SOURCES})
+#target_link_libraries(qdelaunay ${qhull_STATIC})
+#
+#add_executable(qvoronoi ${qvoronoi_SOURCES})
+#target_link_libraries(qvoronoi ${qhull_STATIC})
+#
+#add_executable(qhalf ${qhalf_SOURCES})
+#target_link_libraries(qhalf ${qhull_STATIC})
+
+# ---------------------------------------
+# Define options for linking to qhull_SHAREDR or qhull_SHARED
+# ---------------------------------------
+#if(MSVC)
+#    set(user_eg_DEFINES qh_dllimport)
+#    set(user_eg2_DEFINES qh_dllimport)
+#    set(user_eg3_DEFINES qh_dllimport)
+#    set(user_egp_DEFINES qh_QHpointer_dllimport qh_QHpointer)
+#    set(qhullp_DEFINES qh_QHpointer_dllimport qh_QHpointer)
+#else()
+#    set(user_eg_DEFINES )
+#    set(user_eg2_DEFINES )
+#    set(user_eg3_DEFINES )
+#    set(user_egp_DEFINES )
+#    set(qhullp_DEFINES )
+#endif()
+
+# ---------------------------------------
+# Define testqset linked to qset.o and mem.o
+# Define testqset_r linked to qset_r.o and mem_r.o
+# ---------------------------------------
+
+#add_executable(testqset ${testqset_SOURCES})
+#add_executable(testqset_r ${testqsetr_SOURCES})
+
+# ---------------------------------------
+# Define user_eg linked to reentrant qhull shared library
+# ---------------------------------------
+#
+#set(user_eg_SOURCES     src/user_eg/user_eg_r.c)
+#
+#add_executable(user_eg ${user_eg_SOURCES})
+## user_eg may be linked to qhull_STATICR if user_eg_DEFINES is removed
+#target_link_libraries(user_eg ${qhull_SHAREDR})
+#set_target_properties(user_eg PROPERTIES
+#    COMPILE_DEFINITIONS "${user_eg_DEFINES}")
+
+# ---------------------------------------
+# Define user_eg2 linked to reentrant qhull static library
+# ---------------------------------------
+
+#set(user_eg2_SOURCES    src/user_eg2/user_eg2_r.c)
+#
+#add_executable(user_eg2 ${user_eg2_SOURCES})
+## user_eg2 may be linked to qhull_SHAREDR if user_eg2_DEFINES is added
+#target_link_libraries(user_eg2 ${qhull_STATICR})
+
+# ---------------------------------------
+# Define user_eg3 linked to qhullstatic_r and qhullcpp static library
+# 
+# user_eg3 and qhullcpp must be compiled with the same compiler for setjmp/longjmp
+## ---------------------------------------
+#
+#set(user_eg3_SOURCES    src/user_eg3/user_eg3_r.cpp)
+#
+#add_executable(user_eg3 ${user_eg3_SOURCES})
+## qhull_STATICR must be last, otherwise qh_fprintf,etc. are not loaded from qhull_CPP
+## user_eg3 may be linked to qhull_SHAREDR if user_eg3_DEFINES is added
+#target_link_libraries(user_eg3 ${qhull_CPP} ${qhull_STATICR})
+#
+## ---------------------------------------
+## qhullp is qhull/unix.c linked to deprecated qh_QHpointer libqhull_p
+## Included for testing qh_QHpointer 
+## ---------------------------------------
+#
+#set(qhullp_SOURCES     src/qhull/unix.c)
+#
+#add_executable(qhullp EXCLUDE_FROM_ALL ${qhullp_SOURCES})
+#target_link_libraries(qhullp ${qhull_SHAREDP})
+#set_target_properties(qhullp PROPERTIES
+#    COMPILE_DEFINITIONS "${qhullp_DEFINES}")
+#
+# ---------------------------------------
+# user_egp is user_eg/user_eg.c linked to deprecated qh_QHpointer libqhull_p
+# Included for compatibility with qhull-2012.1 
+# ---------------------------------------
+
+#set(user_egp_SOURCES   src/user_eg/user_eg.c)
+#
+#add_executable(user_egp EXCLUDE_FROM_ALL ${user_egp_SOURCES})
+#target_link_libraries(user_egp ${qhull_SHAREDP})
+#set_target_properties(user_egp PROPERTIES
+#    COMPILE_DEFINITIONS "${user_egp_DEFINES}")
+#
+# ---------------------------------------
+# Define test
+# ---------------------------------------
+
+#enable_testing()
+#add_test(NAME testqset
+#   COMMAND ./testqset 10000)
+#add_test(NAME testqset_r
+#   COMMAND ./testqset_r 10000)
+#add_test(NAME smoketest
+#   COMMAND sh -c "./rbox D4 | ./qhull Tv")
+#add_test(NAME rbox-10-qhull
+#   COMMAND sh -c "./rbox 10 | ./qhull Tv")
+#add_test(NAME rbox-10-qconvex
+#   COMMAND sh -c "./rbox 10 | ./qconvex Tv")
+#add_test(NAME rbox-10-qdelaunay
+#   COMMAND sh -c "./rbox 10 | ./qdelaunay Tv")
+#add_test(NAME rbox-10-qhalf
+#   COMMAND sh -c "./rbox 10 | ./qconvex FQ FV n Tv | ./qhalf Tv")
+#add_test(NAME rbox-10-qvoronoi
+#   COMMAND sh -c "./rbox 10 | ./qvoronoi Tv")
+#add_test(NAME user_eg
+#   COMMAND sh -c "./user_eg")
+#add_test(NAME user_eg2
+#   COMMAND sh -c "./user_eg2")
+#add_test(NAME user_eg3
+#   COMMAND sh -c "./user_eg3 rbox '10 D2' '2 D2' qhull 's p' facets")
+
+# ---------------------------------------
+# Define install
+# ---------------------------------------
+
+install(TARGETS ${qhull_TARGETS_INSTALL}
+        RUNTIME DESTINATION ${BIN_INSTALL_DIR}
+        LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+        ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+
+#install(FILES ${libqhull_HEADERS}    DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull)
+#install(FILES ${libqhull_DOC}        DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull)
+install(FILES ${libqhullr_HEADERS}    DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull_r)
+install(FILES ${libqhullr_DOC}        DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull_r)
+#install(FILES ${libqhullcpp_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhullcpp)
+#install(FILES html/qhull.man         DESTINATION ${MAN_INSTALL_DIR} RENAME qhull.1)
+#install(FILES html/rbox.man          DESTINATION ${MAN_INSTALL_DIR} RENAME rbox.1)
+#install(FILES ${doc_FILES}           DESTINATION ${DOC_INSTALL_DIR})
+#install(DIRECTORY html/              DESTINATION ${DOC_INSTALL_DIR})
diff --git a/src/backend/APT/vtk.cpp b/src/backend/APT/vtk.cpp
new file mode 100644
index 0000000..3d15f0a
--- /dev/null
+++ b/src/backend/APT/vtk.cpp
@@ -0,0 +1,175 @@
+/*
+ *	vtk.cpp - VTK file Import-export 
+ *	Copyright (C) 2016, 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 "vtk.h"
+
+#include <fstream>
+
+using std::endl;
+using std::vector;
+using std::string;
+using std::cerr;
+
+
+//Adapted with permission (2016) from mVTK, by
+// guillaume flandin
+unsigned int vtk_write_legacy(const std::string &filename, unsigned int format,
+		const std::vector<IonHit> &ions)
+{
+
+	std::ofstream f;
+
+	if(format != VTK_ASCII)
+	{
+		cerr << "Binary mode is not implemented"
+			<< endl;
+
+		return VTK_ERR_NOT_IMPLEMENTED;
+	}
+
+	f.open(filename.c_str());
+
+	if(!f)
+		return VTK_ERR_FILE_OPEN_FAIL;
+		
+
+
+	f << "# vtk DataFile Version 2.0\n";
+	f << "Saved using AtomProbe Tools\n";
+	f << "ASCII\n\n";
+
+	f << "DATASET UNSTRUCTURED_GRID\n";
+	f << "POINTS " << ions.size() << " float\n";
+	//Write ion data which is the support points for later scalar data
+	for(unsigned int ui=0;ui<ions.size(); ui++)
+	{
+		f << ions[ui][0] << " " << ions[ui][1] << " "<< 
+			ions[ui][2] << "\n";
+	}	
+
+	f << "POINT_DATA " << ions.size() << endl;
+
+	f << "SCALARS masstocharge float\n"; 
+	f << "LOOKUP_TABLE default\n";
+
+	for(unsigned int ui=0;ui<ions.size(); ui++)
+	{
+		f << ions[ui].getMassToCharge() << "\n";
+	}
+
+
+	return 0;
+}
+
+//TODO: This is a template function, we will need to move it to the header
+template<class T>
+unsigned int vtk_write_legacy(const std::string &filename, unsigned int format,
+
+		const Voxels<T> &vox)
+{
+
+	std::ofstream f;
+
+	if(format != VTK_ASCII)
+	{
+		cerr << "Binary mode is not implemented"
+			<< endl;
+
+		return VTK_ERR_NOT_IMPLEMENTED;
+	}
+
+	f.open(filename.c_str());
+
+	if(!f)
+		return VTK_ERR_FILE_OPEN_FAIL;
+		
+
+
+	f << "# vtk DataFile Version 3.0\n";
+	f << "Saved using AtomProbe Tools\n";
+	f << "ASCII\n\n";
+
+	size_t nx,ny,nz;
+	vox.getSize(nx,ny,nz);
+	f << "DATASET RECTILINEAR_GRID\n";
+	f << "DIMENSIONS " << nx << " " << ny << " " << nz << endl;
+
+
+	f << "X_COORDINATES " << nx << " float" << endl;
+	for(unsigned int ui=0;ui<nx;ui++)
+	{
+		f << vox.getPoint((nx-1)-ui,0,0)[0] << " ";
+	}
+	f << endl; 
+	
+	f << "Y_COORDINATES " << ny << " float" << endl;
+	for(unsigned int ui=0;ui<ny;ui++)
+	{
+		f << vox.getPoint(0,ui,0)[1] << " ";
+	}
+	f << endl; 
+	
+	f << "Z_COORDINATES " << nz << " float" << endl;
+	for(unsigned int ui=0;ui<nz;ui++)
+	{
+		f << vox.getPoint(0,0,ui)[2] << " ";
+	}
+	f << endl; 
+	
+
+	f << "POINT_DATA " << vox.size() << endl;
+	f << "SCALARS masstocharge float\n"; 
+	f << "LOOKUP_TABLE default\n";
+
+	for(unsigned int ui=0;ui<vox.size(); ui++)
+	{
+		f << vox.getData(ui)<< "\n";
+	}
+	return 0;
+}
+
+
+#ifdef DEBUG
+
+bool testVTKExport()
+{
+	vector<IonHit> ions;
+
+	//make a cube of ions, each with a differing mass.
+	for(unsigned int ui=0;ui<8;ui++)
+		ions.push_back(IonHit(Point3D(ui &1, (ui & 2) >>1, (ui &4) >>2),ui));
+
+	//export it
+	TEST(vtk_write_legacy("debug.vtk",VTK_ASCII,ions) == 0,"VTK write");
+
+
+	Voxels<float> v;
+	v.resize(3,3,3);
+	v.setData(0,0,0,1);
+	v.setData(1,0,0,2);
+	v.setData(2,0,0,3);
+	v.setData(2,1,0,4);
+
+
+	vtk_write_legacy("debug-vox.vtk",VTK_ASCII,v);
+
+	return true;	
+}
+
+#endif
diff --git a/src/backend/APT/vtk.h b/src/backend/APT/vtk.h
new file mode 100644
index 0000000..fcea225
--- /dev/null
+++ b/src/backend/APT/vtk.h
@@ -0,0 +1,54 @@
+/*
+ *	vtk.h - VTK file Import-export 
+ *	Copyright (C) 2016, 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 VTK_H 
+#define VTK_H
+
+#include <vector>
+#include <string>
+
+#include "ionhit.h"
+#include "common/voxels.h"
+
+enum
+{
+	VTK_ERR_FILE_OPEN_FAIL=1,
+	VTK_ERR_NOT_IMPLEMENTED,
+	VTK_ERR_ENUM_END
+};
+
+enum
+{
+	VTK_ASCII,
+	VTK_BINARY,
+	VTK_FORMAT_ENUM_END
+};
+
+//write ions to a VTK (paraview compatible) file. 
+// FIXME : This currenly only supports ASCII mode. 
+//	Need binary mode because of the large files we have
+unsigned int vtk_write_legacy(const std::string &filename, 
+	unsigned int format, const std::vector<IonHit> &ions);
+
+unsigned int vtk_write_legacy(const std::string &filename, 
+	unsigned int format, const Voxels<class T> &vox);
+
+#ifdef DEBUG
+//unit testing
+bool testVTKExport();
+#endif
+#endif 
diff --git a/src/backend/filters/profile.cpp b/src/backend/filters/profile.cpp
new file mode 100644
index 0000000..455dba1
--- /dev/null
+++ b/src/backend/filters/profile.cpp
@@ -0,0 +1,1993 @@
+/*
+ *	profile.cpp - Compute composition or density profiles from valued point clouds
+ *	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 "profile.h"
+#include "../plot.h"
+
+#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_AXIAL,
+	PRIMITIVE_CYLINDER_RADIAL,
+	PRIMITIVE_SPHERE,
+	PRIMITIVE_END, //Not actually a primitive, just end of enum
+};
+
+
+//!Error codes
+enum
+{
+	ERR_NUMBINS=1,
+	ERR_MEMALLOC,
+	ERR_ABORT,
+	ERR_COMP_ENUM_END
+};
+
+const char *PRIMITIVE_NAME[]={
+	NTRANS("Cylinder (axial)"),
+	NTRANS("Cylinder (radial)"),
+	NTRANS("Sphere")
+};
+
+const float DEFAULT_RADIUS = 10.0f;
+
+const unsigned int MINEVENTS_DEFAULT =10;
+
+
+ProfileFilter::ProfileFilter() : 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)
+{
+	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(PRIMITIVE_NAME) == PRIMITIVE_END);
+
+	wantDensity=false;	
+	errMode.mode=PLOT_ERROR_NONE;
+	errMode.movingAverageNum=4;
+	
+	vectorParams.push_back(Point3D(0.0,0.0,0.0));
+	vectorParams.push_back(Point3D(0,20.0,0.0));
+	scalarParams.push_back(DEFAULT_RADIUS);
+
+	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 ProfileFilter::binIon(unsigned int targetBin, const RangeStreamData* rng, 
+	const map<unsigned int,unsigned int> &ionIDMapping,
+	vector<vector<size_t> > &frequencyTable, float massToCharge) 
+{
+	//if we have no range data, then simply increment its position in a 1D table
+	//which will later be used as "count" data (like some kind of density plot)
+	if(!rng)
+	{
+		ASSERT(frequencyTable.size() == 1);
+		//There is a really annoying numerical boundary case
+		//that makes the target bin equate to the table size. 
+		//disallow this.
+		if(targetBin < frequencyTable[0].size())
+		{
+			vector<size_t>::iterator it;
+			it=frequencyTable[0].begin()+targetBin;
+			#pragma omp critical
+			(*it)++;
+		}
+		return;
+	}
+
+
+	//We have range data, we need to use it to classify the ion and then increment
+	//the appropriate position in the table
+	unsigned int rangeID = rng->rangeFile->getRangeID(massToCharge);
+
+	if(rangeID != (unsigned int)(-1) && rng->enabledRanges[rangeID])
+	{
+		unsigned int ionID=rng->rangeFile->getIonID(rangeID); 
+		unsigned int pos;
+		pos = ionIDMapping.find(ionID)->second;
+		vector<size_t>::iterator it;
+		it=frequencyTable[pos].begin()+targetBin;
+		#pragma omp critical
+		(*it)++;
+	}
+}
+
+
+Filter *ProfileFilter::cloneUncached() const
+{
+	ProfileFilter *p = new ProfileFilter();
+
+	p->primitiveType=primitiveType;
+	p->showPrimitive=showPrimitive;
+	p->vectorParams.resize(vectorParams.size());
+	p->scalarParams.resize(scalarParams.size());
+
+	std::copy(vectorParams.begin(),vectorParams.end(),p->vectorParams.begin());
+	std::copy(scalarParams.begin(),scalarParams.end(),p->scalarParams.begin());
+
+	p->wantDensity=wantDensity;
+	p->normalise=normalise;	
+	p->fixedBins=fixedBins;
+	p->lockAxisMag=lockAxisMag;
+	
+	p->rgba=rgba;
+	p->binWidth=binWidth;
+	p->nBins = nBins;
+	p->plotStyle=plotStyle;
+	p->errMode=errMode;
+	//We are copying whether to cache or not,
+	//not the cache itself
+	p->cache=cache;
+	p->cacheOK=false;
+	p->userString=userString;
+	return p;
+}
+
+void ProfileFilter::initFilter(const std::vector<const FilterStreamData *> &dataIn,
+				std::vector<const FilterStreamData *> &dataOut)
+{
+	//Check for range file parent
+	for(unsigned int ui=0;ui<dataIn.size();ui++)
+	{
+		if(dataIn[ui]->getStreamType() == STREAM_TYPE_RANGE)
+		{
+			haveRangeParent=true;
+			return;
+		}
+	}
+	haveRangeParent=false;
+}
+
+unsigned int ProfileFilter::refresh(const std::vector<const FilterStreamData *> &dataIn,
+			std::vector<const FilterStreamData *> &getOut, ProgressData &progress) 
+{
+	//Clear selection devices
+	// FIXME: Leaking drawables.
+	clearDevices();
+	
+	if(showPrimitive)
+	{
+		//TODO: This is a near-copy of ionClip.cpp - refactor
+		//construct a new primitive, do not cache
+		DrawStreamData *drawData=new DrawStreamData;
+		drawData->parent=this;
+		switch(primitiveType)
+		{
+			case PRIMITIVE_CYLINDER_AXIAL:
+			case PRIMITIVE_CYLINDER_RADIAL:
+			{
+				//Origin + normal
+				ASSERT(vectorParams.size() == 2);
+				//Add drawable components
+				DrawCylinder *dC = new DrawCylinder;
+				dC->setOrigin(vectorParams[0]);
+				dC->setRadius(scalarParams[0]);
+				dC->setColour(0.5,0.5,0.5,0.3);
+				dC->setSlices(40);
+				dC->setLength(sqrtf(vectorParams[1].sqrMag())*2.0f);
+				dC->setDirection(vectorParams[1]);
+				dC->wantsLight=true;
+				drawData->drawables.push_back(dC);
+				
+					
+				//Set up selection "device" for user interaction
+				//====
+				//The object is selectable
+				dC->canSelect=true;
+				//Start and end radii must be the same (not a
+				//tapered cylinder)
+				dC->lockRadii();
+
+				SelectionDevice *s = new SelectionDevice(this);
+				SelectionBinding b;
+				//Bind the drawable object to the properties we wish
+				//to be able to modify
+
+				//Bind left + command button to move
+				b.setBinding(SELECT_BUTTON_LEFT,FLAG_CMD,DRAW_CYLINDER_BIND_ORIGIN,
+					BINDING_CYLINDER_ORIGIN,dC->getOrigin(),dC);	
+				b.setInteractionMode(BIND_MODE_POINT3D_TRANSLATE);
+				s->addBinding(b);
+
+				//Bind left + shift to change orientation
+				b.setBinding(SELECT_BUTTON_LEFT,FLAG_SHIFT,DRAW_CYLINDER_BIND_DIRECTION,
+					BINDING_CYLINDER_DIRECTION,dC->getDirection(),dC);	
+				if(lockAxisMag)
+					b.setInteractionMode(BIND_MODE_POINT3D_ROTATE_LOCK);
+				else
+					b.setInteractionMode(BIND_MODE_POINT3D_ROTATE);
+				s->addBinding(b);
+
+				//Bind right button to changing position 
+				b.setBinding(SELECT_BUTTON_RIGHT,0,DRAW_CYLINDER_BIND_ORIGIN,
+					BINDING_CYLINDER_ORIGIN,dC->getOrigin(),dC);	
+				b.setInteractionMode(BIND_MODE_POINT3D_TRANSLATE);
+				s->addBinding(b);
+					
+				//Bind middle button to changing orientation
+				b.setBinding(SELECT_BUTTON_MIDDLE,0,DRAW_CYLINDER_BIND_DIRECTION,
+					BINDING_CYLINDER_DIRECTION,dC->getDirection(),dC);	
+				if(lockAxisMag)
+					b.setInteractionMode(BIND_MODE_POINT3D_ROTATE_LOCK);
+				else
+					b.setInteractionMode(BIND_MODE_POINT3D_ROTATE);
+				s->addBinding(b);
+					
+				//Bind left button to changing radius
+				b.setBinding(SELECT_BUTTON_LEFT,0,DRAW_CYLINDER_BIND_RADIUS,
+					BINDING_CYLINDER_RADIUS,dC->getRadius(),dC);
+				b.setInteractionMode(BIND_MODE_FLOAT_TRANSLATE);
+				b.setFloatLimits(0,std::numeric_limits<float>::max());
+				s->addBinding(b); 
+				
+				devices.push_back(s);
+				//=====
+				
+				break;
+			}
+			case PRIMITIVE_SPHERE:
+			{
+				//Add drawable components
+				DrawSphere *dS = new DrawSphere;
+				dS->setOrigin(vectorParams[0]);
+				dS->setRadius(scalarParams[0]);
+				//FIXME: Alpha blending is all screwed up. May require more
+				//advanced drawing in scene. (front-back drawing).
+				//I have set alpha=1 for now.
+				dS->setColour(0.5,0.5,0.5,1.0);
+				dS->setLatSegments(40);
+				dS->setLongSegments(40);
+				dS->wantsLight=true;
+				drawData->drawables.push_back(dS);
+
+				//Set up selection "device" for user interaction
+				//Note the order of s->addBinding is critical,
+				//as bindings are selected by first match.
+				//====
+				//The object is selectable
+				dS->canSelect=true;
+
+				SelectionDevice *s = new SelectionDevice(this);
+				SelectionBinding b[3];
+
+				//Apple doesn't have right click, so we need
+				//to hook up an additional system for them.
+				//Don't use ifdefs, as this would be useful for
+				//normal laptops and the like.
+				b[0].setBinding(SELECT_BUTTON_LEFT,FLAG_CMD,DRAW_SPHERE_BIND_ORIGIN,
+							BINDING_SPHERE_ORIGIN,dS->getOrigin(),dS);
+				b[0].setInteractionMode(BIND_MODE_POINT3D_TRANSLATE);
+				s->addBinding(b[0]);
+
+				//Bind the drawable object to the properties we wish
+				//to be able to modify
+				b[1].setBinding(SELECT_BUTTON_LEFT,0,DRAW_SPHERE_BIND_RADIUS,
+					BINDING_SPHERE_RADIUS,dS->getRadius(),dS);
+				b[1].setInteractionMode(BIND_MODE_FLOAT_TRANSLATE);
+				b[1].setFloatLimits(0,std::numeric_limits<float>::max());
+				s->addBinding(b[1]);
+
+				b[2].setBinding(SELECT_BUTTON_RIGHT,0,DRAW_SPHERE_BIND_ORIGIN,
+					BINDING_SPHERE_ORIGIN,dS->getOrigin(),dS);	
+				b[2].setInteractionMode(BIND_MODE_POINT3D_TRANSLATE);
+				s->addBinding(b[2]);
+					
+				devices.push_back(s);
+				//=====
+				break;
+			}
+			default:
+				ASSERT(false);
+		}
+		drawData->cached=0;	
+		getOut.push_back(drawData);
+	}
+
+
+	//Propagate all the incoming data (excluding ions)
+	propagateStreams(dataIn,getOut,STREAM_TYPE_IONS,true);
+	
+	//use the cached copy of the data if we have it.
+	if(cacheOK)
+	{
+		//propagate our cached plot data.
+		propagateCache(getOut);
+
+		ASSERT(filterOutputs.back()->getStreamType() == STREAM_TYPE_PLOT);
+
+		progress.filterProgress=100;
+		return 0;
+	}
+			
+
+	//Ion Frequencies (composition specific if rangefile present)
+	vector<vector<size_t> > ionFrequencies;
+	
+	RangeStreamData *rngData=0;
+	for(unsigned int ui=0;ui<dataIn.size() ;ui++)
+	{
+		if(dataIn[ui]->getStreamType() == STREAM_TYPE_RANGE)
+		{
+			rngData =((RangeStreamData *)dataIn[ui]);
+			break;
+		}
+	}
+
+	unsigned int numBins, errCode;
+	{
+	float length;
+	errCode=getBinData(numBins,length);
+
+	if(!numBins)
+		return 0;
+	}
+
+	if(errCode)
+		return errCode;
+
+	//Indirection vector to convert ionFrequencies position to ionID mapping.
+	//Should only be used in conjunction with rngData == true
+	std::map<unsigned int,unsigned int> ionIDMapping,inverseIDMapping;
+	//Allocate space for the frequency table
+	if(rngData)
+	{
+		ASSERT(rngData->rangeFile);
+		unsigned int enabledCount=0;
+		for(unsigned int ui=0;ui<rngData->rangeFile->getNumIons();ui++)
+		{
+			//TODO: Might be nice to detect if an ions ranges
+			//are all, disabled then if they are, enter this "if"
+			//anyway
+			if(rngData->enabledIons[ui])
+			{
+				//Keep the forwards mapping for binning
+				ionIDMapping.insert(make_pair(ui,enabledCount));
+				//Keep the inverse mapping for labelling
+				inverseIDMapping.insert(make_pair(enabledCount,ui));
+				enabledCount++;
+			}
+
+		
+
+		}
+
+		//Nothing to do.
+		if(!enabledCount)
+			return 0;
+
+		try
+		{
+			ionFrequencies.resize(enabledCount);
+			//Allocate and Initialise all elements to zero
+			#pragma omp parallel for
+			for(unsigned int ui=0;ui<ionFrequencies.size(); ui++)
+				ionFrequencies[ui].resize(numBins,0);
+		}
+		catch(std::bad_alloc)
+		{
+			return ERR_MEMALLOC;
+		}
+
+	}
+	else
+	{
+		try
+		{
+			ionFrequencies.resize(1);
+			ionFrequencies[0].resize(numBins,0);
+		}
+		catch(std::bad_alloc)
+		{
+			return ERR_MEMALLOC;
+		}
+	}
+
+
+	size_t n=0;
+	size_t totalSize=numElements(dataIn);
+
+	map<size_t,size_t> primitiveMap;
+	primitiveMap[PRIMITIVE_CYLINDER_AXIAL] = CROP_CYLINDER_INSIDE_AXIAL;
+	primitiveMap[PRIMITIVE_CYLINDER_RADIAL] = CROP_CYLINDER_INSIDE_RADIAL;
+	primitiveMap[PRIMITIVE_SPHERE] = CROP_SPHERE_INSIDE;
+
+	CropHelper dataMapping(totalSize,primitiveMap[primitiveType], 
+					vectorParams,scalarParams  );
+	dataMapping.setMapMaxima(numBins);
+
+	for(unsigned int ui=0;ui<dataIn.size() ;ui++)
+	{
+		//Loop through each element data set
+		switch(dataIn[ui]->getStreamType())
+		{
+			case STREAM_TYPE_IONS:
+			{
+				const IonStreamData *dIon = (const IonStreamData*)dataIn[ui];
+#ifdef _OPENMP
+				//OpenMP abort is not v. good, simply spin instead of working
+				bool spin=false;
+#endif
+				//Process ion streams
+			
+				size_t nIons=dIon->data.size();	
+				#pragma omp parallel for shared(n)
+				for(size_t uj=0;uj<nIons;uj++)
+				{
+#ifdef _OPENMP
+					//if parallelised, abort computaiton
+					if(spin) continue;
+#endif
+					unsigned int targetBin;
+					targetBin=dataMapping.mapIon1D(dIon->data[uj]);
+
+					//Keep ion if inside cylinder 
+					if(targetBin!=(unsigned int)-1)
+					{
+						//Push data into the correct bin.
+						// based upon eg ranging information and target 1D bin
+						binIon(targetBin,rngData,ionIDMapping,ionFrequencies,
+								dIon->data[uj].getMassToCharge());
+					}
+
+#ifdef _OPENMP
+					#pragma omp atomic
+					n++; //FIXME: Performance - we could use a separate non-sahred counter to reduce locking?
+
+					if(omp_get_thread_num() == 0)	
+					{
+#endif
+						progress.filterProgress= (unsigned int)((float)(n)/((float)totalSize)*100.0f);
+						if(*Filter::wantAbort)
+						{
+							#ifdef _OPENMP
+							spin=true;
+							#else
+							return ERR_ABORT;
+							#endif 
+						}
+#ifdef _OPENMP
+					}
+#endif
+				}
+
+#ifdef _OPENMP
+				//Check to see if we aborted the Calculation
+				if(spin)
+					return ERR_ABORT;
+#endif
+					
+				break;
+			}
+			default:
+				//Do not propagate other types.
+				break;
+		}
+				
+	}
+
+#ifdef DEBUG
+	ASSERT(ionFrequencies.size());
+	//Ion frequencies must be of equal length
+	for(unsigned int ui=1;ui<ionFrequencies.size();ui++)
+	{
+		ASSERT(ionFrequencies[ui].size() == ionFrequencies[0].size());
+	}
+#endif
+	
+
+	vector<float> normalisationFactor;
+	vector<unsigned int> normalisationCount;
+	normalisationFactor.resize(ionFrequencies[0].size());
+	normalisationCount.resize(ionFrequencies[0].size());
+	bool needNormalise=false;
+
+	//Perform the appropriate normalisation
+	if(!rngData && normalise)
+	{
+		// For density plots, normalise by
+		//  the volume of the primitive's shell
+		switch(primitiveType)
+		{
+			case PRIMITIVE_CYLINDER_AXIAL:
+			case PRIMITIVE_CYLINDER_RADIAL:
+			{
+
+				float dx;
+				if(fixedBins)
+					dx=(sqrtf(vectorParams[1].sqrMag())/(float)numBins);
+
+				else
+					dx=binWidth;
+				needNormalise=true;
+				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:
+			{
+				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)
+					//  note -> (n+1)^3 -n^3  = (3*n^2) + (3*n) + 1
+					normalisationFactor[uj] = 1.0/(4.0/3.0*M_PI*
+						dx*(3.0*((float)uj*(float)uj + uj) + 1.0));
+				}
+				break;
+			}
+			default:
+				ASSERT(false);
+		}
+	}
+	else if(normalise && rngData) //compute normalisation values, if we are in composition mode
+	{
+		// the loops' nesting is reversed as we need to sum over distinct plots
+		//Density profiles (non-ranged plots) have a fixed normalisation factor
+		needNormalise=true;
+
+		for(unsigned int uj=0;uj<ionFrequencies[0].size(); uj++)
+		{
+			float sum;
+			sum=0;
+			//Loop across each bin type, summing result
+			for(unsigned int uk=0;uk<ionFrequencies.size();uk++)
+				sum +=(float)ionFrequencies[uk][uj];
+			normalisationCount[uj]=sum;
+
+	
+			//Compute the normalisation factor
+			if(sum)
+				normalisationFactor[uj]=1.0/sum;
+			else
+				normalisationFactor[uj] = 0;
+		}
+
+	}
+
+	
+	//Create the plots
+	PlotStreamData *plotData[ionFrequencies.size()];
+	for(unsigned int ui=0;ui<ionFrequencies.size();ui++)
+	{
+		plotData[ui] = new PlotStreamData;
+
+		plotData[ui]->index=ui;
+		plotData[ui]->parent=this;
+		plotData[ui]->xLabel= TRANS("Distance");
+		plotData[ui]->errDat=errMode;
+		if(normalise)
+		{
+			//If we have composition, normalise against 
+			//sum composition = 1 otherwise use volume of bin
+			//as normalisation factor
+			if(rngData)
+				plotData[ui]->yLabel= TRANS("Fraction");
+			else
+				plotData[ui]->yLabel= TRANS("Density (\\frac{\\#}{len^3})");
+		}
+		else
+			plotData[ui]->yLabel= TRANS("Count");
+
+		//Give the plot a title like TRANS("Myplot:Mg" (if have range) or "MyPlot") (no range)
+		if(rngData)
+		{
+			unsigned int thisIonID;
+			thisIonID = inverseIDMapping.find(ui)->second;
+			plotData[ui]->dataLabel = getUserString() + string(":") 
+					+ rngData->rangeFile->getName(thisIonID);
+
+		
+			//Set the plot colour to the ion colour	
+			RGBf col;
+			col=rngData->rangeFile->getColour(thisIonID);
+
+			plotData[ui]->r =col.red;
+			plotData[ui]->g =col.green;
+			plotData[ui]->b =col.blue;
+
+		}
+		else
+		{
+			//If it only has one component, then 
+			//it's not really a composition profile is it?
+			plotData[ui]->dataLabel= TRANS("Freq. Profile");
+			plotData[ui]->r = rgba.r();
+			plotData[ui]->g = rgba.g();
+			plotData[ui]->b = rgba.b();
+			plotData[ui]->a = rgba.a();
+		}
+
+		plotData[ui]->xyData.reserve(ionFrequencies[ui].size());
+	
+
+		//Go through each bin, then perform the appropriate normalisation
+		for(unsigned int uj=0;uj<ionFrequencies[ui].size(); uj++)
+		{
+			float xPos;
+			xPos = getBinPosition(uj);
+
+			if(ionFrequencies[ui][uj] < minEvents)
+				continue;
+
+			//Recompute normalisation value for this bin, if needed
+			if(needNormalise)
+			{
+				float normFactor=normalisationFactor[uj];
+
+				//keep the data if we are not using minimum threshold for normalisation, or we met the 
+				// threhsold
+				plotData[ui]->xyData.push_back(
+					std::make_pair(xPos,
+					normFactor*(float)ionFrequencies[ui][uj]));
+			}
+			else
+			{	
+				plotData[ui]->xyData.push_back(
+					std::make_pair(xPos,ionFrequencies[ui][uj]) );
+			}
+		}
+
+
+
+
+		plotData[ui]->plotStyle = plotStyle;
+		plotData[ui]->plotMode=PLOT_MODE_1D;
+
+		//If we ended up with any data, display it
+		// otherwise, trash the plot info
+		if(plotData[ui]->xyData.size())
+		{
+			cacheAsNeeded(plotData[ui]);
+			getOut.push_back(plotData[ui]);
+		}
+		else
+		{
+			consoleOutput.push_back(TRANS("No data remained in profile - cannot display result"));
+			delete plotData[ui];
+		}
+	}
+	
+	progress.filterProgress=100;
+
+	return 0;
+}
+
+std::string  ProfileFilter::getSpecificErrString(unsigned int code) const
+{
+	const char *errCodes[] =   { "",
+		"Too many bins in comp. profile.",
+		"Not enough memory for comp. profile.",
+		"Aborted composition prof." }; 
+
+	COMPILE_ASSERT(THREEDEP_ARRAYSIZE(errCodes) == ERR_COMP_ENUM_END);
+	ASSERT(code < ERR_COMP_ENUM_END);
+
+	return errCodes[code];
+}
+
+bool ProfileFilter::setProperty( unsigned int key, 
+					const std::string &value, bool &needUpdate) 
+{
+			
+	switch(key)
+	{
+		case PROFILE_KEY_DENSITY_ONLY:
+		{
+			if(!applyPropertyNow(wantDensity,value,needUpdate))
+				return false;
+			break;
+		}
+		case PROFILE_KEY_BINWIDTH:
+		{
+			float newBinWidth;
+			if(stream_cast(newBinWidth,value))
+				return false;
+
+			if(newBinWidth < sqrtf(std::numeric_limits<float>::epsilon()))
+				return false;
+
+			binWidth=newBinWidth;
+			clearCache();
+			needUpdate=true;
+			break;
+		}
+		case PROFILE_KEY_FIXEDBINS:
+		{
+			if(!applyPropertyNow(fixedBins,value,needUpdate))
+				return false;
+			break;	
+		}
+		case PROFILE_KEY_NORMAL:
+		{
+			Point3D newPt;
+			if(!newPt.parse(value))
+				return false;
+
+			if(primitiveType == PRIMITIVE_CYLINDER_AXIAL)
+			{
+				if(lockAxisMag && 
+					newPt.sqrMag() > sqrtf(std::numeric_limits<float>::epsilon()))
+				{
+					newPt.normalise();
+					newPt*=sqrtf(vectorParams[1].sqrMag());
+				}
+			}
+			if(newPt.sqrMag() < sqrtf(std::numeric_limits<float>::epsilon()))
+				return false;
+
+			if(!(vectorParams[1] == newPt ))
+			{
+				vectorParams[1] = newPt;
+				needUpdate=true;
+				clearCache();
+			}
+			return true;
+		}
+		case PROFILE_KEY_MINEVENTS:
+		{
+			if(!applyPropertyNow(minEvents,value,needUpdate))
+				return false;
+			break;	
+		}
+		case PROFILE_KEY_NUMBINS:
+		{
+			unsigned int newNumBins;
+			if(stream_cast(newNumBins,value))
+				return false;
+
+			//zero bins disallowed
+			if(!newNumBins)
+				return false;
+
+			nBins=newNumBins;
+
+			clearCache();
+			needUpdate=true;
+			break;
+		}
+		case PROFILE_KEY_ORIGIN:
+		{
+			if(!applyPropertyNow(vectorParams[0],value,needUpdate))
+				return false;
+			return true;
+		}
+		case PROFILE_KEY_PRIMITIVETYPE:
+		{
+			unsigned int newPrimitive;
+			newPrimitive=getPrimitiveId(value);
+			if(newPrimitive >= PRIMITIVE_END)
+				return false;
+
+			//set the new primitive type
+			primitiveType=newPrimitive;
+
+			//set up the values for the new primitive type,
+			// preserving data where possible
+			switch(primitiveType)
+			{
+				case PRIMITIVE_CYLINDER_AXIAL:
+				case PRIMITIVE_CYLINDER_RADIAL:
+				{
+					if(vectorParams.size() != 2)
+					{
+						if(vectorParams.size() <2 )
+						{
+							vectorParams.clear();
+							vectorParams.push_back(Point3D(0,0,0));
+							vectorParams.push_back(Point3D(0,20,0));
+						}
+						else
+							vectorParams.resize(2);
+					}
+
+					if(scalarParams.size() != 1)
+					{
+						if (scalarParams.size() > 1)
+						{
+							scalarParams.clear();
+							scalarParams.push_back(DEFAULT_RADIUS);
+						}
+						else
+							scalarParams.resize(1);
+					}
+
+					if(primitiveType == PRIMITIVE_CYLINDER_RADIAL)
+						fixedBins=true;
+
+					break;
+				}
+				case PRIMITIVE_SPHERE:
+				{
+					if(vectorParams.size() !=1)
+					{
+						if(vectorParams.size() >1)
+							vectorParams.resize(1);
+						else
+							vectorParams.push_back(Point3D(0,0,0));
+					}
+
+					if(scalarParams.size() !=1)
+					{
+						if(scalarParams.size() > 1)
+							scalarParams.resize(1);
+						else
+							scalarParams.push_back(DEFAULT_RADIUS);
+					}
+					break;
+				}
+
+				default:
+					ASSERT(false);
+			}
+	
+			clearCache();	
+			needUpdate=true;	
+			return true;	
+		}
+		case PROFILE_KEY_RADIUS:
+		{
+			float newRad;
+			if(stream_cast(newRad,value))
+				return false;
+
+			if(newRad < sqrtf(std::numeric_limits<float>::epsilon()))
+				return false;
+
+			if(scalarParams[0] != newRad )
+			{
+				scalarParams[0] = newRad;
+				needUpdate=true;
+				clearCache();
+			}
+			return true;
+		}
+		case PROFILE_KEY_SHOWPRIMITIVE:
+		{
+			if(!applyPropertyNow(showPrimitive,value,needUpdate))
+				return false;
+			break;	
+		}
+		case PROFILE_KEY_NORMALISE:
+		{
+			if(!applyPropertyNow(normalise,value,needUpdate))
+				return false;
+			break;	
+		}
+		case PROFILE_KEY_LOCKAXISMAG:
+		{
+			if(!applyPropertyNow(lockAxisMag,value,needUpdate))
+				return false;
+			break;
+		}
+		case PROFILE_KEY_PLOTTYPE:
+		{
+			unsigned int tmpPlotType;
+
+			tmpPlotType=plotID(value);
+
+			if(tmpPlotType >= PLOT_LINE_NONE)
+				return false;
+
+			plotStyle = tmpPlotType;
+			needUpdate=true;	
+			break;
+		}
+		case PROFILE_KEY_COLOUR:
+		{
+			ColourRGBA tmpRgba;
+			if(!tmpRgba.parse(value))
+				return false;
+			
+			rgba=tmpRgba.toRGBAf();
+			needUpdate=true;
+			break;	
+		}
+		case PROFILE_KEY_ERRMODE:
+		{
+			unsigned int tmpMode;
+			tmpMode=plotErrmodeID(value);
+
+			if(tmpMode >= PLOT_ERROR_ENDOFENUM)
+				return false;
+
+			errMode.mode= tmpMode;
+			needUpdate=true;
+
+			break;
+		}
+		case PROFILE_KEY_AVGWINSIZE:
+		{
+			unsigned int tmpNum;
+			if(stream_cast(tmpNum,value))
+				return false;
+
+			if(tmpNum<=1)
+				return false;
+
+			errMode.movingAverageNum=tmpNum;
+			needUpdate=true;
+			break;
+		}
+		default:
+			ASSERT(false);	
+	}
+
+	if(needUpdate)
+		clearCache();
+
+	return true;
+}
+
+void ProfileFilter::getProperties(FilterPropGroup &propertyList) const
+{
+	bool doDensityPlot = (!haveRangeParent) || wantDensity; 
+
+	string str,tmpStr;
+	FilterProperty p;
+	size_t curGroup=0;
+
+	if(haveRangeParent)
+	{
+		stream_cast(tmpStr,wantDensity);
+		p.name=TRANS("Total Density");	
+		p.data=tmpStr;
+		p.key=PROFILE_KEY_DENSITY_ONLY;
+		p.type=PROPERTY_TYPE_BOOL;
+		p.helpText=TRANS("Do not do per-species analysis, perform density computation only");
+		propertyList.addProperty(p,curGroup);
+	}
+
+	//Allow primitive selection if we have more than one primitive
+	if(PRIMITIVE_END > 1)
+	{
+		//Choices for primitive type
+		vector<pair<unsigned int,string> > choices;
+		for(unsigned int ui=0;ui<PRIMITIVE_END;ui++)
+		{
+			str =TRANS(PRIMITIVE_NAME[ui]);
+			choices.push_back(make_pair(ui,str));
+		}
+		p.name=TRANS("Primitive type");
+		p.data=choiceString(choices,primitiveType);
+		p.key=PROFILE_KEY_PRIMITIVETYPE;
+		p.type=PROPERTY_TYPE_CHOICE;
+		p.helpText=TRANS("Basic shape to use for profile");
+		propertyList.addProperty(p,curGroup);
+		propertyList.setGroupTitle(curGroup,TRANS("Primitive"));	
+		curGroup++;
+	}
+
+	
+	stream_cast(tmpStr,showPrimitive);
+	p.name=TRANS("Show Primitive");	
+	p.data=tmpStr;
+	p.key=PROFILE_KEY_SHOWPRIMITIVE;
+	p.type=PROPERTY_TYPE_BOOL;
+	p.helpText=TRANS("Display the 3D composition profile interaction object");
+	propertyList.addProperty(p,curGroup);
+
+	switch(primitiveType)
+	{
+		case PRIMITIVE_CYLINDER_AXIAL:
+		case PRIMITIVE_CYLINDER_RADIAL:
+		{
+			ASSERT(vectorParams.size() == 2);
+			ASSERT(scalarParams.size() == 1);
+			stream_cast(str,vectorParams[0]);
+			p.key=PROFILE_KEY_ORIGIN;
+			p.name=TRANS("Origin");
+			p.data=str;
+			p.type=PROPERTY_TYPE_POINT3D;
+			p.helpText=TRANS("Position for centre of cylinder");
+			propertyList.addProperty(p,curGroup);
+			
+			stream_cast(str,vectorParams[1]);
+			p.key=PROFILE_KEY_NORMAL;
+			p.name=TRANS("Axis");
+			p.data=str;
+			p.type=PROPERTY_TYPE_POINT3D;
+			p.helpText=TRANS("Vector between ends of cylinder");
+			propertyList.addProperty(p,curGroup);
+
+			str=boolStrEnc(lockAxisMag);
+			p.key=PROFILE_KEY_LOCKAXISMAG;
+			p.name=TRANS("Lock Axis Mag.");
+			p.data= str;
+			p.type=PROPERTY_TYPE_BOOL;
+			p.helpText=TRANS("Prevent length of cylinder changing during interaction");
+			propertyList.addProperty(p,curGroup);
+			
+			stream_cast(str,scalarParams[0]);
+			p.key=PROFILE_KEY_RADIUS;
+			p.name=TRANS("Radius");
+			p.data= str;
+			p.type=PROPERTY_TYPE_POINT3D;
+			p.helpText=TRANS("Radius of cylinder");
+			propertyList.addProperty(p,curGroup);
+			break;
+		}
+		case PRIMITIVE_SPHERE:
+		{
+			
+			ASSERT(vectorParams.size() == 1);
+			ASSERT(scalarParams.size() == 1);
+			stream_cast(str,vectorParams[0]);
+			p.key=PROFILE_KEY_ORIGIN;
+			p.name=TRANS("Origin");
+			p.data=str;
+			p.type=PROPERTY_TYPE_POINT3D;
+			p.helpText=TRANS("Position for centre of sphere");
+			propertyList.addProperty(p,curGroup);
+			
+			stream_cast(str,scalarParams[0]);
+			p.key=PROFILE_KEY_RADIUS;
+			p.name=TRANS("Radius");
+			p.data= str;
+			p.type=PROPERTY_TYPE_POINT3D;
+			p.helpText=TRANS("Radius of sphere");
+			propertyList.addProperty(p,curGroup);
+			break;
+		}
+		default:
+			ASSERT(false);
+	}
+
+	//Must be fixed bin num in radial mode. Disallow turning this off
+	if(primitiveType!= PRIMITIVE_CYLINDER_RADIAL)
+	{
+		p.key=PROFILE_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)
+	{
+		stream_cast(tmpStr,nBins);
+		str = TRANS("Num Bins");
+		p.name=str;
+		p.data=tmpStr;
+		p.key=PROFILE_KEY_NUMBINS;
+		p.type=PROPERTY_TYPE_INTEGER;
+		p.helpText=TRANS("Number of bins to use for profile");
+		propertyList.addProperty(p,curGroup);
+	}
+	else
+	{
+		ASSERT(primitiveType!=PRIMITIVE_CYLINDER_RADIAL);
+		str = TRANS("Bin width");
+		stream_cast(tmpStr,binWidth);
+		p.name=str;
+		p.data=tmpStr;
+		p.key=PROFILE_KEY_BINWIDTH;
+		p.type=PROPERTY_TYPE_REAL;
+		p.helpText=TRANS("Size of each bin in profile");
+		propertyList.addProperty(p,curGroup);
+	}
+
+	stream_cast(tmpStr,normalise);
+	p.name= TRANS("Normalise");	
+	p.data=tmpStr;
+	p.key=PROFILE_KEY_NORMALISE;
+	p.type=PROPERTY_TYPE_BOOL;
+	p.helpText=TRANS("Convert bin counts into relative frequencies in each bin");
+	propertyList.addProperty(p,curGroup);
+
+	stream_cast(tmpStr,minEvents);
+	p.name= TRANS("Min. events");	
+	p.data=tmpStr;
+	p.key=PROFILE_KEY_MINEVENTS;
+	p.type=PROPERTY_TYPE_INTEGER;
+	p.helpText=TRANS("Drop data that does not have this many events");
+	propertyList.addProperty(p,curGroup);
+
+	propertyList.setGroupTitle(curGroup,TRANS("Settings"));	
+
+
+
+	curGroup++;
+	
+	//use set 2 to store the plot properties
+	stream_cast(str,plotStyle);
+	//Let the user know what the valid values for plot type are
+	vector<pair<unsigned int,string> > choices;
+
+
+	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));
+
+	tmpStr= choiceString(choices,plotStyle);
+	p.name=TRANS("Plot Type");
+	p.data=tmpStr;
+	p.type=PROPERTY_TYPE_CHOICE;
+	p.helpText=TRANS("Visual style for plot");
+	p.key=PROFILE_KEY_PLOTTYPE;
+	propertyList.addProperty(p,curGroup);
+
+	//If we are not doing per-species, then we need colour
+	if(doDensityPlot)
+	{
+		//Convert the colour to a hex string
+		p.name=TRANS("Colour");
+		p.data=rgba.toColourRGBA().rgbString();
+		p.type=PROPERTY_TYPE_COLOUR;
+		p.helpText=TRANS("Colour of plot");
+		p.key=PROFILE_KEY_COLOUR;
+		propertyList.addProperty(p,curGroup);
+	}
+
+
+	propertyList.setGroupTitle(curGroup,TRANS("Appearance"));
+	curGroup++;
+	
+	choices.clear();
+	tmpStr=plotErrmodeString(PLOT_ERROR_NONE);
+	choices.push_back(make_pair((unsigned int) PLOT_ERROR_NONE,tmpStr));
+	tmpStr=plotErrmodeString(PLOT_ERROR_MOVING_AVERAGE);
+	choices.push_back(make_pair((unsigned int) PLOT_ERROR_MOVING_AVERAGE,tmpStr));
+
+	tmpStr= choiceString(choices,errMode.mode);
+	p.name=TRANS("Err. Estimator");
+	p.data=tmpStr;
+	p.type=PROPERTY_TYPE_CHOICE;
+	p.helpText=TRANS("Method of estimating error associated with each bin");
+	p.key=PROFILE_KEY_ERRMODE;
+	propertyList.addProperty(p,curGroup);
+
+	if(errMode.mode == PLOT_ERROR_MOVING_AVERAGE)
+	{
+		stream_cast(tmpStr,errMode.movingAverageNum);
+		p.name=TRANS("Avg. Window");
+		p.data=tmpStr;
+		p.type=PROPERTY_TYPE_INTEGER;
+		p.helpText=TRANS("Number of bins to include in moving average filter");
+		p.key=PROFILE_KEY_AVGWINSIZE;
+		propertyList.addProperty(p,curGroup);
+	}	
+	propertyList.setGroupTitle(curGroup,TRANS("Error analysis"));
+}
+
+unsigned int ProfileFilter::getBinData(unsigned int &numBins, float &length) const
+{
+	//Number of bins, having determined if we are using
+	//fixed bin count or not
+	switch(primitiveType)
+	{
+		case PRIMITIVE_SPHERE:
+			//radius of sphere
+			length=scalarParams[0];
+			break;
+		case PRIMITIVE_CYLINDER_AXIAL:
+			//length of cylinder, full axis length
+			length=sqrtf(vectorParams[1].sqrMag());
+			break;
+		case PRIMITIVE_CYLINDER_RADIAL:
+			//radius of cylinder
+			length =scalarParams[0];
+			break;
+		default:
+			ASSERT(false);
+	}
+	
+	if(fixedBins)
+		numBins=nBins;
+	else
+	{
+		switch(primitiveType)
+		{
+			case PRIMITIVE_CYLINDER_AXIAL:
+			case PRIMITIVE_CYLINDER_RADIAL:
+			case PRIMITIVE_SPHERE:
+			{
+
+				ASSERT(binWidth > std::numeric_limits<float>::epsilon());
+
+				//Check for possible overflow
+				if(length/binWidth > (float)std::numeric_limits<unsigned int>::max())
+					return ERR_NUMBINS;
+
+				numBins=(unsigned int)(length/binWidth);
+				break;
+			}
+			default:
+				ASSERT(false);
+		}
+		
+	}
+	
+	return 0;
+}
+
+float ProfileFilter::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 ProfileFilter::numBytesForCache(size_t nObjects) const
+{
+	float length;
+	unsigned int errCode, numBins;
+	errCode=getBinData(numBins,length);
+
+	if(errCode)
+		return (unsigned int)-1;
+	
+	return (numBins*2*sizeof(float));
+}
+
+bool ProfileFilter::writeState(std::ostream &f,unsigned int format, unsigned int depth) const
+{
+	using std::endl;
+	switch(format)
+	{
+		case STATE_FORMAT_XML:
+		{	
+			f << tabs(depth) << "<" << trueName() << ">" << endl;
+			f << tabs(depth+1) << "<userstring value=\""<< escapeXML(userString) << "\"/>"  << endl;
+
+			f << tabs(depth+1) << "<primitivetype value=\"" << primitiveType<< "\"/>" << endl;
+			f << tabs(depth+1) << "<showprimitive value=\"" << showPrimitive << "\"/>" << endl;
+			f << tabs(depth+1) << "<lockaxismag value=\"" << lockAxisMag<< "\"/>" << endl;
+			f << tabs(depth+1) << "<vectorparams>" << endl;
+			for(unsigned int ui=0; ui<vectorParams.size(); ui++)
+			{
+				f << tabs(depth+2) << "<point3d x=\"" << vectorParams[ui][0] << 
+					"\" y=\"" << vectorParams[ui][1] << "\" z=\"" << vectorParams[ui][2] << "\"/>" << endl;
+			}
+			f << tabs(depth+1) << "</vectorparams>" << endl;
+
+			f << tabs(depth+1) << "<scalarparams>" << endl;
+			for(unsigned int ui=0; ui<scalarParams.size(); ui++)
+				f << tabs(depth+2) << "<scalar value=\"" << scalarParams[0] << "\"/>" << endl; 
+			
+			f << tabs(depth+1) << "</scalarparams>" << endl;
+			f << tabs(depth+1) << "<normalise value=\"" << normalise << "\" minevents=\"" << minEvents << "\" />" << endl;
+			f << tabs(depth+1) << "<fixedbins value=\"" << (int)fixedBins << "\"/>" << endl;
+			f << tabs(depth+1) << "<nbins value=\"" << nBins << "\"/>" << endl;
+			f << tabs(depth+1) << "<binwidth value=\"" << binWidth << "\"/>" << endl;
+			f << tabs(depth+1) << "<colour r=\"" <<  rgba.r() << "\" g=\"" << rgba.g() << "\" b=\"" << rgba.b()
+				<< "\" a=\"" << rgba.a() << "\"/>" <<endl;
+
+			f << tabs(depth+1) << "<plottype value=\"" << plotStyle << "\"/>" << endl;
+			f << tabs(depth) << "</" << trueName()  << ">" << endl;
+			break;
+		}
+		default:
+			ASSERT(false);
+			return false;
+	}
+
+	return true;
+}
+
+
+void ProfileFilter::setUserString(const std::string &str)
+{
+	if(userString != str)
+	{
+		userString=str;
+		clearCache();
+	}	
+}
+
+bool ProfileFilter::readState(xmlNodePtr &nodePtr, const std::string &stateFileDir)
+{
+	//Retrieve user string
+	//===
+	if(XMLHelpFwdToElem(nodePtr,"userstring"))
+		return false;
+
+	xmlChar *xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	userString=(char *)xmlString;
+	xmlFree(xmlString);
+	//===
+
+	std::string tmpStr;	
+	//Retrieve primitive type 
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"primitivetype"))
+		return false;
+
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+	//convert from string to digit
+	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);
+	//====
+	
+	//Retrieve primitive visibility 
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"showprimitive"))
+		return false;
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+	if(!boolStrDec(tmpStr,showPrimitive))
+		return false;
+
+	xmlFree(xmlString);
+	//====
+	
+	//Retrieve axis lock mode 
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"lockaxismag"))
+		return false;
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+	if(!boolStrDec(tmpStr,lockAxisMag))
+		return false;
+
+	xmlFree(xmlString);
+	//====
+	
+	//Retrieve vector parameters
+	//===
+	if(XMLHelpFwdToElem(nodePtr,"vectorparams"))
+		return false;
+	xmlNodePtr tmpNode=nodePtr;
+
+	nodePtr=nodePtr->xmlChildrenNode;
+
+	vectorParams.clear();
+	while(!XMLHelpFwdToElem(nodePtr,"point3d"))
+	{
+		float x,y,z;
+		//--Get X value--
+		xmlString=xmlGetProp(nodePtr,(const xmlChar *)"x");
+		if(!xmlString)
+			return false;
+		tmpStr=(char *)xmlString;
+		xmlFree(xmlString);
+
+		//Check it is streamable
+		if(stream_cast(x,tmpStr))
+			return false;
+
+		//--Get Z value--
+		xmlString=xmlGetProp(nodePtr,(const xmlChar *)"y");
+		if(!xmlString)
+			return false;
+		tmpStr=(char *)xmlString;
+		xmlFree(xmlString);
+
+		//Check it is streamable
+		if(stream_cast(y,tmpStr))
+			return false;
+
+		//--Get Y value--
+		xmlString=xmlGetProp(nodePtr,(const xmlChar *)"z");
+		if(!xmlString)
+			return false;
+		tmpStr=(char *)xmlString;
+		xmlFree(xmlString);
+
+		//Check it is streamable
+		if(stream_cast(z,tmpStr))
+			return false;
+
+		vectorParams.push_back(Point3D(x,y,z));
+	}
+	//===	
+
+	nodePtr=tmpNode;
+	//Retrieve scalar parameters
+	//===
+	if(XMLHelpFwdToElem(nodePtr,"scalarparams"))
+		return false;
+	
+	tmpNode=nodePtr;
+	nodePtr=nodePtr->xmlChildrenNode;
+
+	scalarParams.clear();
+	while(!XMLHelpFwdToElem(nodePtr,"scalar"))
+	{
+		float v;
+		//Get value
+		xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+		if(!xmlString)
+			return false;
+		tmpStr=(char *)xmlString;
+		xmlFree(xmlString);
+
+		//Check it is streamable
+		if(stream_cast(v,tmpStr))
+			return false;
+		scalarParams.push_back(v);
+	}
+	//===	
+
+	//Check the scalar params match the selected primitive	
+	switch(primitiveType)
+	{
+		case PRIMITIVE_CYLINDER_AXIAL:
+		case PRIMITIVE_CYLINDER_RADIAL:
+			if(vectorParams.size() != 2 || scalarParams.size() !=1)
+				return false;
+			break;
+		case PRIMITIVE_SPHERE:
+			if(vectorParams.size() != 1 || scalarParams.size() !=1)
+				return false;
+			break;
+
+		default:
+			ASSERT(false);
+			return false;
+	}
+
+	nodePtr=tmpNode;
+
+	//Retrieve normalisation on/off 
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"normalise"))
+		return false;
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+
+	if(!boolStrDec(tmpStr,normalise))
+		return false;
+
+	xmlFree(xmlString);
+	//====
+
+	//Retrieve fixed bins on/off 
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"fixedbins"))
+		return false;
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+	if(!boolStrDec(tmpStr,fixedBins))
+		return false;
+
+
+	xmlFree(xmlString);
+	//====
+
+	//Retrieve num bins
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"nbins"))
+		return false;
+
+	
+	if(XMLHelpGetProp(nBins,nodePtr,"value"))
+		return false;
+
+
+	if(XMLHelpGetProp(minEvents,nodePtr,"minevents"))
+	{
+		//FIXME: Deprecate me.
+		minEvents=MINEVENTS_DEFAULT;
+	}
+	//====
+
+	//Retrieve bin width
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"binwidth"))
+		return false;
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+	if(stream_cast(binWidth,tmpStr))
+		return false;
+
+	xmlFree(xmlString);
+	//====
+
+	//Retrieve colour
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"colour"))
+		return false;
+	if(!parseXMLColour(nodePtr,rgba))
+		return false;
+	//====
+	
+	//Retrieve plot type 
+	//====
+	if(XMLHelpFwdToElem(nodePtr,"plottype"))
+		return false;
+
+	xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
+	if(!xmlString)
+		return false;
+	tmpStr=(char *)xmlString;
+
+	//convert from string to digit
+	if(stream_cast(plotStyle,tmpStr))
+		return false;
+
+	if(plotStyle >= PLOT_LINE_NONE)
+	       return false;	
+	xmlFree(xmlString);
+	//====
+
+	return true;
+}
+
+unsigned int ProfileFilter::getRefreshBlockMask() const
+{
+	//Absolutely anything can go through this filter.
+	return 0;
+}
+
+unsigned int ProfileFilter::getRefreshEmitMask() const
+{
+	if(showPrimitive)
+		return STREAM_TYPE_PLOT | STREAM_TYPE_DRAW;
+	else
+		return STREAM_TYPE_PLOT;
+}
+
+unsigned int ProfileFilter::getRefreshUseMask() const
+{
+	return STREAM_TYPE_IONS | STREAM_TYPE_RANGE;
+}
+
+void ProfileFilter::setPropFromBinding(const SelectionBinding &b)
+{
+	switch(b.getID())
+	{
+		case BINDING_CYLINDER_RADIUS:
+		case BINDING_SPHERE_RADIUS:
+			b.getValue(scalarParams[0]);
+			break;
+		case BINDING_CYLINDER_ORIGIN:
+		case BINDING_SPHERE_ORIGIN:
+			b.getValue(vectorParams[0]);
+			break;
+		case BINDING_CYLINDER_DIRECTION:
+		{
+			Point3D pOld=vectorParams[1];
+			b.getValue(vectorParams[1]);
+			//Test getting the bin data.
+			// if something is wrong, abort
+			float length;
+			unsigned int numBins;
+			unsigned int errCode= getBinData(numBins,length);
+			if(errCode || !numBins)
+			{
+				vectorParams[1]=pOld;
+				return;
+			}
+
+			break;
+		}
+		default:
+			ASSERT(false);
+	}
+
+	clearCache();
+}
+
+unsigned int ProfileFilter::getPrimitiveId(const std::string &primitiveName) 
+{
+	for(size_t ui=0;ui<PRIMITIVE_END; ui++)
+	{
+		if( TRANS(PRIMITIVE_NAME[ui]) == primitiveName)
+			return ui;
+	}
+
+	ASSERT(false);
+}
+
+#ifdef DEBUG
+
+bool testDensityCylinder();
+bool testCompositionCylinder();
+void synthComposition(const vector<pair<float,float> > &compositionData,
+			vector<IonHit> &h);
+IonStreamData *synthLinearProfile(const Point3D &start, const Point3D &end,
+					float radialSpread,unsigned int numPts);
+
+bool ProfileFilter::runUnitTests()
+{
+	if(!testDensityCylinder())
+		return false;
+
+	if(!testCompositionCylinder())
+		return false;
+
+	return true;
+}
+
+bool testCompositionCylinder()
+{
+	IonStreamData *d;
+	const size_t NUM_PTS=10000;
+
+	//Create a cylinder of data, forming a linear profile
+	Point3D startPt(-1.0f,-1.0f,-1.0f),endPt(1.0f,1.0f,1.0f);
+	d= synthLinearProfile(startPt,endPt,
+			0.5f, NUM_PTS);
+
+	//Generate two compositions for the test dataset
+	{
+	vector<std::pair<float,float>  > vecCompositions;
+	vecCompositions.push_back(make_pair(2.0f,0.5f));
+	vecCompositions.push_back(make_pair(3.0f,0.5f));
+	synthComposition(vecCompositions,d->data);
+	}
+
+	//Build a faux rangestream
+	RangeStreamData *rngStream;
+	rngStream = new RangeStreamData;
+	rngStream->rangeFile = new RangeFile;
+	
+	RGBf rgb; rgb.red=rgb.green=rgb.blue=1.0f;
+
+	unsigned int aIon,bIon;
+	std::string tmpStr;
+	tmpStr="A";
+	aIon=rngStream->rangeFile->addIon(tmpStr,tmpStr,rgb);
+	tmpStr="B";
+	bIon=rngStream->rangeFile->addIon(tmpStr,tmpStr,rgb);
+	rngStream->rangeFile->addRange(1.5,2.5,aIon);
+	rngStream->rangeFile->addRange(2.5,3.5,bIon);
+	rngStream->enabledIons.resize(2,true);
+	rngStream->enabledRanges.resize(2,true);
+
+	//Construct the composition filter
+	ProfileFilter *f = new ProfileFilter;
+
+	//Build some points to pass to the filter
+	vector<const FilterStreamData*> streamIn,streamOut;
+	
+	bool needUp; std::string s;
+	stream_cast(s,Point3D((startPt+endPt)*0.5f));
+	TEST(f->setProperty(PROFILE_KEY_ORIGIN,s,needUp),"set origin");
+	TEST(f->setProperty(PROFILE_KEY_MINEVENTS,"0",needUp),"set origin");
+	
+	stream_cast(s,Point3D((endPt-startPt)*0.5f));
+	TEST(f->setProperty(PROFILE_KEY_NORMAL,s,needUp),"set direction");
+	TEST(f->setProperty(PROFILE_KEY_SHOWPRIMITIVE,"1",needUp),"Set cylinder visibility");
+	TEST(f->setProperty(PROFILE_KEY_NORMALISE,"1",needUp),"Disable normalisation");
+	TEST(f->setProperty(PROFILE_KEY_RADIUS,"5",needUp),"Set radius");
+	
+	//Inform the filter about the range stream
+	streamIn.push_back(rngStream);
+	f->initFilter(streamIn,streamOut);
+	
+	streamIn.push_back(d);
+	f->setCaching(false);
+
+
+	ProgressData p;
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
+
+	//2* plot, 1*rng, 1*draw
+	TEST(streamOut.size() == 4, "output stream count");
+
+	delete d;
+
+	std::map<unsigned int, unsigned int> countMap;
+	countMap[STREAM_TYPE_PLOT] = 0;
+	countMap[STREAM_TYPE_DRAW] = 0;
+	countMap[STREAM_TYPE_RANGE] = 0;
+
+	for(unsigned int ui=0;ui<streamOut.size();ui++)
+	{
+		ASSERT(countMap.find(streamOut[ui]->getStreamType()) != countMap.end());
+		countMap[streamOut[ui]->getStreamType()]++;
+	}
+
+	TEST(countMap[STREAM_TYPE_PLOT] == 2,"Plot count");
+	TEST(countMap[STREAM_TYPE_DRAW] == 1,"Draw count");
+	TEST(countMap[STREAM_TYPE_RANGE] == 1,"Range count");
+	
+	const PlotStreamData* plotData=0;
+	for(unsigned int ui=0;ui<streamOut.size();ui++)
+	{
+		if(streamOut[ui]->getStreamType() == STREAM_TYPE_PLOT)
+		{
+			plotData = (const PlotStreamData *)streamOut[ui];
+			break;
+		}
+	}
+	TEST(plotData,"Should have plot data");
+	TEST(plotData->xyData.size(),"Plot data size");
+
+	for(size_t ui=0;ui<plotData->xyData.size(); ui++)
+	{
+		TEST(plotData->xyData[ui].second <= 1.0f && 
+			plotData->xyData[ui].second >=0.0f,"normalised data range test"); 
+	}
+
+	delete rngStream->rangeFile;
+	for(unsigned int ui=0;ui<streamOut.size();ui++)
+		delete streamOut[ui];
+
+
+	delete f;
+
+	return true;
+}
+
+bool testDensityCylinder()
+{
+	IonStreamData *d;
+	const size_t NUM_PTS=10000;
+
+	//Create a cylinder of data, forming a linear profile
+	Point3D startPt(-1.0f,-1.0f,-1.0f),endPt(1.0f,1.0f,1.0f);
+	d= synthLinearProfile(startPt,endPt,
+			0.5f, NUM_PTS);
+
+	//Generate two compositions for the test dataset
+	{
+	vector<std::pair<float,float>  > vecCompositions;
+	vecCompositions.push_back(make_pair(2.0f,0.5f));
+	vecCompositions.push_back(make_pair(3.0f,0.5f));
+	synthComposition(vecCompositions,d->data);
+	}
+
+	ProfileFilter *f = new ProfileFilter;
+	f->setCaching(false);
+
+	//Build some points to pass to the filter
+	vector<const FilterStreamData*> streamIn,streamOut;
+	streamIn.push_back(d);
+	
+	bool needUp; std::string s;
+	stream_cast(s,Point3D((startPt+endPt)*0.5f));
+	TEST(f->setProperty(PROFILE_KEY_ORIGIN,s,needUp),"set origin");
+	
+	stream_cast(s,Point3D((endPt-startPt)));
+	TEST(f->setProperty(PROFILE_KEY_NORMAL,s,needUp),"set direction");
+	
+	TEST(f->setProperty(PROFILE_KEY_SHOWPRIMITIVE,"1",needUp),"Set cylinder visibility");
+
+	TEST(f->setProperty(PROFILE_KEY_NORMALISE,"0",needUp),"Disable normalisation");
+	TEST(f->setProperty(PROFILE_KEY_RADIUS,"5",needUp),"Set radius");
+
+	ProgressData p;
+	TEST(!f->refresh(streamIn,streamOut,p),"Refresh error code");
+	delete f;
+	delete d;
+
+
+	TEST(streamOut.size() == 2, "output stream count");
+
+	std::map<unsigned int, unsigned int> countMap;
+	countMap[STREAM_TYPE_PLOT] = 0;
+	countMap[STREAM_TYPE_DRAW] = 0;
+
+	for(unsigned int ui=0;ui<streamOut.size();ui++)
+	{
+		ASSERT(countMap.find(streamOut[ui]->getStreamType()) != countMap.end());
+		countMap[streamOut[ui]->getStreamType()]++;
+	}
+
+	TEST(countMap[STREAM_TYPE_PLOT] == 1,"Plot count");
+	TEST(countMap[STREAM_TYPE_DRAW] == 1,"Draw count");
+
+	
+	const PlotStreamData* plotData=0;
+	for(unsigned int ui=0;ui<streamOut.size();ui++)
+	{
+		if(streamOut[ui]->getStreamType() == STREAM_TYPE_PLOT)
+		{
+			plotData = (const PlotStreamData *)streamOut[ui];
+			break;
+		}
+	}
+
+	float sum=0;
+	for(size_t ui=0;ui<plotData->xyData.size(); ui++)
+		sum+=plotData->xyData[ui].second;
+
+
+	TEST(sum > NUM_PTS/1.2f,"Number points roughly OK");
+	TEST(sum <= NUM_PTS,"No overcounting");
+	
+	for(unsigned int ui=0;ui<streamOut.size();ui++)
+		delete streamOut[ui];
+
+	return true;
+}
+
+
+//first value in pair is target mass, second value is target composition
+void synthComposition(const vector<std::pair<float,float> > &compositionData,
+			vector<IonHit> &h)
+{
+	float fractionSum=0;
+	for(size_t ui=0;ui<compositionData.size(); ui++)
+		fractionSum+=compositionData[ui].second;
+
+	//build the spacings between 0 and 1, so we can
+	//randomly select ions by uniform deviates
+	vector<std::pair<float,float> > ionCuts;
+	ionCuts.resize(compositionData.size());
+	//ionCuts.resize[compositionData.size()];
+	float runningSum=0;
+	for(size_t ui=0;ui<ionCuts.size(); ui++)
+	{
+		runningSum+=compositionData[ui].second;
+		ionCuts[ui]=make_pair(compositionData[ui].first, 
+				runningSum/fractionSum);
+	}
+
+	RandNumGen rngHere;
+	rngHere.initTimer();
+	for(size_t ui=0;ui<h.size();ui++)
+	{
+
+		float newMass;
+		bool haveSetMass;
+		
+		//keep generating random selections until we hit something.
+		// This is to prevent any fp fallthrough
+		do
+		{
+			float uniformDeviate;
+			uniformDeviate=rngHere.genUniformDev();
+
+			haveSetMass=false;
+			//This is not efficient -- data is sorted,
+			//so binary search would work, but whatever.
+			for(size_t uj=0;uj<ionCuts.size();uj++)	
+			{
+				if(uniformDeviate >=ionCuts[uj].second)
+				{
+					newMass=ionCuts[uj].first;
+					haveSetMass=true;
+					break;
+				}
+			}
+		}while(!haveSetMass);
+
+
+		h[ui].setMassToCharge(newMass);
+	}
+}
+
+
+//Create a line of points of fixed mass (1), with a top-hat radial spread function
+// so we end up with a cylinder of unit mass data along some start-end axis
+//you must free the returned value by calling "delete"
+IonStreamData *synthLinearProfile(const Point3D &start, const Point3D &end,
+					float radialSpread,unsigned int numPts)
+{
+
+	ASSERT((start-end).sqrMag() > std::numeric_limits<float>::epsilon());
+	IonStreamData *d = new IonStreamData;
+
+	IonHit h;
+	h.setMassToCharge(1.0f);
+
+	Point3D delta; 
+	delta=(end-start)*1.0f/(float)numPts;
+
+	RandNumGen rngAxial;
+	rngAxial.initTimer();
+	
+	Point3D unitDelta;
+	unitDelta=delta;
+	unitDelta.normalise();
+	
+	
+	d->data.resize(numPts);
+	for(size_t ui=0;ui<numPts;ui++)
+	{
+		//generate a random offset vector
+		//that is normal to the axis of the simulation
+		Point3D randomVector;
+		do
+		{
+			randomVector=Point3D(rngAxial.genUniformDev(),
+					rngAxial.genUniformDev(),
+					rngAxial.genUniformDev());
+		}while(randomVector.sqrMag() < std::numeric_limits<float>::epsilon() &&
+			randomVector.angle(delta) < std::numeric_limits<float>::epsilon());
+
+		
+		randomVector=randomVector.crossProd(unitDelta);
+		randomVector.normalise();
+
+		//create the point
+		Point3D pt;
+		pt=delta*(float)ui + start; //true location
+		pt+=randomVector*radialSpread;
+		h.setPos(pt);
+		d->data[ui] =h;
+	}
+
+	return d;
+}
+#endif
diff --git a/src/backend/filters/profile.h b/src/backend/filters/profile.h
new file mode 100644
index 0000000..5c28135
--- /dev/null
+++ b/src/backend/filters/profile.h
@@ -0,0 +1,159 @@
+/*
+ *	profile.h - Composition/density profiles of 3D point clouds
+ *	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 COMPPROFILE_H
+#define COMPPROFILE_H
+#include "../filter.h"
+#include "../../common/translation.h"
+
+#include <map>
+
+enum
+{
+	PROFILE_KEY_BINWIDTH=1,
+	PROFILE_KEY_FIXEDBINS,
+	PROFILE_KEY_DENSITY_ONLY,
+	PROFILE_KEY_NORMAL,
+	PROFILE_KEY_MINEVENTS,
+	PROFILE_KEY_NUMBINS,
+	PROFILE_KEY_ORIGIN,
+	PROFILE_KEY_PLOTTYPE,
+	PROFILE_KEY_PRIMITIVETYPE,
+	PROFILE_KEY_RADIUS,
+	PROFILE_KEY_SHOWPRIMITIVE,
+	PROFILE_KEY_NORMALISE,
+	PROFILE_KEY_COLOUR,
+	PROFILE_KEY_ERRMODE,
+	PROFILE_KEY_AVGWINSIZE,
+	PROFILE_KEY_LOCKAXISMAG
+};
+//!Filter that does composition or density profiles for various primitives
+class ProfileFilter : public Filter
+{
+	private:
+
+		//!Number explaining basic primitive type
+		/* Possible Modes:
+		 * Cylindrical (origin + axis + length)
+		 */
+		unsigned int primitiveType;
+		//!Whether to show the primitive or not
+		bool showPrimitive;
+		//Lock the primitive axis during for cylinder?
+		bool lockAxisMag; 
+		//!Vector parameters for different primitives
+		std::vector<Point3D> vectorParams;
+		//!Scalar parameters for different primitives
+		std::vector<float> scalarParams;
+
+		//! Does the user explicitly want a density plot? 
+		bool wantDensity;
+		//!Frequency or percentile mode (0 - frequency; 1-normalised (ion freq))
+		bool normalise;
+		//!Use fixed bins?
+		bool fixedBins;
+		
+		//!number of bins (if using fixed bins)
+		unsigned int nBins;
+		//!Width of each bin (if using fixed width)
+		float binWidth;
+
+		//!Number of events required for an entry to be logged in a normalised
+		// histogram
+		unsigned int minEvents;
+		
+		//Plotting stuff
+		//--
+		//colour of plot
+		ColourRGBAf rgba;
+		//Mode for plotting (eg lines, steps)
+		unsigned int plotStyle;
+	
+		PLOT_ERROR errMode;
+
+		//!Do we have a range file above us in our filter tree? This is set by ::initFilter
+		bool haveRangeParent;
+		//--
+		
+		//!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,
+			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:
+		ProfileFilter();
+		//!Duplicate filter contents, excluding cache.
+		Filter *cloneUncached() const;
+		//!Returns FILTER_TYPE_PROFILE
+		unsigned int getType() const { return FILTER_TYPE_PROFILE;};
+
+		//!Get approx number of bytes for caching output
+		size_t numBytesForCache(size_t nObjects) const;
+		
+
+		//!Initialise filter, check for upstream range
+		virtual void initFilter(const std::vector<const FilterStreamData *> &dataIn,
+				std::vector<const FilterStreamData *> &dataOut);
+		//!update filter
+		unsigned int refresh(const std::vector<const FilterStreamData *> &dataIn,
+						std::vector<const FilterStreamData *> &getOut, 
+						ProgressData &progress);
+		
+		virtual std::string typeString() const { return std::string(TRANS("Comp. Prof."));};
+
+		//!Get the properties of the filter, in key-value form. First vector is for each output.
+		void getProperties(FilterPropGroup &propertyList) const;
+
+		//!Set the properties for the nth filter. Returns true if prop set OK
+		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 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;
+		//!Read the state of the filter from XML file. If this
+		//fails, filter will be in an undefined state.
+		bool readState(xmlNodePtr &node, const std::string &packDir);
+		//!Get the stream types that will be dropped during ::refresh	
+		unsigned int getRefreshBlockMask() const;
+
+		//!Get the stream types that will be generated during ::refresh	
+		unsigned int getRefreshEmitMask() const;	
+		
+		//!Get the stream types that may be utilised in computation during ::refresh
+		unsigned int getRefreshUseMask() const;	
+	
+		//!Set internal property value using a selection binding  
+		void setPropFromBinding(const SelectionBinding &b) ;
+
+		void setUserString(const std::string &s); 
+
+#ifdef DEBUG
+		bool runUnitTests() ;
+#endif
+};
+
+#endif

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